web-dev-qa-db-ja.com

C ++のforループに2つのインクリメントステートメントを配置するにはどうすればよいですか?

1つではなくfor- loop条件で2つの変数をインクリメントしたいと思います。

のようなもの:

for (int i = 0; i != 5; ++i and ++j) 
    do_something(i, j);

これの構文は何ですか?

80
Peter Smit

一般的なイディオムは、両方のオペランドを評価し、2番目のオペランドを返す カンマ演算子 を使用することです。副<文>この[前述の事実の]結果として、それ故に、従って、だから◆【同】consequently; therefore <文>このような方法で、このようにして、こんなふうに、上に述べたように◆【同】in this manner <文>そのような程度まで<文> AひいてはB◆【用法】A and thus B <文>例えば◆【同】for example; as an example:

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の元の値を取得することを期待していたので、xに対して5,6,7 ..を表示するはずでした。私が得たのはこれでした

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を使用してきました!

139
Paul Dixon

これを試して

for(int i = 0; i != 5; ++i, ++j)
    do_something(i,j);
54
yeyeyerman

しないでください!

http://www.research.att.com/~bs/JSF-AV-rules.pdf から:

AVルール199
forループの増分式は、単一のループパラメーターをループの次の値に変更する以外のアクションを実行しません。

根拠:読みやすさ。

6
squelart
for (int i = 0; i != 5; ++i, ++j) 
    do_something(i, j);
3
malay

FORループの増分句に2番目のインデックスをコーディングする方法を思い出すためにここに来ました。これは、主にC++で記述された別のプロジェクトに組み込んだサンプルでそれを観察することで行えることを知っていました。

今日、私はC#で作業していますが、FORステートメントはすべてのプログラミングで最も古い制御構造の1つであるため、この点で同じ規則に従うと確信しました。ありがたいことに、最近、古いCプログラムの1つでFORループの動作を正確に文書化するのに数日費やしましたが、これらの研究が今日のC#問題、特に2番目のインデックス変数の動作に当てはまる教訓を保持していることにすぐに気付きました。

不注意なことに、以下は私の観察の要約です。 [ローカル]ウィンドウで変数を注意深く観察することで、今日起こったことをすべて確認し、C#FORステートメントがCまたはC++ FORステートメントとまったく同じように動作するという期待を確認しました。

  1. FORループが最初に実行されるとき、インクリメント句(3つのうち3つ目)はスキップされます。 Visual CおよびC++では、ループを実装するブロックの中央に3つのマシン命令として増分が生成されるため、初期パスは初期化コードを1回だけ実行し、増分ブロックを飛び越えて終了テストを実行します。これにより、インデックスと制限変数の状態に応じて、FORループが0回以上実行される機能が実装されます。
  2. ループの本体が実行される場合、その最後のステートメントは、最初の反復でスキップされた3つの増分命令の最初へのジャンプです。これらの実行後、制御は中間節を実装する制限テストコードに自然に分類されます。そのテストの結果により、FORループの本体が実行されるか、またはスコープの下部のジャンプを過ぎて次の命令に制御が移るかが決まります。
  3. FORループブロックの下部からインクリメントブロックに制御が移行するため、テストが実行される前にインデックス変数がインクリメントされます。この動作は、制限句を学習した方法でコーディングする必要がある理由を説明するだけでなく、3番目の句の一部になるため、カンマ演算子を介して追加する2次インクリメントに影響します。したがって、最初の反復では変更されませんが、最後の反復で変更され、本体は実行されません。

ループの終了時にインデックス変数のいずれかがスコープ内に残っている場合、それらの値は、真のインデックス変数の場合、ループを停止するしきい値より1つ大きくなります。同様に、たとえば、ループに入る前に2番目の変数がゼロに初期化される場合、最後の値は反復カウントになります。これは、減少ではなく増分(++)であり、ループの本体はその値を変更します。

2
David A. Gray

Squelartに同意します。 2つの変数をインクリメントすると、特にそのうちの1つだけをテストする場合は特にバグが発生しやすくなります。

これはこれを行うための読みやすい方法です:

for(int i = 0; i < 5; ++i) {
    ++j;
    do_something(i, j);
}

Forループは、ループが1つの増加/減少変数で実行される場合を想定しています。その他の変数については、ループ内で変更します。

jiに結び付ける必要がある場合、元の変数をそのままにしてiを追加してください。

for(int i = 0; i < 5; ++i) {
    do_something(i,a+i);
}

ロジックがより複雑な場合(たとえば、実際に複数の変数を監視する必要がある場合)、whileループを使用します。

1
Ran Halprin
int main(){
    int i=0;
    int a=0;
    for(i;i<5;i++,a++){
        printf("%d %d\n",a,i);
    } 
}
0
Arkaitz Jimenez

数学を使用します。 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);

特に、値で関数に渡す場合は、望みどおりの結果を得る必要があります。

0
xaviersjs