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


93

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

だから次のようなもの:

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

これの構文は何ですか?

回答:


154

一般的なイディオムは、両方のオペランドを評価して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の元の値を取得することを期待していたので、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を使用しています!


1
forループの最初または3番目の部分にあるカンマ、カンマ演算子ではなく、単なるカンマ区切り文字であることを何度か読みました。(ただし、C ++言語標準の解析が特に
得意

私はあなたが間違っていると最初に思ったが、私はいくつかのテストコードを書いて、あなたは正しい-それはコンマ演算子のように振る舞わない。私の答えを修正します!
ポールディクソン

19
それはあるという文脈でのコンマ演算子。期待どおりの結果が得られない理由は、コマンド演算子の方が代入演算子よりも優先順位が低いためです。そのため、括弧がなければ、(x = i ++)、j ++として解析されます。
カフェ、

6
これはコンマ演算子です。割り当てはコンマ演算子よりも強く結びついているため、x = i ++、a ++は解析され(x = i ++)、a ++であり、x =(i ++、a ++)ではありません。一部のライブラリではその特性が誤用されているため、v = 1,2,3になります。は直感的なことを行いますが、v = 1は、オーバーロードされたコンマ演算子が追加するプロキシオブジェクトを返すためです。
AProgrammer 2009

3
OK。open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2857.pdfのセクション6.5.3最後の部分は、「表現」です。(1.6#2は「式リスト」を「コンマで区切られた式のリスト」として定義していますが、この構成は6.5.3にはありません。)これは、「++ i、++ j」を記述する場合、それ自体が式でなければならないため、「、」はカンマ演算子(5.18)でなければならないことを意味します。(これは、「イニシャライザのリスト」や「関数への引数のリスト」ではなく、5.18#2にあるように「カンマに特別な意味が与えられている」例です。)少しわかりにくいかもしれませんが。
ダニエルダラナス2009

55

これを試して

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

17
+1最初の部分でjを宣言することもできます。for(int i = 0、j = 0; i!= 5; ++ i、++ j){...}
ダニエルダラナス2009

1
+1補足として、これと同じ構文がC#でも機能します(Googleで「C#for loop incrament 2 counters」を検索したので、これについて触れておきます)。
CodingWithSpike

@CodingWithSpike:まあ、C#ではコンマ特別です。実際にコンマ演算子式をそこに表示することはできません。C ++でのカンマ演算子の正当な使用例ですが、C#によって拒否されました:for( ; ; ((++i), (++j)) )
Ben Voigt

@BenVoigtはコンマとは関係ありません。これも正当なC#ではありませんfor(int i = 0; i != 5; (++i)) {。余分な括弧は、コンパイラをだまして「インクリメント」操作ではないと考えさせます。
CodingWithSpike 2017年

@CodingWithSpike:それは事実ですが、括弧 C#がコンマを認識する方法変更し、forアクション内の特別な意味を防ぎます。
Ben Voigt 2017年

6

それをしないようにしてください!

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

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

根拠:読みやすさ。


4
それは本当ですが、公平を期すために、ルールの標準がガーデンバラエティC(++)プログラムではなく、戦闘機の組み込みソフトウェア用に書かれたと確信しています。とは言っても、F-35ソフトウェアを設計することになるのは、おそらく読みやすさの習慣であり、誰が知っているのでしょうか。
ガロア2015


2

ここに来て、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ループブロックの下部からインクリメントブロックに移るため、テストが実行される前にインデックス変数がインクリメントされます。この動作は、学習した方法でlimit句をコーディングする必要がある理由を説明するだけでなく、3番目の句の一部になるため、カンマ演算子を介して追加する2次インクリメントに影響します。したがって、最初の反復では変更されませんが、本体を実行しない最後の反復では変更されます。

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


1

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

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

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

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

に関連付ける必要jがある場合はi、元の変数をそのままにして追加してみiませんか?

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

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


2
最初の例では、jはiよりも1だけインクリメントされます。最初のxステップで何らかのアクションを実行する必要があるイテレーターについてはどうですか?(そして、コレクションは常に十分に長いです)反復ごとにイテレータを昇格することはできますが、はるかにきれいです。
Peter Smit

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

1
ループを作成せずiaローカルにしないことのポイントは何ですか?
sbi 2009

2
なし、forで両方のインクリメントを実行する方法を示すだけで、sytnaxの単なる例
Arkaitz Jimenez

0

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

特に、値で関数に渡す場合、必要な機能を正確に実行する必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.