First it should be noted that defining a function with the same name as a library function is considered undefined behavior by the C standard. That being said, implementations like gcc allow this by defining such library functions as weak symbols, meaning that implementations may define their own version of such functions to override the library versions.
The printf function is defined as part of the standard C library that is linked in by default. It is declared in the stdio.h file.
When you compile the first piece of code you may get a warning that either printf is undeclared or that a default declaration is being used. The default implicit declaration for a function f is int f(), i.e. a function that takes an unspecified number of arguments and returns an int. This is (almost) compatible with the actual declaration, so in practice it ends up working as expected.
The problem with the second piece of code has nothing to do with the missing header. In fact, since your definition is compatible with the declaration in the header, there would be no error if you did so and the behavior would be the same.
The difference in behavior is due to optimizations being done by gcc. When it sees a call to printf with only a format string ending in a newline, it optimizes this to a call to puts as the observable behavior would be the same. If you compile with -S and look at the generated assembly, you'll see that's exactly what happens:
main: push rbp mov rbp, rsp mov edi, OFFSET FLAT:.LC0 call puts // printf call optimized away mov esi, 2 mov edi, OFFSET FLAT:.LC1 mov eax, 0 call printf // printf call not optimized mov edi, OFFSET FLAT:.LC2 call puts // printf call optimized away mov eax, 0 pop rbp ret
Of course, this optimization arguably shouldn't apply if a user-defined implementation of printf is given. If you compile with the flag -fno-builtin-printf, the standard printf function won't be linked in and this optimization will not happen.
printf("hello 1\n");can be optimized toputs("hello 1");I found that gcc can apply this optimization even with-O0option.printfcalls withputs. And the two snippets are unrelated,int main() {printf("hello %d\n", 2);}by itself does work too.<stdio.h>header does is to declare the functionprintf, but it doesn't define it. The definition is in a library that is automatically linked.#include <stdio.h>