+1  A: 

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?

Jason Dagit
A: 

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 (i.e., 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.

Ben Collins
+5  A: 

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.

DGentry
+3  A: 

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.

Ben Combee
A: 

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.

/Allan

Allan Wind
A: 

This is to do with something called 'K & R C' and 'ANSI C'. In good old K & R C, 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.

Vardhan Varma
+1  A: 

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

itj