Implicit function declarations and linkage

会有一股神秘感。 提交于 2019-12-05 20:22:55

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.

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.

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.

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!