C预处理器可以执行算术吗?如果是,如何执行算术?

时间:2014-04-26 04:12:31

标签: c compiler-construction compiler-optimization c-preprocessor

我目前正在为微控制器编写代码;由于ATMega128没有硬件乘法器或分频器,因此这些操作必须在软件中完成,并且它们占用相当多的周期。但是,为了代码的可移植性和易用性,我不希望将预先计算的值硬编码到我的代码中。例如,我有许多依赖于系统时钟频率的任务。目前我'以16MHz运行,但我应该选择降低它,比如为了降低电池应用的功耗,我想改变一行代码而不是很多代码。

所以说,C预处理器可以计算算术表达式然后"粘贴"结果进入我的代码,而不是"粘贴"原始表达式进入代码?如果是这样,我该怎么做呢?他们的编译器选项和我需要考虑的是什么?

注意:我想要计算的值是常量值,所以我认为没有理由不这样做。

4 个答案:

答案 0 :(得分:6)

这是一个问题:

  • Q1。 C预处理器可以执行算术吗?

这是另一个:

  • Q2。 C预处理器可以计算算术表达式,然后粘贴" 结果进入我的代码,而不是"粘贴"原始表达式进入代码?

Q1的答案是肯定的。 Q2的答案是否定的。两个事实都可以说明 使用以下文件:

<强> foo.c的

#define EXPR ((1 + 2) * 3)
#if EXPR == 9
int nine = EXPR;
#else
int not_nine = EXPR;
#endif

如果我们将它传递给C预处理器,可以通过cpp foo.c或 等价gcc -E foo.c,我们看到输出如:

# 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);

预处理器保留定义int nine和。的行的事实 已经删除了定义not_nine的行,向我们显示它已正确执行 评估#if EXPR == 9所需的算术。

定义的预处理文本为int nine = ((1 + 2) * 3); 告诉我们#define指令导致预处理器被替换 EXPR的定义为((1 + 2) * 3),而,其算术值为{0} 其定义9

除了#define之外,C预处理器是否还有其他指令 影响?否。

但这当然不意味着int nine的定义必然包含一个问题 运行时计算,因为编译器几乎肯定会进行评估 在编译时使用算术表达式((1 + 2) * 3)并将其替换为 常数9

通过检查,我们可以看到编译器如何翻译源文件 编译目标文件。大多数工具链都会提供类似GNU binutils的东西 objdump为此提供帮助。如果我使用gcc:

编译foo.c
gcc -c -o foo.o foo.c

然后调用:

objdump -s foo.o

要查看foo.o的完整内容,我得到:

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.

9部分中存在希望的.data硬编码。

注意预处理器的算术功能仅限于整数算术

答案 1 :(得分:3)

它可以但不必要:除非你真的想要以某种方式生成涉及数字的新标识符(例如func1func2等内容),否则实际上不需要涉及预处理器

1 + 2 * 3这样的表达式,其中所有元素都是编译时常量整数值,将在编译时被单个结果替换(C标准或多或少要求这样,所以它不是“真的” “优化)。所以只需#define常量,你需要命名一个可以从一个地方改变的值,确保表达式不涉及任何运行时变量,除非你的编译器有意阻碍你的方式,否则你应该没有运行时操作担心。

答案 2 :(得分:1)

是的,您可以使用预处理器进行算术运算,但需要做很多工作。阅读this page here,显示如何创建增量计数器和while循环。所以你可以创建另外的:

#define ADD_PRED(x, y) y
#define ADD_OP(x, y) INC(x), DEC(y)
#define ADD(x, y) WHILE(ADD_PRED, ADD_OP, x, y)

EVAL(ADD(1, 2)) // Expands to 3

重新使用ADD宏,然后您可以创建一个MUL宏,如下所示:

#define MUL_PRED(r, x, y) y
#define MUL_OP(r, x, y) ADD(r, x), x, DEC(y)
#define MUL_FINAL(r, x, y) r
#define MUL(x, y) MUL_FINAL(WHILE(MUL_PRED, MUL_OP, 0, x, y))

EVAL(MUL(2, 3)) // Expands to 6

分割和减法可以用类似的方式构建。

答案 3 :(得分:0)

我使用gcc -E编译了包含以下行的文件。

#define MUL(A, B) ((A)*(B))

#define CONST_A 10
#define CONST_B 20

int foo()
{
   return MUL(CONST_A, CONST_B);
}

输出结果为:

# 1 "test-96.c"
# 1 "<command-line>"
# 1 "test-96.c"





int foo()
{
   return ((10)*(20));
}

这只是您的一个数据点。