Leaving out forward declarations (prototypes) - c

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.

Related

explicit type is missing ("int" assumed)

i am trying to develop a project with IAR.
here is the error message:
Error[Pe260]: explicit type is missing ("int" assumed)
Regards.
When i try:
void send_data_byte(unsigned char dattt) {
i see a new error:
Error[Pe159]: declaration is incompatible with previous "send_data_command"
my sen_data_command function in is on the below
send_data_byte(unsigned char dattt){
for(j=0;j<8;j++){
pwmstart(1);
pwmstop(18);
if(dattt & 0x01){
__delay_cycles(1687);
dattt=dattt>>1;
}
else
{
__delay_cycles(562);
dattt=dattt>>1;
}
}
pwmstop(1);
}
void send_data_command(unsigned char dat){
for (int r=0;r<160;r++)
{pwmstart(1);}
for (int y=0;y<80;y++)
{pwmstop(1);}
send_data_byte(dat);
repeat();
}
You need to explicitly declare the function's return type. In this case if you have nothing to return, you should declare it as void:
void send_data_byte(unsigned char dattt) {
The error message indicate that, you don't explicitly declare any return type of function send_data_type. and it suggesting to put a int before send_data_type. Error massage suggest u to write it in following way:
int send_data_byte(unsigned char dattt) {
You can also write declare the function as void if you don't need to return anything.
void send_data_byte(unsigned char dattt) {
There are one more error in your code is that, in function send_data_byte you don't declare j. The following part of the code
send_data_byte(unsigned char dattt){
for(j=0;j<8;j++){
should be,
send_data_byte(unsigned char dattt){
int j;
for(j=0;j<8;j++){
Old versions of the C language, prior to 1999, had an "implicit int" rule. If you declared a function without specifying the return type, it would be assumed to return a result of type int. The 1999 standard dropped this rule, and made it mandatory to specify the return type on any function declaration or definition. Many compilers cater to old code by permitting such declarations, or by diagnosing them with a non-fatal warning.
Even in pre-1999 C there was no real reason to take advantage of the "implicit int" rule. If a function returns an int result, you can always declare it that way explicitly. (Very old C, before 1989, didn't have void, but support for post-1989 C is essentially universal now.)
As for your other error:
Error[Pe159]: declaration is incompatible with previous "send_data_command"
it indicates that you have two declarations, or a declaration and a definition, of send_data_command, and they differ in some way. There's only one occurrence of send_data_command in your question, so you haven't shown us the code that causes that error. Make sure all references to send_data_command in your program are consistent, and make sure a declaration -- specifically a prototype (which specifies the types of any parameters) -- is visible any time you call it.
(Incidentally, your code would be much easier to read if it were indented properly. There are automated tools that can help you do this. Indentation should reflect the nested structure of your code.)

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

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

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.

Segmentation fault while accessing the return address from a C function in 64 bit machine

I have code in C (linux(x86_64)) some like this:
typedef struct
{
char k[32];
int v;
}ABC;
ABC states[6] = {0};
ABC* get_abc()
{
return &states[5];
}
while in main():
int main()
{
ABC *p = get_abc();
.
.
.
printf("%d\n", p->v);
}
I am getting segmentation fault at printf statement while accessing p->v. I tried to debug it from gdb and it says "can not access the memory". One important thing here is that when I compile this code, gcc throws me a warning on ABC *p = get_abc(); that I am trying to convert pointer from integer. My question here is that I am returning address of structure from get_abc() then why compiler gives me such warning? why compiler considers it as integer? I think I am getting segmentation fault due to this warning as an integer can not hold memory address in x86_64.
Any help would be appreciated.
Define the get_abc prototype before main function. If function prototype is not available before that function call means, compiler will treat that function by default as passing int arguments and returning int. Here get_abc actually returning 8 byte address, but that value has been suppressed to 4 bytes and it is stored in ABC *p variable which leads the crash.
ABC* get_abc();
int main()
{
ABC *p = get_abc();
}
Note : This crash will not occur in 32 bit machine where size of int and size of address is 4 bytes, because suppression will not happen. But that warning will be there.
You haven't shown us all your code, but I can guess with some confidence that the get_abc() and main() functions are defined in separate source files, and that there's no visible declaration of get_abc() visible from the call in main().
You should create a header file that contains a declaration of get_abc():
ABC *get_abc();
and #include that header both in the file that defines get_abc() and in the one that defines main(). (You'll also need header guards.) You'll need to move the definition of the type ABC to that header.
Or, as a quick-and-dirty workaround, you can add an explicit declaration before your definition of main() -- but that's a rather brittle solution, since it depends on you to get the declaration exactly right.
In the absence of a visible declaration, and undeclared function is assumed to return int. The compiler sees your call to get_abc(), generates code to call it as if it returned an int, and implicitly converts that int value to a pointer. Hilarity ensues.
Note: There actually is no implicit conversion from int to pointer types, apart from the special case of a null pointer constant, but many compilers implement such an implicit conversion for historical reasons. Also, the "implicit int" rule was dropped in the 1999 version of the standard, but again, many compilers still implement it for historical reasons. Your compiler should have options to enable better warnings. If you're using gcc, try gcc -pedantic -std=c99 -Wall -Wextra.

Resources