%s expects 'char *', but argument is 'char[100]' - c

I have something along the lines of:
#include <stdio.h>
typedef struct {
char s[100];
} literal;
literal foo()
{
return (literal) {"foo"};
}
int main(int argc, char **argv) {
printf("%s", foo().s);
return 0;
}
And I get this error when compiling it (with gcc):
warning: format ‘%s’ expects argument of type ‘char *’, but argument 2
has type ‘char[100]’ [-Wformat=]
any1 has any ideas on how I can fix it? And what is wrong with that function return type?
EDIT
The problem (if not clear in the discussion) was the c standard gcc was using to compile the file. if you use -std=c99 or -std=c11 it works.

It is not an error but a warning, not all warnings that -Wall produces are sensible.
Here the compiler is "kind-of" right: before evaluation your argument is an array and not a pointer, and taking the address of a temporary object is a bit dangerous. I managed to get rid of the warning by using the pointer explicitly
printf("%s\n", &foo().s[0]);
Also you should notice that you are using a rare animal, namely an object of temporary lifetime, the return value or your function. Only since C99, there is a special rule that allows to take the address of such a beast, but this is a corner case of the C language, and you would probably be better off by using some simpler construct. The lifetime of the pointer ends with the end of the expression. So if you would try to assign the pointer to a variable, say, and use it later on in another expression, you would have undefined behavior.
Edit(s): As remarked by mafso in a comment, with C versions from before C99, it was not allowed to take the address of the return value of the function. As Pascal Cuoq notes, the behavior of your code is undefined even for C99, because between the evaluation of the arguments and the actual call of the function there is a sequence point. C11 rectified this by indroducing the object of temporary lifetime that I mentioned above.

I don't exactly why maybe someone with more knowledge in C than me can explain it but writing main() on this way:
int main(int argc, char **argv) {
literal b = foo();
printf("%s", b.s);
return 0;
}
The code prints "foo" correctly on GCC 4.5.4

Related

Unexpected result when parsing argument in main function in C

