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

问题描述 投票:0回答:4

我目前正在为微控制器编写代码;由于ATmega128没有硬件乘法器或除法器,这些操作必须在软件中完成,并且它们会占用相当多的周期。

但是,为了代码的可移植性和易用性,我不希望将预先计算的值硬编码到我的代码中。例如,我有许多依赖于系统时钟频率的任务。目前我的运行频率为 16 MHz,但我是否应该选择降低该频率,例如降低电池应用的功耗?

我想更改一行代码而不是多行。

那么,C 预处理器是否可以计算算术表达式,然后将结果“粘贴”到我的代码中,而不是将原始表达式“粘贴”到代码中?如果是这样,我该如何去做呢?他们的编译器选项是我需要考虑的吗?

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

c compiler-construction compiler-optimization c-preprocessor
4个回答
8
投票

这是一个问题:

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

这是另一个:

  • Q2。 C预处理器可以计算算术表达式然后“粘贴”吗 结果到我的代码中而不是“粘贴”原始表达式到代码中?

问题 1 的答案是 。 Q2 的答案是否定的。这两个事实都可以说明 带有以下文件:

foo.c

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

如果我们通过

cpp foo.c
或等效的
gcc -E foo.c
将其传递给 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)
,以及 not 及其算术值 其定义,
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

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


4
投票

是的,您可以使用预处理器进行算术运算,但这需要大量工作。阅读本页,展示如何创建增量计数器和 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
投票

它可以,但没有必要:您实际上不需要涉及预处理器,除非您实际上想生成以某种方式涉及数字的新标识符(例如像

func1
func2
之类的东西)。

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


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

这只是您的一个数据点。

© www.soinside.com 2019 - 2024. All rights reserved.