tautvilas
tautvilas

Reputation: 137

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

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

Upvotes: 9

Views: 2143

Answers (5)

P.P
P.P

Reputation: 121387

When the compiler sees go() in str1.c it assumes that the function is defined elsewhere. It's only at the link time that the linker searches for the definition of go().

I think, you are compiling the two files separately and link them together which is fine. Because at link time the defintion of go() exists.

If you try to compile str1.c (gcc str1.c as opposed to gcc -c str1.c)separately, you'll get the error about go() not being found by linker.

UPDATE:

Even the implicit declaration by the compiler is not standard compliant (since C99).

Technically, every function should have a prototype irrespective of its return type if it is called before compiler can see it's defintion. The implicit declaring an int returning function is no longer valid ( valid in C89) and has been removed since C99 (and C11).

Although, most compilers still issue only a warning about this, not an error. But if some compiler refuses to compile due to function prototype not being present, then you can't complain about it as it is not standard compliant.

Upvotes: 0

Maksim Skurydzin
Maksim Skurydzin

Reputation: 10541

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.

Upvotes: 8

Jeyaram
Jeyaram

Reputation: 9474

By default

go() will be int go(). i.e returns int and accepts any number of arguments. so your actual function matches with default function type.

Upvotes: 0

Omkant
Omkant

Reputation: 9204

When you make one executable using these two source files then final executable will have definition of go() so no need of it.

But it's better to put declaration in a header file and then include that header file in both the source files

here is header file someheader.h

#ifndef __SMH_
#define __SMH_

int go(char);

#endif

now include it like this

#include "someheader.h"

in src1.c and src2.c

Upvotes: 0

Manik Sidana
Manik Sidana

Reputation: 2155

First of all, function declarations should be put into header files. Now an answer to your question:
When you compile both the files, then at the time of linking, the linker finds the symbol definition of go() in src2.o and thus resolves the symbol reference in the executable, this is the reason why your program works.

You are trying to use sizeof() which is a compile time operator and it would thus output 1 since you are suing it on a character.
You are also passing an integer value >255 to a char variable, this would cause overflow and t would store 1789modulo255 .

Upvotes: 0

Related Questions