C Language
反復ステートメント/ループ:for、while、do-while
サーチ…
構文
- / *すべてのバージョン* /
- for([expression]; [expression]; [expression])one_statement
- for([expression]; [expression]; [expression]){ゼロまたは複数の文}
- 一方、(式)one_statement
- while(式){0個以上の文}
- while(expression)while one_statementを実行します。
- while(expression)while {1つ以上の文}を実行します。
- //上記のフォームに加えてC99以降
- for(宣言; [式]; [式])one_statement;
- for(宣言; [式]; [式]){ゼロまたは複数の文}
備考
反復ステートメント/ループは2つのカテゴリに分類されます。
- head-controlled iteration文/ループ
- フットコントロール反復ステートメント/ループ
ヘッド制御反復ステートメント/ループ
for ([<expression>]; [<expression>]; [<expression>]) <statement>
while (<expression>) <statement>
for ([declaration expression]; [expression] [; [expression]]) statement
フットコントロールのイテレーションステートメント/ループ
do <statement> while (<expression>);
ループの場合
何度も繰り返してコードブロックを実行するには、ループが画像に入ります。 for
ループは、コードブロックが一定回数実行されるときに使用されます。例えば、サイズn
配列をユーザ入力で満たすには、 scanf()
をn
回実行する必要があります。
#include <stddef.h> // for size_t
int array[10]; // array of 10 int
for (size_t i = 0; i < 10; i++) // i starts at 0 and finishes with 9
{
scanf("%d", &array[i]);
}
このように、 scanf()
関数呼び出しはn
回(この例では10回)実行されますが、1回だけ書き込まれます。
ここで、変数i
はループインデックスであり、提示されたものとして最もよく宣言される。 size_t
( 型の型 )は、データオブジェクトを数えたりループしたりするすべてのものに使用する必要があります。
for
内部で変数を宣言するこの方法は、C99標準に更新されたコンパイラでのみ使用できます。なんらかの理由で古いコンパイラに悩まされている場合は、 for
ループの前にループインデックスを宣言することができます:
#include <stddef.h> /* for size_t */
size_t i;
int array[10]; /* array of 10 int */
for (i = 0; i < 10; i++) /* i starts at 0 and finishes at 9 */
{
scanf("%d", &array[i]);
}
whileループ
while
ループは、条件が真である間にコードを実行するために使用されます。 while
ループは、コードのブロックが可変回数実行されるときに使用されます。たとえば、表示されているコードは、ユーザーが0
数字を挿入している限り、ユーザー入力を取得します。ユーザーが0
挿入した場合、while条件は真ではなくなり、実行はループを終了し、後続のコードに続きます。
int num = 1;
while (num != 0)
{
scanf("%d", &num);
}
Do-Whileループ
for
ループとwhile
ループとは異なり、 do-while
ループはループの最後に条件の真偽をチェックします。つまり、 do
ブロックは一度実行され、ブロックの最下部のwhile
の状態をチェックします。つまり、 do-while
ループは常に少なくとも1回は実行されます。
たとえば、このdo-while
ループは、これらの値の合計が50
以上になるまで、ユーザーからの数値を取得します。
int num, sum;
num = sum = 0;
do
{
scanf("%d", &num);
sum += num;
} while (sum < 50);
do-while
ループは、ほとんどのプログラミングスタイルで比較的まれです。
forループにおける制御の構造と流れ
for ([declaration-or-expression]; [expression2]; [expression3])
{
/* body of the loop */
}
for
ループでは、ループ条件に3つの式があり、すべてオプションです。
- 最初の式
declaration-or-expression
は、ループを初期化します。ループの開始時に1回だけ実行されます。
これは、ループ変数の宣言と初期化、または一般的な式のいずれかです。宣言の場合、宣言された変数のスコープはfor
ステートメントによって制限されます。
Cの歴史的なバージョンではここで式のみが許され、ループ変数の宣言はfor
前に置く必要がありfor
。
- 2番目の式
expression2
がテスト条件です。初期化後に最初に実行されます。条件がtrue
場合、コントロールはループの本体に入ります。そうでない場合は、ループの最後にループの本体の外側に移動します。その後、bodyとupdateステートメントが実行されるたびにこの条件がチェックされます。true
場合、コントロールはループの本体の先頭に戻ります。この条件は通常、ループの本体が実行される回数をチェックすることを意図しています。これはループを終了させる主な方法です。もう1つはジャンプ文を使用する方法です。 - 3番目の式
expression3
は、 更新ステートメントです。ループの本体が実行されるたびに実行されます。これは、ループ本体が実行された回数を保持する変数をインクリメントするためによく使用され、この変数はイテレータと呼ばれます 。
ループ本体の各実行インスタンスは反復と呼ばれます 。
例:
for(int i = 0; i < 10 ; i++)
{
printf("%d", i);
}
出力は次のとおりです。
0123456789
上記の例では、最初にi = 0
が実行され、 i
初期化されます。次に、条件i < 10
がチェックされ、これはtrue
と評価される。コントロールはループの本体に入り、 i
の値が出力されます。その後、制御へ移行しi++
の値更新、 i
続いて0から1に、条件が再度チェックされ、処理が続行されます。これは、 i
の値が10になるまで続きます。次に、条件i < 10
はfalse
評価し、その後、コントロールはループから移動します。
無限ループ
ループが無限ループと呼ばれるのは、制御が入力されてもループの本体から離れることはないということです。これは、ループのテスト条件がfalse
評価されない場合に発生しfalse
。
例:
for (int i = 0; i >= 0; )
{
/* body of the loop where i is not changed*/
}
上記の例では、イテレータである変数i
は0に初期化されていtrue
。テスト条件は最初はtrue
です。しかし、 i
は体のどこでも変更されず、更新式は空です。したがって、 i
は0のままであり、テスト条件は決してfalse
に評価されることはなく、無限ループにつながります。
ジャンプ文がないと仮定すると、無限ループが形成される別の方法は、明示的に条件を真に保つことです:
while (true)
{
/* body of the loop */
}
for
ループでは、条件文はオプションです。この場合、条件が常にtrue
無限ループにつながる、意味をなさない。
for (;;)
{
/* body of the loop */
}
しかし、場合によっては、 break
などのジャンプ文を使用してループを終了する意図で、条件を意図的にtrue
に保つことができます。
while (true)
{
/* statements */
if (condition)
{
/* more statements */
break;
}
}
ループアンロールとダフのデバイス
時には、ストレートフォワードループをループ本体に完全に含めることはできません。これは、ループがいくつかのステートメントBによってプライムされる必要があるからです。次に、繰り返しがいくつかのステートメントAで始まり、ループの前に再びBが続きます。
do_B();
while (condition) {
do_A();
do_B();
}
コード内で二回Bを繰り返すことで潜在的なカット/ペーストの問題を回避するには、 ダフのデバイスがの途中からループを開始するために適用され得るwhile
使用して、体switch文をし、行動を通って落下します。
switch (true) while (condition) {
case false: do_A(); /* FALL THROUGH */
default: do_B(); /* FALL THROUGH */
}
ダフのデバイスは、実際にはループアンローリングを実装するために発明されました。メモリブロックにマスクを適用すると想像してくださいn
は正の値を持つ符号付き整数型です。
do {
*ptr++ ^= mask;
} while (--n > 0);
n
が常に4で割り切れる場合は、簡単にこれを展開することができます:
do {
*ptr++ ^= mask;
*ptr++ ^= mask;
*ptr++ ^= mask;
*ptr++ ^= mask;
} while ((n -= 4) > 0);
しかし、Duffのデバイスでは、 n
が4で割り切れない場合、コードはループの途中で正しい場所にジャンプするこのアンローリングイディオムに従うことができます。
switch (n % 4) do {
case 0: *ptr++ ^= mask; /* FALL THROUGH */
case 3: *ptr++ ^= mask; /* FALL THROUGH */
case 2: *ptr++ ^= mask; /* FALL THROUGH */
case 1: *ptr++ ^= mask; /* FALL THROUGH */
} while ((n -= 4) > 0);
コンパイラの最適化エンジンはプログラマのためにループを展開することができるので、この種の手動アンロールは最新のコンパイラではほとんど必要ありません。