C++ の「for」ループに 2 つのインクリメント ステートメントを入れるにはどうすればよいですか?
-
22-07-2019 - |
質問
2つの変数を一度にインクリメントしたいのですが、 for
-ループ条件を 1 つではなくループします。
したがって、次のようなものです:
for (int i = 0; i != 5; ++i and ++j)
do_something(i, j);
これの構文は何ですか?
解決
一般的なイディオムは、 カンマ演算子 これは両方のオペランドを評価し、2 番目のオペランドを返します。したがって:
for(int i = 0; i != 5; ++i,++j)
do_something(i,j);
しかし、それは本当にカンマ演算子なのでしょうか?
これを書いた後、あるコメント投稿者は、これは実際には for ステートメント内の特殊な構文糖であって、カンマ演算子ではまったくないのではないかと示唆しました。GCCで次のように確認しました。
int i=0;
int a=5;
int x=0;
for(i; i<5; x=i++,a++){
printf("i=%d a=%d x=%d\n",i,a,x);
}
x が a の元の値を取得すると期待していたので、5、6、7 が表示されるはずでした。xの場合。私が得たのはこれです
i=0 a=5 x=0
i=1 a=6 x=0
i=2 a=7 x=1
i=3 a=8 x=2
i=4 a=9 x=3
ただし、式を括弧で囲んでパーサーに実際にカンマ演算子を認識させると、次のようになります。
int main(){
int i=0;
int a=5;
int x=0;
for(i=0; i<5; x=(i++,a++)){
printf("i=%d a=%d x=%d\n",i,a,x);
}
}
i=0 a=5 x=0
i=1 a=6 x=5
i=2 a=7 x=6
i=3 a=8 x=7
i=4 a=9 x=8
最初は、これはカンマ演算子としてまったく動作していないことを示しているのではないかと思いましたが、結局のところ、これは単に優先順位の問題でした。カンマ演算子には 可能な限り低い優先順位, したがって、式 x=i++,a++ は、実質的に (x=i++),a++ として解析されます。
たくさんのコメントありがとうございます。興味深い学習体験でした。私は何年も C を使ってきました。
他のヒント
これを試してください
for(int i = 0; i != 5; ++i, ++j)
do_something(i,j);
しないでください!
http://www.research.att.com/~bs/JSF-AV-rules.pdf :
AVルール199
forループのインクリメント式は、単一の変更以外のアクションを実行しません ループの次の値へのループパラメーター。根拠:読みやすさ。
for (int i = 0; i != 5; ++i, ++j)
do_something(i, j);
FORループのインクリメント句に2番目のインデックスをコーディングする方法を思い出すためにここに来ました。これは主に、C ++で記述された別のプロジェクトに組み込んだサンプルでそれを観察することで実行できるとわかっていました。
今日、私はC#で作業していますが、FORステートメントはすべてのプログラミングで最も古い制御構造の1つであるため、この点で同じ規則に従うと確信しました。ありがたいことに、最近、古いCプログラムの1つでFORループの動作を正確に文書化するために数日を費やしました。 。
不注意なことに、以下は私の観察の要約です。 Localsウィンドウで変数を注意深く観察することで、今日起こっているすべてのことを確認し、C#FORステートメントがCまたはC ++ FORステートメントとまったく同じように動作するという期待を確認しました。
- FORループが初めて実行されるとき、インクリメント句(3つのうち3つ目)はスキップされます。 Visual CおよびC ++では、ループを実装するブロックの中央に3つのマシン命令として増分が生成されるため、初期パスは初期化コードを1回だけ実行し、増分ブロックを飛び越えて終了テストを実行します。これにより、インデックスと制限変数の状態に応じて、FORループが0回以上実行される機能が実装されます。
- ループの本体が実行される場合、最後のステートメントは、最初の反復でスキップされた3つのインクリメント命令の最初へのジャンプです。これらの実行後、制御は自然に中間節を実装する制限テストコードに分類されます。そのテストの結果により、FORループの本体が実行されるか、またはスコープの下部のジャンプを過ぎて次の命令に制御が移るかどうかが決まります。
- FORループブロックの下部からインクリメントブロックに制御が移動するため、テストが実行される前にインデックス変数がインクリメントされます。この動作は、制限句を学習した方法でコーディングする必要がある理由を説明するだけでなく、3番目の句の一部になるため、カンマ演算子を介して追加する2次インクリメントに影響します。したがって、最初の反復では変更されませんが、最後の反復で変更され、本体は実行されません。
ループの終了時にいずれかのインデックス変数がスコープ内に残っている場合、それらの値は、真のインデックス変数の場合、ループを停止するしきい値よりも1つ大きくなります。同様に、たとえば、ループに入る前に2番目の変数がゼロに初期化された場合、最後の値は反復カウントになります。これは、減少ではなく増分(++)であり、ループの本体の値が変更されます。
squelartに同意します。特に1つだけをテストする場合、2つの変数をインクリメントするとバグが発生しやすくなります。
これはこれを行うための読みやすい方法です:
for(int i = 0; i < 5; ++i) {
++j;
do_something(i, j);
}
For
ループは、ループが1つの増加/減少変数で実行される場合を対象としています。その他の変数については、ループ内で変更します。
j
を i
に関連付ける必要がある場合、元の変数をそのままにして i
を追加してみませんか?
for(int i = 0; i < 5; ++i) {
do_something(i,a+i);
}
ロジックがより複雑な場合(たとえば、実際に複数の変数を監視する必要がある場合)、 while
ループを使用します。
int main(){
int i=0;
int a=0;
for(i;i<5;i++,a++){
printf("%d %d\n",a,i);
}
}
数学を使用します。 2つの操作が数学的にループの繰り返しに依存する場合、なぜ数学を行わないのですか?
int i, j;//That have some meaningful values in them?
for( int counter = 0; counter < count_max; ++counter )
do_something (counter+i, counter+j);
または、より具体的にOPの例を参照:
for(int i = 0; i != 5; ++i)
do_something(i, j+i);
特に、値で関数に渡す場合は、希望どおりの動作をするものを取得する必要があります。