简体   繁体   中英

Argument is passed to function

Consider the following sources: main.c

int main(){
    print_hello("str");
    return 0;
}

and hello.c

#include<stdio.h>
void print_hello(){
    printf("Hello world\n");
}

After compiling and linking gcc -o hello hello.c main.c it's work fine, but I'm expected that error will be occured. Because signatures of print_hello in main.c and print_hello in hello.c are differents. Why it works fine?

You've not told the compiler about what the print_hello() function looks like in the file main.c . If you had a header file hello.h such as:

#ifndef HELLO_H_INCLUDED
#define HELLO_H_INCLUDED

extern void print_hello(void);

#endif

And you had #include "hello.h" at the top of each file, then you'd get the relevant compiler warnings from misusing print_hello() in main.c .

Your compiler uses calling convention which allows extra arguments passing to called function without extra effects. This is typical between calling conventions for C, due to its support of variable argument lists (as in printf, scanf, etc.) In such conventions,

  • arguments are passed in the order that the first argument is the closest to the stack top (if all are placed on stack) or in a equivalent register, and others are put in sequence,
  • caller knows used stack size and restores stack pointer after function call.

In X86 calling conventions this is usually named "cdecl" (optionally, with 1 or 2 underscores).

A such convention is tolerable to extra arguments, but, definitely, not to unspecified arguments which are then used by a callee function; the latter case results in garbage in arguments.

Generally one shouldn't exploit such runtime features, but there are some corner cases it's useful.

To prevent mismatch between a function use and its definition, you should use identical declarations for both. Typically it's done including the same header file to all sources where this function is declared or called.

In addition to the other explanations, assuming GCC 4.8 or better on Linux :

You should compile with gcc -Wall -std=c99 to get all warnings ( -Wall ) and to tell the compiler which standard you want to obey ( -std=c99 ). With these settings you get warned:

  main.c: In function 'main':
  main.c:2:5: warning: implicit declaration of function 'print_hello' 
              [-Wimplicit-function-declaration]
  print_hello("str");
 ^

As to why no errors are given, they could be given only at link time by the linker ( ld , called by gcc ). And Unix and Linux linkers (from GNU binutils ) work only with names: each function (and more generally each linked symbol) has a name (and both object files and executables are in ELF , which defines how names are represented, etc....) If two names are the same the linker resolves by making them refer to the same address (see Linkers and Loaders by Levine). So since both names (or symbols) in main.o and hello.o are print_hello they are linked together. Use nm to query the names in an ELF object or executable file. Of course, what happens then at runtime is undefined behavior , but the ABI conventions for x86 (and x86-64 ) enable the behavior you expect. The important thing is that ELF do not keep typing or signature meta-data in object files.

With C++, it is a bit different: the compiler is transforming a function name by name mangling (so names in ELF *.o files are not simply the C++ source name), so the linker sees different names (and would have failed).

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