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

brccelvz  于 2023-10-16  发布在  其他
关注(0)|答案(4)|浏览(102)

我目前正在为一个微控制器编写代码;由于ATmega128没有硬件乘法器或除法器,这些操作必须在软件中完成,并且它们占用相当多的周期。
但是,为了代码的可移植性和易用性,我不希望将预先计算的值硬编码到代码中。例如,我有许多依赖于系统时钟频率的任务。目前我的运行频率为16 MHz,但我是否应该选择降低该频率,例如降低电池应用的功耗?
我想修改一行代码而不是很多行。
那么,C预处理器是否可以计算算术表达式,然后将结果“粘贴”到我的代码中,而不是将原始表达式“粘贴”到代码中?如果是的话,我该怎么做呢?他们的编译器选项和诸如此类的东西,我需要考虑?
注意:我想要计算的值是常数值,所以我看不出为什么这不是一个特性。

uubf1zoe

uubf1zoe1#

这是一个问题:

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

这是另一个:

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

Q1的答案是“是”。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.

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

注意,预处理器的算术能力仅限于 integer 算术

b4qexyjb

b4qexyjb2#

是的,你可以用预处理器做算术运算,但这需要做很多工作。阅读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

除法和减法可以用类似的方法来构建。

11dmarpk

11dmarpk3#

它可以,但不必要:你实际上不需要涉及预处理器,除非你真的想生成新的标识符,以某种方式涉及数字(例如,例如func1func2)。
1 + 2 * 3这样的表达式,其中所有元素都是编译时常量整数值,将在编译时被单个结果替换(这或多或少是C标准所要求的,所以它不是“真正的”优化)。所以只有#define常量,你需要命名一个可以从一个地方改变的值,确保表达式不涉及任何运行时变量,除非你的编译器故意妨碍你,否则你不应该担心运行时操作。

sf6xfgos

sf6xfgos4#

我使用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));
}

这对你来说只是一个数据点。

相关问题