Implicit function declarations and linkage

2019-05-17 07:53发布

Recently I've learnt about implicit function declarations in C. The main idea is clear but I have some troubles with understanding of the linkage process in this case.

Consider the following code ( file a.c):

#include <stdio.h>

int main() {
    double someValue = f();
    printf("%f\n", someValue);
    return 0;
}

If I try to compile it:

gcc -c a.c -std=c99

I see a warning about implicit declaration of function f().

If I try to compile and link:

gcc a.c -std=c99

I have an undefined reference error. So everything is fine.

Then I add another file (file b.c):

double f(double x) {
    return x;
}

And invoke the next command:

gcc a.c b.c -std=c99

Surprisingly everything is linked successfully. Of course after ./a.out invocation I see a rubbish output.

So, my question is: How are programs with implicitly declared functions linked? And what happens in my example under the hood of compiler/linker?

I read a number of topics on SO like this, this and this one but still have problems.

4条回答
甜甜的少女心
2楼-- · 2019-05-17 08:28

Here is what is happening.

  1. Without a declaration for f(), the compiler assumes an implicit declaration like int f(void). And then happily compiles a.c.
  2. When compiling b.c, the compiler does not have any prior declaration for f(), so it intuits it from the definition of f(). Normally you would put some declaration of f() in a header file, and include it in both a.c and b.c. Because both the files will see the same declaration, the compiler can enforce conformance. It will complain about the entity that does not match the declaration. But in this case, there is no common prototype to refer to.
  3. In C, the compiler does not store any information about the prototype in the object files, and the linker does not perform any checks for conformance (it can't). All it sees is a unresolved symbol f in a.c and a symbol f defined in b.c. It happily resolves the symbols, and completes the link.
  4. Things break down at run time though, because the compiler sets up the call in a.c based on the prototype it assumed there. Which does not match what the definition in b.c looks for. f() (from b.c) will get a junk argument off the stack, and return it as double, which will be interpreted as int on return in a.c.
查看更多
女痞
3楼-- · 2019-05-17 08:35

After compiling, all type information is lost (except maybe in debug info, but the linker doesn't pay attention to that). The only thing that remains is "there is a symbol called "f" at address 0xdeadbeef".

The point of headers is to tell C about the type of the symbol, including, for functions, what arguments it takes and what it returns. If you mismatch the real ones with the ones you declare (either explicitly or implicitly), you get undefined behavior.

查看更多
放荡不羁爱自由
4楼-- · 2019-05-17 08:43

How are programmes with implicitly declared functions are linked? And what happens in my example under the hood of compiler/linker?

The implicit int rule has been outlawed by the C standard since C99. So it's not valid to have programs with implicit function declarations.

It's not valid since C99. Before that, if a visible prototype is not available then the compiler implicitly declares one with int return type.

Surprisingly everything is linked successfully. Of course after ./a.out invocation I see a rubbish output.

Because you didn't have prototype, compiler implicitly declares one with int type for f(). But the actual definition of f() returns a double. The two types are incompatible and this is undefined behaviour.

This is undefined even in C89/C90 in which the implicit int rule is valid because the implicit prototype is not compatible with the actual type f() returns. So this example is (with a.c and b.c) is undefined in all C standards.

It's not useful or valid anymore to have implicit function declarations. So the actual detail of how compiler/linker handles is only of historic interest. It goes back to the pre-standard times of K&R C which didn't have function prototypes and the functions return int by default. Function prototypes were added to C in C89/C90 standard. Bottom line, you must have prototypes (or define functions before use) for all functions in valid C programs.

查看更多
够拽才男人
5楼-- · 2019-05-17 08:51

First of all, since C99 , implicit declaration of a function is removed from the standard. compilers may support this for compilation of legacy code, but it's nothing mandatory. Quoting the standard foreword,

  • remove implicit function declaration

That said, as per C11, chapter §6.5.2.2

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.

So, in your case,

  • the function call itself is implicit declaration (which became non-standard since C99),

  • and due to the mismatch of the function signature [Implicit declaration of a function were assumed to have an int return type], your code invokes undefined behavior.

Just to add a bit more reference, if you try to define the function in the same compilation unit after the call, you'll get a compilation error due to the mismatch signature.

However, your function being defined in a separate compilation unit (and missing prototype declaration), compiler has no way to check the signatures. After the compilation, the linker takes the object files and due to the absence of any type-checking in the linker (and no info in object files either), happily links them. Finally, it will end up in a successful compilation and linking and UB.

查看更多
登录 后发表回答