cfunctionlinker

Dangerous C function with different prototype is possible


Here's a question that's pretty special and one I've never been aware of before. Apparently I can create a function in C that differs from its function prototype in the passing parameters. The only requirement is that the function must not know the function prototype at compile time.

In my minimal example, I created the following header Test.h:

int Test(int);

I also created the following main.c:

#include <stdio.h>
#include "Test.h"

int main()
{
    int a = Test(5);
    int b = Test(5);
    printf("%d", a);
}

When I use this Test.c, an error occurs when compiling, as expected:

#include <stdio.h>
#include "Test.h"

void Test(void)
{
    printf("HW\n");
}

If I write Test.c like this, the program is compiled, linked and is executable. Of course, the variable a has a seemingly arbitrary value.

#include <stdio.h>

void Test(void)
{
    printf("HW\n");
}

Why can this program be linked and where does the return parameter come from, for example? This also seems very dangerous if something like this happens accidentally in larger code.

Quick additional info, when I use the same code in a C++ project, there are Linker Errors. So if I use main.cpp and Test.cpp instead of main.c and Test.c. Here things seem to be handled differently with the left. But I would also have been surprised, since C++ has function overloading.


Solution

  • The behavior is undefined, as you have observed.

    The reason you do not get an error is the linker has no information about the calling convention used in Test.c and main.c the only information is the symbol name Test and the type of reference (function call).

    Here is a possible sequence of events:

    You can investigate more by making the compiler produce the assembly source code with -s or -S, or play with Godbolt Compiler Explorer

    Compiling the code as C++ does produce an error because unlike C, C++ stores the argument types and class information along with the function names to handle overloading (it constructs synthetic names in a process called mangling) so when you link the main.o object file with the incorrect Test.o, the function names do not match and you get a missing symbol error.

    Your example is a very good example of potential problems when linking multiple modules. The programmer(s) should adhere to good practice for mitigating this issue:

    Modern compilers such as gcc and clang support warnings to to try and enforce this approach. You should always compile all source files with -Wall -Wextra -Werror which define these: