Kami Kaze
Kami Kaze

Reputation: 2080

Why does preprocessor macros ignore statements in parentheses

Following to my (duplicate) question ( and as suggested by StoryTeller)

Why do preprocessor macros ignore function names in parenthesis?

#include  <stdio.h>
#include <stdlib.h>
#define abs(x) ((x))

int main(void)
{
    printf("%d\n", abs(-1)); // output: -1
    printf("%d\n", (abs)(-1)); // output: 1
    return 0;
}

Is this defined in the standard?

Upvotes: 2

Views: 398

Answers (2)

Why? Because the C preprocessor is agnostic of the C language!

The C preprocessor has been conceived as a pure text replacement tool, barely powerful enough to provide C programmers with a simple means to

  • define constants that are pasted literally into the program

  • literally paste the contents of files (i.e. headers) into the program

  • provide a simple means of creating simple templates of code, that are again pasted literally into the program

None of this includes any awareness of the C syntax! It's pure text manipulation.
On the contrary, you can do stuff like this

#define CLASS(name, base) typedef struct name name;\
    struct name {\
        base super;
#define END_CLASS };

CLASS(foo, bar)
    int baz;
END_CLASS

Note how the preprocessor will generate an unmatched { token when expanding the CLASS macro, and an unmatched } token when expanding the END_CLASS macro.

Thus, the syntax for using a macro has nothing to do with the syntax for calling a function. You can call a C function in many different ways (foo(), foo (), (foo)(), (**(*foo) ) (), etc.) because the C language handles expressions of functions, and defines what happens when you place its name in parentheses (it's implicitly converted to a pointer to it), dereference it, or call it. All this does not exist in the preprocessor, so there is exactly one way to use a macro: foo() with no extra space between the name and the (.


Side note:

The C preprocessor is so agnostic of the C language, that it's not just used with C, it's also commonly used with languages like FORTRAN. (Which is a horrible fit, actually. Nevertheless, the C preprocessor is the best, commonly supported thing that can be used to make FORTRAN a bit less painful.)

Upvotes: 0

The preprocessor's macro substitution is specified as follows:

6.10.3 Macro replacement / p10 - Emphasis mine:

A preprocessing directive of the form

# define identifier lparen identifier-list<opt> ) replacement-list new-line
# define identifier lparen ... ) replacement-list new-line
# define identifier lparen identifier-list , ... ) replacement-list new-line

defines a function-like macro with parameters, whose use is similar syntactically to a function call. The parameters are specified by the optional list of identifiers, whose scope extends from their declaration in the identifier list until the new-line character that terminates the #define preprocessing directive. Each subsequent instance of the function-like macro name followed by a ( as the next preprocessing token introduces the sequence of preprocessing tokens that is replaced by the replacement list in the definition (an invocation of the macro). The replaced sequence of preprocessing tokens is terminated by the matching ) preprocessing token, skipping intervening matched pairs of left and right parenthesis preprocessing tokens. Within the sequence of preprocessing tokens making up an invocation of a function-like macro, new-line is considered a normal white-space character.

It says it right there in bold. For substitution to occur, the very next preprocessing token after the macro name must be a (. When it's a ), such as when the macro is in parentheses, no substitution can occur.

So that leaves us only with the function name in parentheses, an expression that is identical to the function's designator.

Upvotes: 9

Related Questions