This is one question:
- Q1. Can the C preprocessor perform arithmetic?
And this is another:
- Q2. Can the C preprocessor compute arithmetic expressions and then "paste"
the result into my code rather than "pasting" the original expression into the code?
The answer to Q1 is yes. The answer to Q2 is No. Both facts can be illustrated
with the following file:
foo.c
#define EXPR ((1 + 2) * 3)
#if EXPR == 9
int nine = EXPR;
#else
int not_nine = EXPR;
#endif
If we pass this to the C preprocessor, either by cpp foo.c
or equivalently gcc -E foo.c
, we see output like:
# 1 "foo.c"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 30 "/usr/include/stdc-predef.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/predefs.h" 1 3 4
# 31 "/usr/include/stdc-predef.h" 2 3 4
# 1 "<command-line>" 2
# 1 "foo.c"
int nine = ((1 + 2) * 3);
The fact that the preprocessor retains the line defining int nine
and
has dropped the line defining not_nine
shows us that it has correctly performed
the arithmetic required to evaluate #if EXPR == 9
.
The fact that the preprocessed text of the definition is int nine = ((1 + 2) * 3);
shows us that the #define
directive causes the preprocessor to replace
EXPR
with its definition ((1 + 2) * 3)
, and not with the arithmetic value
of its definition, 9
.
Does the C preprocessor have any directive besides #define
which has the second
effect? No.
But this does not of course imply that the definition of int nine
must entail a run-time calculation, because the compiler will almost certainly evaluate the arithmetic expression ((1 + 2) * 3)
at compile time and replace it with the constant 9
.
We can see how the compiler has translated the source file by examining the
compiled object file. Most toolchains will provide something like GNU binutils' objdump
to assist with this. If I compile foo.c
with GCC:
gcc -c -o foo.o foo.c
and then invoke:
objdump -s foo.o
to see the full contents of foo.o
, I get:
foo.o: file format elf64-x86-64
Contents of section .data:
0000 09000000 ....
Contents of section .comment:
0000 00474343 3a202855 62756e74 752f4c69 .GCC: (Ubuntu/Li
0010 6e61726f 20342e38 2e312d31 30756275 naro 4.8.1-10ubu
0020 6e747539 2920342e 382e3100 ntu9) 4.8.1.
And there is the hoped-for 9
hard-coded in the .data
section.
Note that the preprocessor's arithmetic capabilities are restricted to integer arithmetic