I have really simple code to understand command line arguments parsing of main in C language:
#include <stdio.h>
int main(int argc, char* argv[]){
for(int i=1; i < argc; i++)
printf("%d\n", argv[i]);
printf("Number of arguments: %d\n", argc);
}
Having compiled and executed in a terminal passing 3 integers (eg. 1, 2, 3), I obtain the following strange result:
1175842993
1175842995
1175842997
Number of arguments: 4
I know that the solution is lying right straightforwardly towards my eyes, but it's been a hour that I cannot actual figure it out! Any hints? Thanks!
Dereference your pointer:
printf("%d\n", *argv[i]);
You may also be a little surprised, as you will get a numerical value represented by the (first) character printed; for instance for 1 it is 49.
In general you can tell your compiler to tell you a bit more by enabling more warnings. For instance gcc -Wall would tell you:
warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘char *’ [-Wformat=]
printf("%d\n", argv[i]);
Which would actually be a pretty solid hint.
And a side note: if you declare your main as int, you should return one too. ;) (But see EDIT2 for details).
EDIT: I guess I should have mentioned that with the actual output surprise in the second paragraph.
If verbatim reprint of the argument(s) is what you actually wanted. You'd use %s in your formatting string as Jonathan Leffler pointed out in the comment. In that case, you want the argument passed to be pointer to a string again (i.e. you do not use the * dereference operator).
EDIT2: As pointed out by Antti Haapala regarding the closing "Side note".
When using C99 or C11 you do not have to explicitly return (a value) for your main (and we're talking about main) that has been declared to return int. In that case default of 0 will be assumed. You really only need to do that for when adhering to C89 standard or other functions. For sake of simplicity though and/or when not sure what your target compiler configuration is, you won't be wrong being (type) consistent between your declared and actually returned value type. And again. gcc -Wall for example would let you know about that.

Lack of access specifer & in C does not result in a compile error

In C, when & is absent for an argument in scanf(), no compilation error is produced; instead, the displayed results are wrong (i.e. a semantic error occurs).
Consider the following code:
char str[30];
int a;
printf("Enter the value");
scanf("%s %d", str, a); // This is the statement in question.
printf("You entered %s %d", str, a);
Here I know str is a character array so it will have a base address, and thus will not produce a compilation error. But why does the absence of & for the argument a not result in a compilation error?
Also str gives correct output, but the integer is always producing the value -28770 as the output. Why is this?
scanf has the prototype:
int scanf(const char *fmt, ...);
The ... means that any number of arguments of any type may be specified after fmt. Hence it is the responsibility of the caller, not the compiler, to ensure that the provided arguments match what the function will be expecting.
int scanf(const char *format, ...);
This is the prototype for scanf. First argument is char* and other arguments are variable lengths. So no error will be generated.
As Umamahesh P wrote, you won't get a compilation error, because scanf is a variable-argument function. As to what happens when you run the program, it is what the C standard calls "undefined behaviour". Anything can happen. You need to look at what happens at the machine-instruction and memory address level to see what exactly scanf does with the integer value you gave it instead of a pointer.
You can get a compiler warning, depending on what compiler you use and what options you specify.
Also even if it tries to show those warnings, your compiler will be totally unable to do it if you use vscanf() or if you use a dynamic string as your format parameter.
For instance, this is the output I get using mingw-64 gcc 4.5.4:
gcc -Wall -o small.exe small.c
small.c:7:3: warning: format '%d' expects type 'int *', but argument 3 has type 'int'
small.c:7:8: warning: 'a' is used uninitialized in this function
Just add -Werror option to turn these warnings into errors.

Leaving out forward declarations (prototypes)

I'm up to lesson 14 on the famous "Learn C The Hard Way" online course.
In that lesson, it introduces the concept of forward declarations in C.
There are two forward declarations in the code sample. One of them can be commented out, and the code still compiles, but the other one cannot be commented out. To me they both look equally as important.
Here's the code. It simply prints out all characters and their hex codes if they are from the alphabet, otherwise it skips them.
The two compiler outputs are at the bottom of the code.
Could someone explain why one errors out and the other one doesn't?
#include <stdio.h>
#include <ctype.h>
// forward declarations
int can_print_it(char ch); //NOT OK to skip(??)
void print_letters(char arg[]); //OK to skip(??)
void print_arguments(int argc, char *argv[])
{
int i = 0;
for(i = 0; i < argc; i++) {
print_letters(argv[i]);
}
}
void print_letters(char arg[])
{
int i = 0;
for(i = 0; arg[i] != '\0'; i++) {
char ch = arg[i];
if(can_print_it(ch)) {
printf("'%c' == %d ", ch, ch);
}
}
printf("\n");
}
int can_print_it(char ch)
{
return isalpha(ch) || isblank(ch);
}
int main(int argc, char *argv[])
{
print_arguments(argc, argv);
return 0;
}
If I comment out the first forward declaration (first one only), this happens:
cc -Wall -g ex14.c -o ex14
ex14.c: In function ‘print_letters’:
ex14.c:24:9: warning: implicit declaration of function ‘can_print_it’ [-Wimplicit-function-declaration]
ex14.c: At top level:
ex14.c:32:5: error: conflicting types for ‘can_print_it’
ex14.c:33:1: note: an argument type that has a default promotion can’t match an empty parameter name list declaration
ex14.c:24:12: note: previous implicit declaration of ‘can_print_it’ was here
make[1]: *** [ex14] Error 1
make[1]: Leaving directory `/home/andrew/c_tutorials/lesson14/ex14_original'
make: *** [all] Error 2
And if I comment out the second declaration (second one only), this happens:
cc -Wall -g ex14.c -o ex14
ex14.c: In function ‘print_arguments’:
ex14.c:13:9: warning: implicit declaration of function ‘print_letters’ [-Wimplicit-function-declaration]
ex14.c: At top level:
ex14.c:17:6: warning: conflicting types for ‘print_letters’ [enabled by default]
ex14.c:13:9: note: previous implicit declaration of ‘print_letters’ was here
make[1]: Leaving directory `/home/andrew/c_tutorials/lesson14/ex14_original'
Well compiler hints why this does happen. The crucial thing it here:
ex14.c:32:5: error: conflicting types for ‘can_print_it’
ex14.c:33:1: note: an argument type that has a default promotion can’t match an empty parameter name list declaration
The argument for can_print_it has a default promotion, therefore it cannot have an implicit declaration. Great read on it is here: Default argument promotions in C function calls. Basically, the argument type for can_print_it (char) is illegal to be used with implicit declarations. To make it work, you would need to use appropriate type, for char it is int. For other types you can check out the linked question and answer.
The print_letters has no such arguments its argument is of a pointer type.
Side-note: As one could see, with 3 wrong answers, people get confused. Implicit declarations are not used often and can be tricky. IMO in general, or at least practical applications, their usage is discouraged. Nevertheless, they are perfectly legal.
You give a function prototype so the compiler knows what to do when it first comes across that function in your code. Specifically, if it has no other information, the compiler will
assume that the return value is an int
promote arguments:
"integer types" to int (so char becomes int, for example)
promote float to double
pointers become pointers to int
The problem is that when you convert a char to an int, it is possible that the significant byte ends up offset by (for example) 3 bytes from where you thought you stored it - since a value like 0x33 might be stored as 0x00000033. Depending on the architecture of the machine, this will cause a problem.
The same is not true with pointers. A pointer "always" has the same size, and always points to the first byte of the object (this wasn't always true... some of us remember "near" and "far" pointers, and not with nostalgia). Thus, even though the compiler may think it is passing a pointer to an int, the subsequent interpretation (by the function-that-had-not-been-declared) as a pointer to a char does not cause a problem.
The fact that your second function is declared as void when the compiler assumed it would return int does not matter, since you never used its return value (which it doesn't have) in an assignment or expression. So even though it's a bit confusing for the compiler, this generates only a warning, not an error. And since the argument is a pointer, the promotion rules again don't cause a conflict.
That said - it is a good idea to always declare your function prototypes before using them; in general, you should turn on all compiler warnings, and improve your code until it compiles without warnings or errors.

What does "char *p, *getenv();" do? Why a function with no args in a variable declaration line?

I'm reading a bit of code in C, and in the variable declaration line, there's "char *p, *getenv();"
I understand "char *p" of course. What does "char *getenv()" do? Later on in the code, the function getenv() is called with a variable passed to it. But what's the point of "char *getenv();" without any arguments?
Sorry if this is basic. I'm just starting to learn C.
It is "valid C" (I would almost say "unfortunately") - but it is not particularly useful. It is a "declaration without declaration" - "I will be using a function getenv() but I'm not going to tell you what the arguments will be". It does have the advantage of preventing a compiler warning / error - something that would normally be prevented with a "real" function prototype, for example by including the appropriate .h file (in this case, stdlib.h).
To clarify: the following code
#include <stdio.h>
int main(void) {
char *p;
p = getenv("PATH");
printf("the path is %s\n", p);
return 0;
}
will throw a compiler warning (and you should never ignore compiler warnings):
nonsense.c: In function ‘main’:
nonsense.c:5: warning: implicit declaration of function ‘getenv’
nonsense.c:5: warning: assignment makes pointer from integer without a cast
Either adding #include <stdlib.h> or the line you had above, will make the warning go away (even with -Wall -pedantic compiler flags). The compiler will know what to do with the return value - and it figures out the type of the arguments on the fly because it knows the type when it sees it.
And that is the key point: until you tell the compiler the type of the return value of the function it does not know what to do (it will assume int, and make appropriate conversions for the variable you are assigning to - this can be inappropriate. For example, a pointer is often bigger than an int, so information will be lost in the conversion.)
It's a declaration (an old-style one without a prototype, since the argument list is missing) for the function getenv. Aside from being arguably bad style to hide a function declaration alongside the declaration of an object like this, it's bad to omit the prototype and bad not to be using the standard headers (stdlib.h in this case) to get the declarations of standard functions.

Extremely simple C program won't compile in gcc compiler

I have the following program
main()
{
char a,b;
printf("will i get the job:");
scanf("%c",&a);
printf("%c",a);
printf("We did it");
}
I saved the file as Hope.c. When I try to compile the code above with the gcc compiler, I will get the following error:
Hope.c:In function 'main':
Hope.c:4:2:warning:incompatible implicit declaration of built-in function 'printf' [enabled by default]
Hope.c:5:2:warning:incompatible implicit declaration of built-in function scanf[enabled by default]
The compiler gives this error when I use printf() or scanf(), even in a simple "Hello world" program.
Is there something wrong with my code, or is there a problem with the compiler?
You're missing #include <stdio.h> at the top. Note that these were warnings, though, not errors. Your program should still have compiled as is.
Also, for good measure, you should write out the return type and parameters to main() and return a value at the end.
#include <stdio.h>
int main(void)
{
char a,b;
printf("will i get the job:");
scanf("%c",&a);
printf("%c",a);
printf("We did it");
return 0;
}
When you call functions that you're not familiar with, look at the man page and include the headers that are mentioned there. It's #include <stdio.h> in your case.
That's really extermely important, e.g. I have experienced printf( "%s", func( ) ) causing a segmentation fault although func() returned a valid null terminated string, but there was no prototype that declared the return type of func() as char * (doing a little bit research, we found that only the last four bytes of the 64 bit pointer have been passed to printf())
Yes, there's something wrong with the code. You're using the functions printf and scanf without declaring them.
The usual thing to do is use the declarations shipped with the compiler (because they're known to be correct) with
#include <stdio.h>
C.89/C.90 permits implicit declarations of functions. Your warning messages are informing you that you have not provided explicit declarations for scanf and printf. As has been mentioned, you can correct this by adding #include <stdio.h> to the beginning of your program. By not doing so, the behavior of your program is undefined.
Because scanf() and printf() have implicit declarations, they are treated as if their prototypes were given as:
extern int scanf ();
extern int printf ();
These declarations state that scanf() and printf() take an as of yet unknown number of arguments and return and int. However, this kind of declaration still assumes that these functions will take a fixed number of arguments. This is incompatible with their true prototypes, in which they take a variable number of arguments:
extern int scanf (const char *, ...);
extern int printf (const char *, ...);
Your C compiler apparently knows about the true prototypes of these functions because it treats those functions as "built-ins", meaning it can generate special case code when compiling to source code that calls those functions. Since the implicit declaration does not match its built-in knowledge of their prototypes, it generated the warning.
A compiler that did not have this "built-in knowledge" probably would not have generated the warning. It would then have generated code to call scanf() and printf() as if they took a fixed number of arguments. The error then may occur at runtime, since the calling convention for a function that takes a variable number of arguments may differ from the calling convention of a function that takes a fixed number of arguments.
This is all described in C.89 §3.3.2.2.
If the expression that precedes the parenthesized argument list in
a function call consists solely of an identifier, and if no
declaration is visible for this identifier, the identifier is
implicitly declared exactly as if, in the innermost block containing
the function call, the declaration
extern int identifier();
appeared.
...
If the expression that denotes the called function has a type that
does not include a prototype, the integral promotions are performed on
each argument and arguments that have type float are promoted to
double. ... If the function is defined with
a type that includes a prototype, and the types of the arguments after
promotion are not compatible with the types of the parameters, or if
the prototype ends with an ellipsis ( ", ..." ), the behavior is
undefined.
Note that C.99 removes the allowance for implicit function declarations.

Resources