Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C function's declaration and definition in more than one source file

Tags:

c

declaration

Here is the content of src1.c:

#include <stdio.h>
extern int w;
//int go(char); // no need to declare here. WHY????
  main(){
    char a='f';
    go(a);
    printf("%d\n", w);
}

And here is the content of src2.c:

#include <stdio.h>
int w = 99;
int go(char t){
   printf("%c\n%d\n",t,sizeof(t));
}

Why isn't it mandatory to declare the go function in src1.c file after compiling it in Linux?

 cc src1.c src2.c; 

Does the compiler put the go function's definition from src2.c file above the main function's code so that declaration then would not be required?

In I do it this way:

#include <stdio.h>
int go(char); // need to declare here, because if not, arguments of go will be promoted to intS and they would conflict with char parameters defined in go. Error is droped!
  main(){
    char a='f';
    go(a);
} 
  int go(char t){
   printf("%c\n%d\n",t,sizeof(t));
}

So everyone that says, it is possible to pass whatever number and types of arguments in absence of prototype is wrong. They are promoted to ints in this case, but have to agree with those specified in definition.


I did some tests and found out that even it compiles with no errors it does not work correctly.

src1:

#include <stdio.h>
int go(int t){
    printf("%d\n%d\n",t,sizeof(t));
}

sr2.c:

#include <stdio.h>
int go(int); //if I omit this prototype, program outputs 1 which is far from correct answer :)
main(){ 
    double b=33453.834;
    go(b);
}

So finally the answer could only be undefined behavior.

Thanks Maxim Skurydin

like image 270
tautvilas Avatar asked Oct 26 '12 11:10

tautvilas


1 Answers

It's indeed not mandatory to have a prototype for a function before using it, but this is a quirk of the early days of C.

When there is no prototype present, the compiler cannot check the actual types that are being passed to the function or returned by it, which might be very bad in case of mismatch between the usage and declaration.

When compiler sees no prototype for go when go(b); is invoked, it assumes it has the following prototype int go(<any number of arguments can be there>). The default argument promotions are performed on the arguments prior to function invocation. Of course, if there is no function go in another translation module, you will get a linker error.

From c99 standard:

6.5.2.2 Function calls

If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions. If the number of arguments does not equal the number of parameters, the behavior is undefined. If the function is defined with a type that includes a prototype, and either the prototype ends with an ellipsis (, ...) or the types of the arguments after promotion are not compatible with the types of the parameters, the behavior is undefined. If the function is defined with a type that does not include a prototype, and the types of the arguments after promotion are not compatible with those of the parameters after promotion, the behavior is undefined, except for the following cases:

— one promoted type is a signed integer type, the other promoted type is the corresponding unsigned integer type, and the value is representable in both types;

— both types are pointers to qualified or unqualified versions of a character type or void.

6.3.1.1 Boolean, characters, and integers

2/ If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions.48) All other types are

update:

Does compiler put the go function's definition from src2.c file above the main function's code so that declaration then would not be required?

No, it doesn't put anything. A cite of the standard above says that no prototype is necessary. Each file is compiled independently so when src1.c is compiled, it doesn't know anything about src2.c and a go function definition inside.

So everyone that says, it is posible to pass whatever number and types of arguments in absence of prototype is wrong. They are promoted to intS in this case, but have to agree with those specified in definition.

It is possible and I've faced a couple of obscure bugs after system-wide change that compiled just fine without any warnings for some reason (actually, it's undefined behavior). Again, since each *.c file is compiled independently, there is now way it can check the number of arguments and their types of go function defined in another translation unit. If the function takes more arguments that you have provided, the "unused" arguments will be filled with some random data. You should keep in mind, that if arguments don't match - it's undefined behavior, which means that anything can happen.

like image 57
Maksim Skurydzin Avatar answered Oct 07 '22 05:10

Maksim Skurydzin