3

The following code can be compiled and output undefined warnings for functions without including any header files.

// gcc a.c -std=c17 int main(){ printf("hello\n"); } 

This makes it impossible for me to define a function with the same name as the library function, as shown in the following code:

int printf(const char* str, ...) { return 0; } int main() { printf("hello 1\n"); printf("hello %d\n", 2); printf("hello 3\n"); } 

output:

hello 1 hello 3 

The first printf and the third call to library functions, but the second call to the function I defined. There are no redefined errors reported here, nor are there always calls to library functions or functions defined by me. Is this a bug in the compiler.

gcc version: gcc (Debian 12.2.0-14) 12.2.0

6
  • 9
    printf("hello 1\n"); can be optimized to puts("hello 1"); I found that gcc can apply this optimization even with -O0 option. Commented Mar 6, 2024 at 13:00
  • GCC silently replaces single-argument printf calls with puts. And the two snippets are unrelated, int main() {printf("hello %d\n", 2);} by itself does work too. Commented Mar 6, 2024 at 13:01
  • Possible duplicate of this Commented Mar 6, 2024 at 13:01
  • 5
    You have to differ between undeclared, and undefined symbols. A declaration is telling the compiler that a symbol exists, somewhere. A definition is the actual implementation of that symbol. A definition can also be a declaration, if the symbol haven't been declared before. What the <stdio.h> header does is to declare the function printf, but it doesn't define it. The definition is in a library that is automatically linked. Commented Mar 6, 2024 at 13:08
  • 1
    The output is the same whether or not you #include <stdio.h> Commented Mar 6, 2024 at 13:10

1 Answer 1

4

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.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.