I'm writing a version of the Unix expand utility that replaces tabs with spaces in a file. To do this, I'm reading in each character and testing if it is a tab character. If it is, it replaces the tab with the given amount of spaces, otherwise the character gets printed.
My main method goes like
int main(int argc, char *argv[]){
FILE *fp;
char *help1="-help";
char *help2= "--help";
//int spaces; //number of spaces to replace tabs
fp= fopen(argv[1], "rw");
parse_file(fp, 4);
fclose(fp);
return 0;
}
the parse_file method goes like
void parse_file(FILE *fp, int spaces)
{
int i; //loop counter
char c; //current character
while (c!= EOF)
{
c= getchar(); //get char from stream
if (c=='\t') //if char is a tab
{
for (i=0; i< spaces; i++)
putchar(" "); //replace with spaces
}
else
putchar(c); //otherwise, print the character
}
}
When compiling, I get an integer from pointer without cast warning for putchar(" "); and the program hits a segfault when executing.
So, my questions:
1- What is the warning "makes integer from pointer without cast" all about? What can I do to resolve it?
2- The code generates a segfault on execution with a text file passed in as an argument. Is there anything in this code that would cause that?
you must use
putchar(' ')
instead
putchar(" ")
You're calling putchar on a string (" "), but it wants a char argument (' '). (Actually an int, but only passing a char is safe.)
The segfault is probably due to the fclose on fp, which may be NULL. You should check the return value from fopen. The reason you only notice after parse_file is that it doesn't touch fp at all (it reads from stdin and writes to stdout). To use the stream fp, you should use getc(fp) and putc(fp) instead. (That still won't work because you'd overwrite the stream with more data than you're reading from it, so you'll get garbage out.)
In fact, the program is sure to segfault when no command line argument is given. Either fopen segfaults because it is handed the null pointer argv[1], or it returns a null pointer itself.
When writing these kinds of programs, please adhere to the Unix philosophy and write them as filters: read from stdin, write to stdout. Don't modify a file in-place if you don't have to.
In C string literals are of type char *, a pointer to some area containing string characters.
" " is a string literal, not a character. Use ' ' when you need a single character
As everyone else says, re. The use of char vs. string. As for the logic behind the actual error message you see, the string is a pointer to a const array of characters. Hence the error is saying it is converting the pointer to an int. Most of the char functions work with ints.
To summarise the issues (I'm repeating stuff other people have said, but issues 5 and 6 havehas not been mentioned so far):
putchar() does not take a string pointer as an argument but an int - the constant ' ' is an acceptable parameter
you don't check that argc > 1 before using argv[1]
you don't check that fopen() successfully opens the file
c should be defined as an int since in some character sets (char) -1 (0xFF) is a legitimate character and also the comparison c == EOF may fail if c is not sign extended
The first time through the loop c is used uninitialised and in the loop you also treat EOF as a normal character. The normal idiom in C programs is
int c;
while ((c = fgetc(fp)) != EOF)
{
// do stuff with c
}
You are getting your characters from stdin and not fp hence use fgetc() not getchar()
I think that covers everything.
To answer your explicit question, you get the warning "makes integer from pointer without cast" when an int is expected but you use a pointer (in this case the type of " " is const char*).
In addition to what has already been said, getchar() returns int not char. EOF is an int constant. You will need to read the result from getchar() into an int, check for EOF, and if not found, cast the int into a char.
Related
I am writing a program in C for a basic calculator. I am trying to do this using what I have learned so far: printf() and scanf() functions. I am passing arguments into my program through the command line. I am assuming three arguments will be passed at a time which includes: first int, an operator, and the second int. I want to check if the second arg passed is an operator and then check if it's +,-,*... so on. Here is what I came up with:
int main(int argc, char **argv) {
scanf("%d %c %d", &a, &oper, &b);
if (oper != 43) {
printf("Error: Operator is not a +");
return(1);
}
}
So obviously, I have omitted a lot of the code and kept the relevant part. Here I am just checking if the oper is a +. The ASCII key is 43 so I thought this would work but no luck! Any ideas? (I would like to see if I can do this just with printf and scanf if possible)
EDIT: For example if 12 b 13 was entered, it should return the error above. Same goes for '10 +a 10' or '10 ++ 10'.
Firstly I would highly recommend looking at the man-pages for any C library function you come across, they have a lot of useful information. It seems like you are using scanf() improperly as it is not made to be used with command line arguments.
You can check for matches for a single character by comparing the argument like this:
if(argv[2][0] == '+') ...
(argv[0] is the program's file name).
If would would like to compare string you can use strcmp(). But for the operator example you can get away with just checking the first and second characters in the argument like this:
if(argv[2][0] == '+' && argv[2][0] == '\0') ...
What this does is compare the first two characters of the argument. It first checks for the '+' and then checks if that is the end of the string with by checking for the null terminator '\0'.
We can make the assumption that any argument has at least two characters, the visible character and a null terminator. Performing this on other strings has no guarantee of this however.
The other characters, specifically the numbers need to be converted from their respective ASCII values to integers. You can use atoi or strtol to do this, although atoi will most likely be easier for you.
As David C. Rankin pointed out, **argv is a double pointer which at a high level and in most cases you can treat as a double array. In C a string is actually just an array of type char, so what argv[2] is doing above is first accessing the third index of **argv, this is now de-referenced to a type char * where the string (char array) is located. This can then further be de-referenced by the [0] in argv[2][0] to look at the first char of the string.
Code example:
char **my_arrays = argv; // a array of arrays
char *array = *argv; // de-references to index 0 in argv
char *array = *(argv + 1); // de-references to index 1 in argv
char *array = argv[0]; // de-references to index 0 in argv
char *array = argv[1]; // de-references to index 1 in argv
char first_char = *(*argv) // the first char of the first array of argv
char first_char = *(argv[0]) // the same as above
char first_char = argv[0][0] // the same as above
A side note. All strings in C should end in a null terminator which can be represented by NULL, 0, or '\0' values. This will represent the end of the string and many C functions rely on this to know when to stop.
Also NULL is technically a C macro, but you don't need to treat it any differently than 0 because it literally just expands to 0.
It's char **argv. As Some programmer dude said, you should reread your book/tutorial.
scanf doesn't read arguments. It reads from stdin.
Arguments are of type char* and are stored in argv. To convert these arguments to integers, use atoi or strtol (preferably strtol). See this for more info.
If you want to read from stdin using scanf, that is fine, and what you have will work as long as you instead input the data into stdin, and not as command line arguments.
i'm having a problem with comparisons using getchar() and file redirection.
I have a code that resembles this:
char result = getchar(); // getchar returns the next char in the file
int linecount = 0;
if (result == "\n") {
linecount++;
}
But I get a warning when compiling it. It says that I can't compare an int with a pointer, but from my understanding, result is a char and so is "\n", so I'm really confused. I can also use printf("%c", result") and it works fine, implying that result is a char. Does anyone know why I'm getting this error? Thanks! Also, running the code, linecount will always return 0 even if the first character in the file I'm using as my input is a newline.
You are comparing a char with a char *, that is, a string. "" (doublequoted) values are treated as strings in C, so your code should be
if (result == '\n') {
linecount++;
}
Alternatively, you could use strcmp or strncmp with the char casted to a pointer, but that's not necessary.
Do note that the size of a char is less than an int so the conversion from a char to int doesn't make you lose anything.
For some reason when building my program, it says that I have this error:
error: invalid conversion from 'char' to 'const char*' [-fpermissive]
strcpy(phrase[counter].word, ch);
I'm not really sure what the compiler means, I've tried changing char ch to const char ch, but that doesn't seem to fix the issue. If anyone could shed some light on this issue that would be great.
Here is the code you can refer to:
const char* clean_word(void);
void create_word(struct Word_setup phrase[], FILE *fp)
{
char ch;
fscanf(fp, "%s", &ch);
strcpy(phrase[counter].word, ch);
strcpy(phrase[counter].word, clean_word());
}
const char* clean_word()
{
int i;
const char *ch = phrase[counter].word;
ch = phrase[counter].word;
for (i = 0; i < M; i++)
{
if (phrase[counter].word[i] == 39 || isalpha(phrase[counter].word[i])) //39 is the ASCII value for: '
i = i; //Just to be there, no reason to it. I just want the program to do nothing if its a letter or '
else
phrase[counter].word[i] = '\0';
}
return ch;
}
Also in case you're wondering what this is for, I'm building a program that will scan a *.txt file and then print out the word with the highest number of appearances, as as well as the number of appearances.
If you've already read my old post, I decided to try writing it without re-allocating space every time the array gets filled, and I just set it to 200 max different types of words. I will change the program once I'm finished so that it reallocates space every time that the array fills up.
The problem is here:
char ch;
fscanf(fp, "%s", &ch);
strcpy(phrase[counter].word, ch);
The strcpy() function expects its second argument to be a pointer to the head of the source string, but ch is a char, not a char * or const char *. As Keith Thompson explained, this is an error because integers are not implicitly convertible to pointers in C (other than a constant expression with value 0). Even if the compiler performed an implicit conversion anyway, the behavior would surely not be what you want.
Moreover, the fscanf() isn't going to do what you want, either, because even though argument &ch is the correct type for the format string it accompanies, it is a pointer to only a single character of storage, and the fscanf will always write outside its bounds if it successfully scans a string (because it must write a string terminator even for a one-character string).
#BLUEPIXY's approach is much better, supposing that phrase[counter].word is a char array, but even there you risk overrunning its bounds. To protect yourself against that, specify a field width. For instance, if phrase[counter].word is an array of 20 chars, then use this ...
fscanf(fp, "%19s", phrase[counter].word);
... to ensure that scanf() does not write more than 20 chars (including a string terminator) to the array.
maybe...
void create_word(struct Word_setup phrase[], FILE *fp)
{
fscanf(fp, "%s", phrase[counter].word);
clean_word();
}
What i would like to do:
Read bits from one file (input file), and write these (with some probability) inverted bits to other file (output file).
What is the problem:
Probability idea seem not to be working properly. And more importantly, output file always contains more characters then the original input file, while they should contain equal number of characters.
In this code sample, instead of inverted bits i have put 'x' and 'y', so that it is more obvious that output file contains more characters
INPUT file: 01001
OUTPUT file: xyxxxyx
The code:
void invert_bits(FILE **input, FILE **output, double prob){
srand(clock());
char symbol;
while((symbol = getc(*input)) != EOF){
double result = rand()/RAND_MAX;
if(result < prob){
if(simbol == '0'){
char bit = 'x';
fprintf(*output, &bit);
}
else{
char bit = 'y';
fprintf(*output, &bit);
}
}else{
fprintf(*output, &symbol);
}
}
}
(f)printf expects a format string as its second argument. You are providing it with the address of a char, which is not even a valid string (since it is not NUL-terminated).
Don't do that. It's a bad habit. When you use printf, fprintf or sprintf always use a format string. (Read this for more information.)
You could have used fprintf(*output, "%c", bit); but it would be a lot simpler to just print the character with fputc(bit, *output);
I don't understand why you feel the need to pass the FILE* arguments as pointers, by the way.
You aren't using the fprintf function properly.
The function's signature is:
int fprintf ( FILE * stream, const char * format, ... );
Instead of a null terminated string, you're providing it with an address of a char, which might follow by a null character, or might not.
The correct way of printing a character with the *printf functions is:
fprintf(*output, "%c", bit);
P.S. Why are you receiving a pointer to the file handle, i.e. FILE** and not just FILE*?
I'm attempting to use the getc() function to copy the contents of one file into another. But I'm making an unknown logical error because the output of the following program is a bunch of garbage.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
FILE *f;
FILE *write;
f = fopen("nums.csv","r");
write = fopen("numsWrite.dat","w");
char tempChar;
int i;
for(i = 0; (tempChar = getc(f)) != EOF; i++)
{
tempChar = getc(f);
fprintf(write,"%c",tempChar);
}
fprintf(write,"\n");
fclose(f);
fclose(write);
return 0;
}
content of nums.csv is:
1256,2548,35151,15,56,38
program returns:
2624,55,55,8
There are several problems with your code.
int main() should be int main(void); this is a minor issue that almost certainly won't hurt anything, but the latter is more correct.
You don't check whether the fopen() calls succeed.
You're using i to count the characters you read, but you never do anything with its value.
The getc() function returns a result of type int, so you should definitely make tempChar an int. The reason for this is that it can return either a valid character value (which will fit in a char object) or the value EOF which is typically -1. By storing the result of getc() in a char object, either you'll never see EOF (if plain char is unsigned), or you won't be able to distinguish EOF from a valid input character.
In a comment on Razvan's answer, you said you changed the test to tempChar != EOF. Apart from the problem I explained above, on the first iteration of the loop tempChar has not been initialized, and the result of the comparison is unpredictable.
The conventional way to write an input loop using getc() is:
int c;
while ((c = getc(f)) != EOF) {
/* do something with c */
}
As a matter of style, write is not a very good name for a FILE*. For one thing, there's a function of that name (defined by POSIX, not by C, but it's still potentially confusing). You might call the FILE* objects in and out instead.
You call getc two times: once in the for condition and once in the for body. Delete this line: tempChar = getc(f); and try again.