为什么一个expression式而不是一个常量,在C for-loop的条件?
在许多编程竞赛中,我看到人们为这种for
回旋”编写了这种types
for(i = 0; i < (1 << 7); i++)
除非我失去了一些东西,那就是一样的
for(i = 0; i < 128; i++)
为什么要使用(1 << 7)
版本?
是不是每次都计算不必要的开销?
是的,他们在行为上是等同的。
那么为什么人们使用(1 << 7)版本呢?
我想,他们用它来logging它是2的力量。
每次计算条件必须是开销! 我无法find背后的原因!
不是真的,任何普通的编译器都会用128
来代替1 << 7
,所以两个循环都会有相同的性能。
(C11,6.6p2)“常量expression式可以在翻译过程中而不是在运行时进行评估,因此可以用在常量可能存在的任何地方。
让我们把这些选项中的每一个翻译成简单的英文:
for(i = 0; i < (1 << 7); i++) // For every possible combination of 7 bits for(i = 0; i < 128; i++) // For every number between 0 and 127
运行时行为在两种情况下都应该是相同的。
事实上,假设一个体面的编译器,即使汇编代码应该是相同的。
所以第一个选项基本上是用来“发表声明”。
你可以使用第二个选项,并在上面添加注释。
1 << 7
是一个常量expression式,编译器将其视为128
,运行时没有开销。
没有循环体,很难说为什么作者使用它。 可能是一个循环迭代7位相关的东西,但这只是我的猜测。
那么为什么人们使用(1 << 7)版本呢?
它是一种文档forms,它不是一个神奇的数字,而是2^7
( 二至七 ),这对编写代码的人来说是有意义的。 一个现代的优化编译器应该为这两个例子生成完全相同的代码,所以使用这个表单是没有代价的,并且有添加上下文的好处。
使用godbolt我们可以证实这确实是这样的,至less对于几个版本的gcc
, clang
和icc
。 使用一个带有副作用的简单例子来确保代码不会被完全优化掉:
#include <stdio.h> void forLoopShift() { for(int i = 0; i < (1 << 7); i++) { printf("%d ", i ) ; } } void forLoopNoShift() { for(int i = 0; i < 128; i++) { printf("%d ", i ) ; } }
对于代码的相关部分,我们可以看到他们都生成了以下生活 :
cmpl $128, %ebx
我们拥有的是C11标准草案中定义的整型常量expression式 。6.6 常量expression式 ,它表示:
整数常量expression式117)应具有整数types,并且只应具有整数常量,枚举常量,字符常量,sizeofexpression式的结果为整数常量的操作数,[…]
和:
常数expression式不得包含赋值,增量,减量,函数调用或逗号运算符,除非它们包含在未评估的子expression式中。
我们可以看到,在翻译过程中可以对一个常量expression式进行评估:
常量expression式可以在翻译过程中而不是在运行时进行评估,因此可以用在常量可能存在的任何地方。
for(i = 0; i <(1 << 7); i ++)
和
for(i = 0; i <128; i ++)
(i = 0; i <(1 << 7); i ++)在循环中使用
for(int k = 0; k < 8; k++) { for(int i = 0; i < (1 << k); i++) { //your code } }
现在它在内环上限即(1 << k)随着2个运行时间的功率而变化。 但是,如果你的algorithm需要这个逻辑,它是适用的。
编译器为这两种情况输出相同的代码。 你可能要根据上下文使用不同的forms。
- 您可以使用
NUM_STEPS
或NUM_ELEMENTS_IN_NETWORK_PACKET
作为algorithm中的一个常量部分或devise选项。 - 或者你可以写
128
,明确它是128
,一个常数。 - 或者如果你参加一个比赛,并且testing中说“跑了2 7次”,那就写
1 << 7
。
或者,你可以炫耀,你知道一点操作!
在我看来,编程就像为两个人写信,编译器和需要阅读的人。 你的意思应该清楚。
由于两个操作数都是常量,所以由预处理器评估。
但是如果你打算使用数字而不是位移不应该是0x0100?