Understanding Macros in C

Why is the output from the following code set to 5?

#include<stdio.h> #define A -B #define B -C #define C 5 int main() { printf("The value of A is %d\n", A); return 0; } 
+7
c ++ c c-preprocessor
source share
5 answers

This is a tricky question because it is a stress test for the compiler preprocessor.

Depending on whether the preprocessor is an integrated phase of the compiler or a separate program that passes its output to the compiler via a file or a channel, in which case, is it enough to not insert the marker incorrectly, you can get the expected result: 5 or you may get a compilation error.

After the pre-processed contents of stdio.h processed, the source code extends to:

 int main() { printf("The value of A is %d\n", --5); return 0; } 

But two are separate tokens, so depending on whether the preprocessor separates them at its output or not, you can get a program that outputs 5 or one that does not compile, because -- it cannot be applied to the letter 5 .

Both the gcc and clang preprocessors behave correctly and share - with extra space to prevent marker insertion when they produce the preprocessor using the -E command line -E . They output this as pre-processed source code after the extension <stdio.h> :

 int main() { printf("The value of A is %d\n", - -5); return 0; } 

Try your own compiler to see how it extends the source code. It seems that Visual Studio 2013 and 2015 did not pass the test and rejected the program with an error.

To make everything clear, I am not saying that the behavior of a program should depend on the architecture of the compiler. I was hoping that at least one common C compiler would not apply this conformance test. I am not surprised that MS Visual Studio 2013 and 2015 did not pass this test.

Additional space is needed only in the text output of the preprocessor. It does not matter whether Visual Studio uses several separate phases or not, the source program is absolutely correct and their compilation failure is an error.

+8
source share

No need to compile this code, just use gcc -E on it (preprocessor) and see what happens:

 <lots of output expanding stdio.h> ... int main() { printf("The value of A is %d\n", - -5); return 0; } 

Obviously, the result is 5 (which could be guessed by looking at the nested macros, but a little preprocessor test won't hurt).

(Other answers noted that some compilers may handle preprocessing minus signs, which will lead to a compiler error. gcc does a great job.)

+5
source share

The question doesn't really make sense, but I decided to do it anyway.

Visual Studio 2013 and 2015: error C2105: '--' needs l-value

The reason is the following line:

 printf("The value of A is %d\n", A); 

first converted to (A becomes -B):

 printf("The value of A is %d\n", -B); 

then in (B becomes -C);

 printf("The value of A is %d\n", --C); 

and then in (C becomes 5):

 printf("The value of A is %d\n", --5); 

And since 5 is not an l-value, you cannot reduce it, hence the error. It seems quite logical, knowing that the preprocessor will simply perform a simple string replacement.

+3
source share

This is a great example of how not to use a preprocessor. To avoid parentheses, use parentheses (not only in this case)

 #define A (-B) #define B (-C) #define C (5) 
0
source share

each preprocessing directive #define will insert into the preprocessor environment a variable assigned to a value compiled from a list of preprocessing directives.

 {A -> -B; B->-C; C->5} 

is the environment at the time of assessment A Now, making the process of estimating A , we have

  A -> -B (the identifier `A` is transformed in the stream of preprocessing tokens `-B`) -B -> --C --C -> --5 -> 5 

and this Prosser algorithm will no longer be evaluated as it has no more identifiers.

So, reducing

  A->5 

stream A converted to stream 5 , and this one will be converted from pre-processing tokens to C-tokens and sent further to the C compiler.

0
source share

All Articles