Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to understand the C preprocessor

Why do these blocks of code yield different results?

Some common code:

#define PART1PART2 works
#define STRINGAFY0(s) #s
#define STRINGAFY1(s) STRINGAFY0(s)

case 1:

#define GLUE(a,b,c) a##b##c  
STRINGAFY1(GLUE(PART1,PART2,*))
//yields
"PART1PART2*"

case 2:

#define GLUE(a,b) a##b##*
STRINGAFY1(GLUE(PART1,PART2))
//yields
"works*"

case 3:

#define GLUE(a,b) a##b
STRINGAFY1(GLUE(PART1,PART2*))
//yields
"PART1PART2*"

I am using MSVC++ from VS.net 2005 sp1

Edit: it is currently my belief that the preprocessor works like this when expanding macros: Step 1: - take the body - remove any whitespace around ## operators - parse the string, in the case that an identifier is found that matches the name of a parameter: -if it is next to a ## operator, replace the identifier with the literal value of the parameter (i.e. the string passed in) -if it is NOT next to a ## operator, run this whole explanation process on the value of the parameter first, then replace the identifier with that result. (ignoring the stringafy single '#' case atm) -remove all ## operators

Step 2: - take that resultant string and parse it for any macros

now, from that I believe that all 3 cases should produce the exact same resultant string:

PART1PART2*

and hence after step 2, should result in

works*

but at very least should result in the same thing.

like image 612
matt Avatar asked Jul 19 '10 07:07

matt


2 Answers

cases 1 and 2 have no defined behavior since your are tempting to paste a * into one preprocessor token. According to the association rules of your preprocessor this either tries to glue together the tokens PART1PART2 (or just PART2) and *. In your case this probably fails silently, which is one of the possible outcomes when things are undefined. The token PART1PART2 followed by * will then not be considered for macro expansion again. Stringfication then produces the result you see.

My gcc behaves differently on your examples:

/usr/bin/gcc -O0 -g -std=c89 -pedantic   -E test-prepro.c
test-prepro.c:16:1: error: pasting "PART1PART2" and "*" does not give a valid preprocessing token
"works*"

So to summarize your case 1 has two problems.

  • Pasting two tokens that don't result in a valid preprocessor token.
  • evaluation order of the ## operator

In case 3, your compiler is giving the wrong result. It should

  1. evaluate the arguments to STRINGAFY1
  2. to do that it has to expand GLUE
  3. GLUE results in PART1PART2*
  4. which must be expanded again
  5. the result is works*
  6. which then is passed to STRINGAFY1
like image 80
Jens Gustedt Avatar answered Oct 10 '22 10:10

Jens Gustedt


It's doing exactly what you are telling it to do. The first and second take the symbol names passed in and paste them together into a new symbol. The third takes 2 symbols and pastes them, then you are placing the * in the string yourself (which will eventually evaluate into something else.)

What exactly is the question with the results? What did you expect to get? It all seems to be working as I would expect it to.

Then of course is the question of why are you playing with the dark arts of symbol munging like this anyways? :)

like image 33
Michael Dorgan Avatar answered Oct 10 '22 09:10

Michael Dorgan