Others have already discussed the differences between <malloc.h> and <stdlib.h>
As for the warning when neither is included, that is the definition of how C functions work. A function without a prototype (which is what you have when you don't declare your own and don't include a header with one) is treated as a function with an int
return type and an unspecified argument list.
The compiler will perform default promotions (e.g. float to double and others) and the function is called. If the number of arguments used by the function is different from the number passed, or if the argument types after default promotions are not compatible with the function's implementation, it is undefined behavior.
See ISO 9899:1999 (C99) §6.5.2.2, ¶ 6:
If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float
are promoted to double
. These are called the default argument promotions. If the number of arguments does not equal the number of parameters, the behavior is undefined. If the function is defined with a type that includes a prototype, and either the prototype ends with an ellipsis (, ...) or the types of the arguments after promotion are not compatible with the types of the parameters, the behavior is undefined. If the function is defined with a type that does not include a prototype, and the types of the arguments after promotion are not compatible with those of the parameters after promotion, the behavior is undefined, except for the following cases:
- one promoted type is a signed integer type, the other promoted type is the corresponding unsigned integer type, and the value is representable in both types;
- both types are pointers to qualified or unqualified versions of a character type or
void
.
In the case of calling malloc()
without a prototype, this has the potential to be very bad. malloc()
accepts a size_t
argument and returns a void *
pointer. If the result of default promotion of your integer argument produces an integer different in size from size_t
, you will have undefined behavior. And if int
is a different size from void *
(e.g. on 64-bit systems, where int
is often 32-bits and void *
will be 64-bits,) the returned pointer will be messed up.