为什么表达式而不是常量,在C for循环的条件中?

har*_*mas 57 c expression for-loop coding-style constantfolding

在许多编程比赛中,我看到有人写这种类型的for-loop

for(i = 0; i < (1 << 7); i++)
Run Code Online (Sandbox Code Playgroud)

除非我遗漏了什么,否则就是这样

for(i = 0; i < 128; i++)
Run Code Online (Sandbox Code Playgroud)

为何使用该(1 << 7)版本?
每次不必要的开销是不是计算条件?

oua*_*uah 76

是的,它们在行为上是等同的.

那为什么人们会使用(1 << 7)版本?

我想,他们用它来证明它是2的力量.

每次计算条件必须是开销!我无法找到这背后的原因!

不是真的,任何正常的编译器将取代1 << 7128等两个环路将具有相同的性能.

(C11,6.6p2)"可以在翻译期间而不是运行时评估常量表达式,因此可以在常量可能的任何位置使用."

  • 请注意,C语言*要求*编译器*能够*在编译时评估常量表达式,因为它们可用于必须知道实际值的各种上下文中,以确定约束违规(例如否定数组大小或无效的位域宽度)或表达式类型(由于表达式是否为空指针常量取决于值,结果可以通过`?:`运算符以非常强大的方式传播).因此没有充分的理由不在编译时完全评估所有常量表达式. (20认同)

bar*_*nos 31

让我们将这些选项中的每一个翻译成简单的英语:

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
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,运行时行为应该相同.

实际上,假设一个不错的编译器,即使汇编代码也应该是相同的.

因此,第一个选项主要用于"发表声明".

您也可以使用第二个选项并在上面添加注释.


Yu *_*Hao 19

1 << 7是一个常量表达式,编译器将其视为128,运行时没有开销.

没有循环体,很难说为什么作者使用它.可能它是一个循环,迭代与7位相关的东西,但这只是我的猜测.

  • 谢谢..我正在看的for循环正在使用7位!谢谢! (3认同)
  • 是啊.如果它有助于理解,我会这样做.我称他们为"吟游者":) (2认同)

Sha*_*our 14

那为什么人们会使用(1 << 7)版本?

它是一种文档形式,它不是一个神奇的数字,但是2^7(两到七次幂)对于编写代码的人来说都是有意义的.现代优化编译器应该为这两个示例生成完全相同的代码,因此使用此表单没有任何成本,并且添加上下文有好处.

使用godbolt我们可以验证这一点确实是这样的,至少在几个版本gcc,clangicc.使用带有副作用的简单示例来确保代码未完全优化:

#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 ) ;
  }
}
Run Code Online (Sandbox Code Playgroud)

对于代码的相关部分,我们可以看到它们都生成以下内容直播:

cmpl    $128, %ebx
Run Code Online (Sandbox Code Playgroud)

我们所拥有的是一个整数常量表达式,如草案C11标准部分中所定义的6.6 常量表达式所示:

整数常量表达式117)应具有整数类型,并且只能具有整数常量的操作数,枚举常量,字符常量,结果为整数常量的sizeof表达式,[...]

和:

常量表达式不应包含赋值,递增,递减,函数调用或逗号运算符,除非它们包含在未评估的子表达式中.15)

我们可以看到在翻译期间允许评估常量表达式:

可以在转换期间而不是运行时期间评估常量表达式,并且因此可以在常量可以在任何地方使用.

  • 对于Godbolt链接+1 - 这太棒了. (2认同)

Ani*_*Pal 5

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
    }

}
Run Code Online (Sandbox Code Playgroud)

现在它处于内循环上限,即(1 << k)随着2运行时的功率而变化.但如果您的算法需要此逻辑,则适用.

  • `(1 << k)`是[循环不变量](https://en.wikipedia.org/wiki/Loop_invariant),因此编译器*应该*每***循环迭代只计算一次.实际上,它可以执行[归纳变量消除](https://en.wikipedia.org/wiki/Induction_variable),将其转换为`for(int j = 1; j <= 128; j << = 1) (i = 0; i <j; i ++){...}` (2认同)