Search code examples
cstaticprintfstdioc-standard-library

Is printf a static function in C?


I'm pretty new to C, and I know that static functions can only be used within the same object file. Something that still confuses me though, is how if I hover over a call to printf in my IDE it tells me that printf is a static function, when I can perfectly use printf in multiple object files without any problems? Why is that?




Edit: I'm using Visual Studio Code the library is stdio.h and compiling using GCC

#include <stdio.h>
int main()
{
  printf("Hello, world!");
  return 0;
}

Hovering over printf would give that hint

Edit 2: if static inline functions are different from inline functions how so? I don't see how making a function inline would change the fact that it's only accessible from the same translation unit

Edit 3: per request, here's the definition of printf in the stdio.h header

__mingw_ovr
__attribute__((__format__ (gnu_printf, 1, 2))) __MINGW_ATTRIB_NONNULL(1)
int printf (const char *__format, ...)
{
  int __retval;
  __builtin_va_list __local_argv; __builtin_va_start( __local_argv, __format );
  __retval = __mingw_vfprintf( stdout, __format, __local_argv );
  __builtin_va_end( __local_argv );
  return __retval;
}

screenshot of the printf definition in my IDE


Solution

  • Thanks for all the updates. Your IDE is showing you implementation details of your C library that you're not supposed to have to worry about. That is arguably a bug in your IDE but there's nothing wrong with the C library.

    You are correct to think that a function declared static is visible only within the translation unit where it is defined. And that is true whether or not the function is also marked inline. However, this particular static inline function is defined inside stdio.h, so every translation unit that includes stdio.h can see it. (There is a separate copy of the inline in each TU. This is technically a conformance violation since, as chux surmises, it means that &printf in one translation unit will not compare equal to &printf in another. However, this is very unlikely to cause problems for a program that isn't an ISO C conformance tester.)

    As "Adrian Mole" surmised in the comments on the question, the purpose of this inline function is, more or less, to rewrite

    printf("%s %d %p", string, integer, pointer);
    

    into

    fprintf(stdout, "%s %d %p", string, integer, pointer);
    

    (All the stuff with __builtin_va_start and __mingw_vfprintf is because of limitations in how variadic functions work in C. The effect is the same as what I showed above, but the generated assembly language will not be nearly as tidy.)


    Update 2022-12-01: It's worse than "the generated assembly language will not be nearly as tidy". I experimented with all of the x86 compilers supported by godbolt.org and none of them will inline a function that takes a variable number of arguments, even if you try to force it. Most silently ignore the force-inlining directive; GCC gets one bonus point for actually saying it refuses to do this:

    test.c:4:50: error: function ‘xprintf’ can never be inlined 
        because it uses variable argument lists
    

    In consequence, every program compiled against this version of MinGW libc, in which printf is called from more than one .c file, will have multiple copies of the following glob of assembly embedded in its binary. This is bad just because of cache pollution; it would be better to have a single copy in the C library proper -- which is exactly what printf normally is.

    printf:
            mov     QWORD PTR [rsp+8], rcx
            mov     QWORD PTR [rsp+16], rdx
            mov     QWORD PTR [rsp+24], r8
            mov     QWORD PTR [rsp+32], r9
            push    rbx
            push    rsi
            push    rdi
            sub     rsp, 48
            mov     rdi, rcx
            lea     rsi, QWORD PTR f$[rsp+8]
            mov     ecx, 1
            call    __acrt_iob_func
            mov     rbx, rax
            call    __local_stdio_printf_options
            xor     r9d, r9d
            mov     QWORD PTR [rsp+32], rsi
            mov     r8, rdi
            mov     rdx, rbx
            mov     rcx, QWORD PTR [rax]
            call    __stdio_common_vfprintf
            add     rsp, 48
            pop     rdi
            pop     rsi
            pop     rbx
            ret     0
    

    (Probably not exactly this assembly, this is what godbolt's "x64 MSVC 19.latest" produces with optimization option /O2, godbolt doesn't seem to have any MinGW compilers. But you get the idea.)