Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does C preprocessor actually work?

Tags:

c

macros

I made the code snippet simpler to explain

// Example 1

#define sum2(a, b) (a + b)
#define sum3(a, b, c) (sum2(a, sum2(b, c)))

sum3(1, 2, 3)    // will be expanded to ((1 + (2 + 3)))


// Example 2

#define score student_exam_score
#define print_score(student_exam_score) printf("%d\n", score)
#undef  score

print_score(80); // will be expanded to printf("%d\n", score);
                 // but not printf("%d\n", 80); that I expect

The first one is intuitive, and that kinds of codes exists in several places such as finding the maximum or minimum number. However, I want to use that technique to make my code clean and easy to read, so I replace the some words in a macro with a shorter and more meaningful name.

AFAIK, C preprocessor runs only once per compilation unit and only performs string replacement, but why print_score cannot be expanded to printf("%d\n", 80);?

This is the replacement procedure I guess:

#define score student_exam_score
#define print_score(student_exam_score) printf("%d\n", score)
#undef  score

print_score(80);

//  --> 

#define score student_exam_score  // runs this first
#define print_score(student_exam_score) printf("%d\n", student_exam_score)  // changed
#undef  score

print_score(80);

//  --> 

#define score student_exam_score
#define print_score(student_exam_score) printf("%d\n", student_exam_score)  // then this
#undef  score

printf("%d\n", 80);  // changed
like image 331
Kevin Dong Avatar asked Oct 19 '22 09:10

Kevin Dong


1 Answers

It's a sequencing issue. First the macros are defined, and score is undefined before it is ever used. Then, when print_score is expanded, it first substitutes all instances of student_exam_score, of which there are none. It then rescans the result, looking for further macros to expand, but there are none since score has been undefined and is no longer available.

Even if you moved #undef score down below the reference to print_score, it still wouldn't work since parameter substitution only happens once (score would be expanded but student_exam_score would not).

Note that score is not substituted into the body of print_score at the time is it defined. Substitution only happens when the macro is instantiated, which is why #undef score results in the score macro having no effect whatsoever.

These examples will make it clearer. First, consider the following:

#define foo bar
#define baz(bar) (foo)
baz(123)

This is expanded as follows:

    baz(123)
->  (foo)
->  (bar)

Expansion stops here. Parameter substitution was done before foo was expanded, and does not happen again.

Now consider the following:

#define foo bar
#define baz(bar) (foo)
#undef foo
baz(123)

This is expanded as follows:

    baz(123)
->  (foo)

Expansion stops here because foo is no longer defined. Its earlier definition had no effect on the definition of baz, because macro substitution does not happen when macros are defined. It only happens when they are expanded.

like image 61
Tom Karzes Avatar answered Oct 21 '22 05:10

Tom Karzes