Search code examples
cfunctionmacrosstdiogetc

How can I make sure I will use the function version of a C standard library function like "getc" instead of the function-like macro version?


I was learning about functions vs function-like macros in C.

Was reading the C Standard and I found this:

Any macro definition of a function can be suppressed locally by enclosing the name of the function in parentheses, because the name is then not followed by the left parenthesis that indicates expansion of a macro function name. For the same syntactic reason, it is permitted to take the address of a library function even if it is also defined as a macro.185) The use of #undef to remove any macro definition will also ensure that an actual function is referred to.

So according to this, if I want to make sure get will be a function inside my code I have to access to the <stdio.h> header file and enclose (I don't know if the function name or the macro name) in parenthesis or #undef the macro?

I mean this: int (getc)(FILE *stream); (or this? #define (getc)(...) ) or this: #undef getc

But all this in the <stdio.h> file?

Or is inside my code?

Can you give me an example on how to make sure to use getc as a function in C?


Solution

  • As explained in the quoted paragraph, there is a simple way to prevent macro expansion of function-like macros defined in standard library headers: use the identifier not followed by an ( introducing the argument list, eg: using parentheses around the identifier.

    For example:

    #include <stdio.h>
    
    // copy stdin to stdout bypassing macro expansion
    int main(void) {
        int c;
        while ((c = (getc)(stdin)) != EOF) {
            (putc)(c, stdout);
        }
        return 0;
    }
    

    Alternatively, you can undefine the macros before using the functions in your code. This will ensure that the preprocessor leaves the getc and putc identifiers unchanged and the compiler will use their function declarations from <stdio.h>.

    #include <stdio.h>
    
    // copy stdin to stdout preventing macro expansion
    
    #undef getc
    #undef putc
    #undef fgetc
    #undef fputc
    
    int main(void) {
        int c;
        while ((c = getc(stdin)) != EOF) {
            putc(c, stdout);
        }
        return 0;
    }
    

    A simpler approach is to use the functions fgetc and fputc which are guaranteed to behave as functions even if they may also be implemented as macros for performance:

    #include <stdio.h>
    
    // copy stdin to stdout using the functions
    int main(void) {
        int c;
        while ((c = fgetc(stdin)) != EOF) {
            fputc(c, stdout);
        }
        return 0;
    }
    

    In any case, do not attempt to modify the standard headers, which should be read-only on modern systems.

    Note also that the macro versions of the standard functions defined in <stdio.h> are only allowed to multi-evaluate the stream argument (the one corresponding to the FILE *), not the character argument. Ancient systems where this was not guaranteed do not conform to any of the C Standards, and are completely obsolete. See for example the specification of the putc function in the ISO C90 document:

    7.9.7.8 The putc function

    Synopsis

    #include <stdio.h>
    int putc(int c, FILE *stream);
    

    Description

    The putc function is equivalent to fputc except that if it is implemented as a macro, it may evaluate stream more than once, so the argument should never be an expression with side effects.

    The final word is: don't worry about this and use the macros or the functions without hesitation, but don't pass an expression with side effects as the stream argument, which would be very cumbersome and error prone anyway.