web-dev-qa-db-ja.com

C forループの条件付きで、定数ではなく式を使用するのはなぜですか?

多くのプログラミングコンテストで、人々がこのタイプのfor- loopを書くのを見てきました。

for(i = 0; i < (1 << 7); i++)

私が何かを逃していない限り、それはと同じです

for(i = 0; i < 128; i++)

なぜ(1 << 7)バージョンを使用するのですか?
不要なオーバーヘッドが発生するたびに条件を計算していませんか?

57
harrythomas

はい、動作は同等です。

では、なぜ人々は(1 << 7)バージョンを使用するのでしょうか。

私は、彼らはそれが2の累乗であることを文書化するためにそれを使用していると思います。

毎回条件を計算するのはオーバーヘッドでなければなりません!この背後にある理由を見つけることができません!

実際には、通常のコンパイラは1 << 7128に置き換えるため、両方のループのパフォーマンスは同じになります。

(C11、6.6p2)「定数式は、実行時ではなく変換中に評価できるため、定数が存在する可能性のある任意の場所で使用できます。」

76
ouah

これらのオプションのそれぞれを平易な英語に翻訳しましょう:

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

ランタイムの動作は、どちらの場合も同じである必要があります。

実際、まともなコンパイラを想定すると、アセンブリコードでさえ同一である必要があります。

したがって、最初のオプションは基本的に「ステートメントを作成する」ためだけに使用されます。

2番目のオプションを使用して、上記にコメントを追加することもできます。

31
barak manos

1 << 7は定数式であり、コンパイラーはそれを128のように扱い、実行時のオーバーヘッドはありません。

ループ本体がないと、作者がなぜそれを使用するのかを言うのは難しいです。おそらくそれは7ビットに関連する何かを繰り返すループですが、それは私の推測です。

19
Yu Hao

では、なぜ人々は(1 << 7)バージョンを使用するのでしょうか。

これはドキュメントの形式であり、マジックナンバーではなく、コードを書いた人にとって意味のある2^72の7乗)です。最新の最適化コンパイラは、両方の例でまったく同じコードを生成する必要があるため、このフォームを使用するコストはなく、コンテキストを追加する利点があります。

godbolt を使用すると、少なくともgccclang、および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標準セクション6.6定数式で定義されている整数定数式です。

整数定数式117)は整数型であり、整数定数、列挙定数、文字定数、結果が整数定数である式のサイズであるオペランドのみを持つものとします。[...]

そして:

定数式には、評価されない部分式に含まれている場合を除き、代入、インクリメント、デクリメント、関数呼び出し、またはコンマ演算子を含めないでください。115)

そして、変換中に定数式を評価できることがわかります。

定数式は、実行時ではなく変換中に評価できるため、定数が存在する可能性のある任意の場所で使用できます。

14
Shafik Yaghmour

for(i = 0; i <(1 << 7); i ++)

そして

for(i = 0; i <128; i ++)

同じパフォーマンスを提供しますが、for(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ランタイムの累乗で変化します。ただし、アルゴリズムでこのロジックが必要な場合は適用できます。

5
Anirban Pal

コンパイラーは、どちらの場合も同じコードを出力します。コンテキストに応じて異なるフォームを使用することをお勧めします。

  1. NUM_STEPSまたはNUM_ELEMENTS_IN_NETWORK_PACKETは、明確にしたいアルゴリズムの定数部分または設計上の選択である場合に使用できます。
  2. または、128と記述して、定数である128であることを明確にすることもできます。
  3. または、競技会に参加していて、テストで"2 ^ 7回実行"のようになっている場合は、1 << 7と記述します。

または、ビット演算を知っていることを誇示することもできます。

私の謙虚な意見では、プログラミングは、コンパイラーとそれを読まなければならない人の2人に手紙を書くようなものです。あなたが何を意味しているのかは、両方にとって明確でなければなりません。

3
Luc