有人提到,编写良好的C编译器应该在编译时执行移位操作符(即不是运行时);例如,在此代码中,左移 - <&lt ;.谁能证明这个有效性呢?
代码:
constant unsigned int elements = length/8 + (length % y > 0 ? 1 : 0);
unsigned char bit_arr[elements];
Psuedo-Code:
bit_arr[i] |= (1 << j); // Set
bit_arr[i] &= ~(1 << j); // Unset
if( bit_arr[i] & (1 << j) ) // Test
答案 0 :(得分:4)
你究竟在问什么?你的意思是“编译器会自己进行转换”吗?如果那就是你要问的答案是“它取决于”:)。如果移位的数字和移位的大小都是编译时常量,编译器几乎肯定会进行移位(尽管它没有)。否则它将生成将执行移位的低级代码(通常是单个机器指令)。
答案 1 :(得分:3)
符合C标准的任何编译器都将提供移位运算符 <<
和>>
,如standard中第6.5.7节所述。它们可以在没有整数表达式问题的情况下工作(不管它们是否恒定) 1 )。
但是,在负整数上使用它们时要小心,因为符号传播不是标准规定的(实际上,带左负操作数的左移是未定义的行为,而右移是实现定义的)。此外,带有符号整数的溢出行为未定义。
<小时/>
秒>
每个操作数都应具有整数类型。
所有这些东西都不再相关,因为问题完全改变了。
对于它的价值,当所有操作数都已知(并且启用了优化)时,编译器可以并且通常将在编译时执行计算。
答案 2 :(得分:3)
编译器没有义务将您的程序 verbatim (或您期望的方式)翻译成汇编语言或机器语言。只要行为相同,编译器就可以自由翻译您的程序,在语言标准的范围内。如果您的程序调用未定义的行为,特定于平台的行为或编译器定义的行为,则所有投注都将关闭。
编译器通常不会使用常量对移位进行编码。他们可以在编译期间计算值并加载值。他们可能选择乘法或除法而不是转移。
更智能的编译器可以评估您的表达式并使用更有效的公式来产生答案。另一方面,编译器也可以消除不使用答案的转换。
我建议专注于程序的正确性和健壮性,而不是担心编译器如何优化代码。在程序正常运行并且运行正常后,如果您有额外的时间或程序的大小或执行速度不令人满意,请进行优化。
答案 3 :(得分:1)
编译器实现的移位的一个示例是以下示例(具有未定义的行为):
#include <stdio.h>
int main(void) {
const int i = 32;
printf("%d %d\n", 1 << 32, 1 << i);
return 0;
}
在Cygwin上使用gcc 4.3.4时,gcc foo.c; ./a
会提供0 1
而不是预期的0 0
。 ( gcc 会发出编译器警告,而-O3
会产生预期结果,并发出两条警告。)因此,请参阅SO {{ 3}}
答案 4 :(得分:0)
每个C编译器都必须实现移位运算符,因为它们是语言的一部分。对j没有任何限制。
编辑:由于您完全改变了问题:是的,要进行优化,当然j需要是编译时常量,否则,编译器应该如何知道它在编译时的值,但是,在优化代码时,首先要做的事情就是使用常量操作数优化算术指令。实际上,即使没有激活优化级别,VC和gcc也会这样做,我认为,因为它很明显。
答案 5 :(得分:0)
如果编译器可以在编译时确定操作数的值,并且它在适当的优化级别调用,它可能会在编译时执行操作。
将变量标记为const
可能有助于编译器执行此确定。在某些情况下,编译器也可以通过数据流分析(例如:初始化为常量且永不修改的局部变量,......)来自行完成。
答案 6 :(得分:0)
是否存在对j的依赖 常数与变量?
当然有。编译器如何在编译时计算结果,该结果取决于直到运行时才知道的操作数?
无论是谁'提到',对你来说都是错误的,或者没有说出你理解他们的意思。