简体   繁体   中英

Why does a declared prototype's function type not need to match the actual function's type?

I have a file1 that uses the function function_1 . In a file2 that I compile with file1, I defined the function as int function_1(char a) . In my file1, I have the prototype line void function_1(char a) . When I compile and test, everything works fine and I don't have any warnings. Why is this ok? Why even ask for the prototype function type if it works either way?

edit: I appreciate the warnings, but only melpomene has even made an attempt to answer the question that was asked, although I am not satisfied with the answer. If the linker doesn't check types, why should it even be part of the declaration at all?

It's because the compiler does all checks for the single file it's been given, and the compilation result just contains a reference to the name function_1 . The linker doesn't check any types, it just connects names.

C uses types for two purposes:

  1. For consistency checks (to prevent (some) obvious mistakes by the programmer)
  2. For code generation (the types tell the compiler how much memory to reserve for variables, what code to generate to access them, etc.)

All of this is done one compilation unit at a time. (A compilation unit is basically a .c file and all the headers it includes.)

You can declare the same name with different types in different compilation units. (Because the compiler only considers one unit at a time, it won't catch this.)

But that means you have two pieces of code with different ideas about what kind of entity the program is dealing with. This can lead to fun and exciting bugs at runtime. The exact details depend on your platform/ABI.

For example, on x86 a float and an int have the same size (4 bytes). You can define a function

float foo(void) { return 42.0f; }

in file A and declare it as

int foo(void);

in file B. You might expect the resulting program to mostly work, except it would reinterpret the bit pattern for 42.0f as an integer. But that's not what happens: The calling convention specifies that an int is returned in the %eax register whereas a float uses the %st0 floating-point register. The likely result is that A will put 42.0f in %st0 but B will take whatever happens to be in %eax as the return value of foo .

(This can go wrong in more interesting ways: You could have eg float bar[] = { ... }; in one file and void bar(void); in the other. This will probably result in the second file interpreting the contents of the floating-point array as machine code (to be executed when calling bar() ).)

This is why the compiler needs to know the types of the parameters and return value: It's so it can generate the right code for a function call (putting arguments where the function expects them, and retrieving the return value from where the function put it).

But unfortunately it doesn't cross-check declarations in multiple files, relying on the programmer to get it right. That's why it is best practice to declare your "exported" (or "public") functions (ie those don't marked as static ) in a header file that is included both by the file that defines the functions and by users of those functions. This way you don't need to manually declare functions you want to call, and if you do it anyway, any type mismatches will be caught as an invalid/incompatible redeclaration.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM