3

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

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

+1

如果表达式是常量,它们是不是被编译器优化掉了呢? –

+0

http://stackoverflow.com/a/1560385/995714 –

+0

的副本嗯,有预处理器库,可能有一个给你,它主要取决于你定位的C语言版本,例如C99和上面有P99 http://p99.gforge.inria.fr/ – user2485710

回答

3

它可以但不是必要的:除非您真的想生成以某种方式涉及数字的新标识符(例如func1,func2之类的东西),否则实际上并不需要涉及预处理器。

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

+0

好的,无论哪种方式,我的表达式将在编译时被单个常量替换(假设我的'#define'中没有变量)? – audiFanatic

+0

为了以防万一,我会仔细检查装配。并非所有嵌入式C编译器都遵循C规范;有些偏离很大。 – nneonneo

+2

@Leushenko在第二种情况下,甚至可以使用枚举而不是定义,这样程序更容易调试,而且根本不需要预处理器。 –

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

这对你只是一个数据点。

+2

正如它应该那样 - 预处理器的使用只是文本替换。尽管如此,编译器本身通常会在编译时用常量替换表达式。 –

6

这是一个问题:

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

这是另一种:

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

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

foo。Ç

#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)与其定义,9的算术值 。

C预处理器除#define之外是否还有其他指令,它具有第二个 效果?第

但是,这并不当然意味着的int nine定义必须包括一个 运行时间计算,因为编译器将在编译时几乎肯定评价 算术表达式((1 + 2) * 3)与 不断更换9

我们可以通过检查 编译目标文件来了解编译器如何翻译源文件。大多数工具链将提供类似GNU binutils objdump的协助。如果我编译foo.c用gcc:

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

注意预处理器的运算能力被限制整数算术

+0

查看'-S' gcc选项,它可以生成汇编代码。你可能会觉得更容易解释。 – rici

+1

为了记录,预处理器*是* [[完全能够用宏发射算术'9]],但支持代码需要做的很荒唐。 – Leushenko

+0

@Leushenko strewth!:) –

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 

司和减法可以建在一个类似的方式。

相关问题