简体   繁体   中英

Why don't we get a compile time error even if we don't include stdio.h in a C program?

How does the compiler know the prototype of sleep function or even printf function, when I did not include any header file in the first place?

Moreover, if I specify sleep(1,1,"xyz") or any arbitrary number of arguments, the compiler still compiles it. But the strange thing is that gcc is able to find the definition of this function at link time, I don't understand how is this possible, because actual sleep() function takes a single argument only, but our program mentioned three arguments.

/********************************/
int main()
{
 short int i;
 for(i = 0; i<5; i++)
 {
    printf("%d",i);`print("code sample");`
    sleep(1);
 }
 return 0;
}

Lacking a more specific prototype, the compiler will assume that the function returns int and takes whatever number of arguments you provide.

Depending on the CPU architecture arguments can be passed in registers (for example, a0 through a3 on MIPS) or by pushing them onto the stack as in the original x86 calling convention. In either case, passing extra arguments is harmless. The called function won't use the registers passed in nor reference the extra arguments on the stack, but nothing bad happens.

Passing in fewer arguments is more problematic. The called function will use whatever garbage happened to be in the appropriate register or stack location, and hijinks may ensue.

In classic C, you don't need a prototype to call a function. The compiler will infer that the function returns an int and takes a unknown number of parameters. This may work on some architectures, but it will fail if the function returns something other than int, like a structure, or if there are any parameter conversions.

In your example, sleep is seen and the compiler assumes a prototype like

int sleep();

Note that the argument list is empty. In C, this is NOT the same as void. This actually means "unknown". If you were writing K&R C code, you could have unknown parameters through code like

int sleep(t)
int t;
{
   /* do something with t */
}

This is all dangerous, especially on some embedded chips where the way parameters are passed for a unprototyped function differs from one with a prototype.

Note: prototypes aren't needed for linking. Usually, the linker automatically links with a C runtime library like glibc on Linux. The association between your use of sleep and the code that implements it happens at link time long after the source code has been processed.

I'd suggest that you use the feature of your compiler to require prototypes to avoid problems like this. With GCC, it's the -Wstrict-prototypes command line argument. In the CodeWarrior tools, it was the "Require Prototypes" flag in the C/C++ Compiler panel.

C will guess int for unknown types. So, it probably thinks sleep has this prototype:

int sleep(int);

As for giving multiple parameters and linking...I'm not sure. That does surprise me. If that really worked, then what happened at run-time?

This is to do with something called 'K & RC' and 'ANSI C'. In good old K & RC, if something is not declared, it is assumed to be int. So any thing that looks like a function call, but not declared as function will automatically take return value of 'int' and argument types depending on the actuall call.

However people later figured out that this can be very bad sometimes. So several compilers added warning. C++ made this error. I think gcc has some flag ( -ansic or -pedantic? ) , which make this condition an error.

So, In a nutshell, this is historical baggage.

Other answers cover the probable mechanics (all guesses as compiler not specified).

The issue that you have is that your compiler and linker have not been set to enable every possible error and warning. For any new project there is (virtually) no excuse for not doing so. for legacy projects more excuse - but should strive to enable as many as possible

Depends on the compiler, but with gcc (for example, since that's the one you referred to), some of the standard (both C and POSIX) functions have builtin "compiler intrinsics". This means that the compiler library shipped with your compiler (libgcc in this case) contains an implementation of the function. The compiler will allow an implicit declaration (ie, using the function without a header), and the linker will find the implementation in the compiler library because you're probably using the compiler as a linker front-end.

Try compiling your objects with the '-c' flag (compile only, no link), and then link them directly using the linker. You will find that you get the linker errors you expect.

Alternatively, gcc supports options to disable the use of intrinsics: -fno-builtin or for granular control, -fno-builtin-function . There are further options that may be useful if you're doing something like building a homebrew kernel or some other kind of on-the-metal app.

In a non-toy example another file may include the one you missed. Reviewing the output from the pre-processor is a nice way to see what you end up with compiling.

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