「for」ループ内の「for」ループで同じカウンター変数名を使用できますか?


107

forループ内のループに同じカウンター変数を使用できforますか?

それとも変数は互いに影響し合うのでしょうか?次のコードでは、2番目のループにやなどの別の変数を使用するj必要がありiますか、それとも問題ありませんか?

for(int i = 0; i < 10; i++)
{
  for(int i = 0; i < 10; i++)
  {
  }
}

72
それは混乱しています—コードレビューで私を通り越すことはないでしょう。しかし、それは正当です。iスコープが異なる、両方ともと呼ばれる2つの異なる変数があります。-Wshadowこのような問題を自動的に報告するには、GCCを使用します。
ジョナサンレフラー

15
-Wshadow含まれていないことに驚いてい-Wallます。
leftaroundabout

5
@leftaroundabout -Wshadowは、グローバル変数のシャドウイングについても警告します。これは、大規模なプロジェクトで簡単に迷惑になる可能性があります。
キュービック

9
@leftaroundaboutにはさらに驚くべきことに、-Wextraが含まれていません-Wshadow。一部のプロジェクトでは十分に一般的であるか、または一部のgcc開発者は、このように省略されることを保証するために、コーディングスタイルとしてシャドウイングを愛しています。
ハイド

5
@leftaroundaboutキュービックが言ったことをエコーすると、-Wshadow恐ろしい偽陽性率になり、完全に役に立たなくなります。スコープには理由があり、シャドウイングは先験的に問題ありません。現在-Wshadow-local(注:ではない -Wshadow=local)は大きく異なります。しかし残念ながら、GCCはこれをトランクに含めることをこれまで拒否していました(GCCのフォークには含まれているようですが)。
Konrad Rudolph

回答:


140

同じ名前(識別子)を使用できます。別のオブジェクトになります。それらは互いに影響しません。内側のループ内では、外側のループで使用されているオブジェクトを参照する方法はありません(オブジェクトへのポインターを提供するなどして特別な準備をしない限り)。

これは一般的に悪いスタイルであり、混乱を招きやすいので避けてください。

オブジェクトは、内部オブジェクトが個別に定義されている場合にのみ異なりint iます。新しいオブジェクトを定義せずに同じ名前を使用すると、ループは同じオブジェクトを使用し、相互に干渉します。


3
for(i)とfor(j)をネストして、i ++内で使用すると、外側のループ変数が増加します。ただし、スコープが異なる変数であるため、両方のループで同じ識別子を使用する場合、あなたが言うことは正しいです。
KYL3R 2018

3
@BloodGain:「オブジェクト」は、C標準で使用される技術用語です。ここでは意図的に使用しました。
Eric Postpischil 2018

1
@EricPostpischil:ああ、なるほど。Cには一般的にこの用語を使用するという意味での「オブジェクト」がないため、私は標準でのその定義を認識していませんでした。また、これは新しいプログラマに誤解を招く恐れがあります(これは初心者の質問です)。私はそれをC11標準で見ていますが、今はC11より前にそのように定義されていたのかどうか気になります。
Bloodgain

1
そうだった。C99標準では3.15ではなく3.14です。だから私の側に言い訳はありません。それはあなたに質問することを教えてくれます<:-|
Bloodgain

1
より一般的には、ネストされたスコープで変数名を再利用することを妨げるものは何もありません。もちろん、紛らわしいコードを書くことに対する神の罰の恐れを除いて。
Isaac Rabinovitch

56

まず、これは完全に合法です。コードはコンパイルされて実行され、ネストされたループの本体を10×10 = 100回繰り返します。ループカウンタiネストされたループ内であろう隠す二つのカウンタは、互いに独立してインクリメントされるので、外側のループのカウンタ。

外側iは非表示であるため、ネストされたループの本体内のコードは、外側のループからではiなく、ネストされたループの値にのみアクセスできiます。ネストされたループが外側のiコードにアクセスする必要がない状況では、そのようなコードは完全に正当化できます。ただし、これは読者に混乱をもたらす可能性が高いため、「メンテナンスの責任」を回避するために、そのようなコードを記述しないことをお勧めします。

注:両方のループのカウンター変数の識別子は同じiですが、2つの独立した変数のままです。つまり、両方のループで同じ変数を使用していません。両方のループで同じ変数を使用することも可能ですが、コードが読みにくくなります。次に例を示します。

for (int i = 1 ; i < 100 ; i++) {
    for ( ; i % 10 != 0 ; i++) {
        printf("%02d ", i);
    }
    printf("%d\n", i);
}

これで、両方のループが同じ変数を使用します。ただし、このコードがコンパイルせずに何を行うかを理解するには、少し時間がかかります(デモ)。


4
質問は「同じカウンター変数を使用する」と表現されているため、シャドウイングは再定義が行われたときにのみ行われることも指摘しておきます。int内部のforループを省略すると、つまり実際には同じカウンター変数を使用すると、内部のループが終了するため、外部のループは1回だけ実行されますi == 10。これは
取るに足らない

@EastonBornemeier正解です。答えの本文で「同じ変数」の問題に対処する必要があると考えました。ありがとうございました!
dasblinkenlight 2018

@EricPostpischil "Variable shadowing"は公式用語で、wikipediaに独自のページがあります。ただし、標準の表現と一致するように回答を更新しました。ありがとうございました!
dasblinkenlight 2018

2
@dasblinkenlight:実際、私は方向について頭のけいれんを起こしました。内側の名前は外側の名前を覆っています。私の以前のコメントはその点で間違っていました。謝罪いたします。(ただし、これは英語の意味であり、公式の意味ではありません。Wikipediaは、Cまたはプログラミング一般の公式の出版物ではありません。また、この用語を定義する官庁や権威機関については知りません。)C標準では、 「隠す」ので、それが望ましいです。
Eric Postpischil 2018

特に「同じ変数」の例でいいですね。ただし、「コードは期待どおりにコンパイルおよび実行されます」は、「コードを注意深く読み、期待されるすべての影響を理解した人としてコードがコンパイルおよび実行される」という方がいいと思います... 読者の混乱を招く可能性があります。問題は読者が混乱することであり、読者はそれ以外のことを期待するかもしれません。
TripeHound 2018

26

あなたはできる。ただし、isのスコープに注意する必要があります。外側のiwith i_1と内側のiwith を呼び出すi_2と、isのスコープは次のようになります。

for(int i = 0; i < 10; i++)
{
     // i means i_1
     for(int i = 0; i < 10; i++)
     {
        // i means i_2
     }
     // i means i_1
}

これらは互いに影響を与えず、定義の範囲のみが異なることに注意してください。


17

それは完全に可能ですが、最初に宣言されたiに対処することはできません。

for(int i = 0; i < 10; i++)//I MEAN THE ONE HERE
{

  for(int i = 0; i < 10; i++)
    {

    }
}

2番目の子ループ内の2番目のループ

for(int i = 0; i < 10; i++)
{

  for(int i = 0; i < 10; i++)//the new i
    {
        // i cant see the i thats before this new i here
    }
}

最初のiの値を調整または取得する必要がある場合は、2番目のループでjを使用します

for(int i = 0; i < 10; i++)
{

  for(int j = 0; j < 10; j++)
    {

    }
}

クリエイティブが十分であれば、1つのループで両方を実行できます。

for(int i ,j= 0; i < 10; (j>9) ? (i++,j=0) : 0 ,j++)
{
    printf("%d %d\n",i,j);
}

6
コードのレビュー中に、ネストされたループでシャドウされたi変数をキャッチした場合、それをコーチングの機会と見なします。最後の例のように内部ループを難読化している誰か(つまり、1つのループではない)を見つけた場合、それらをウィンドウから捨てる可能性があります。
Bloodgain

1つのループで、forループは1つしかありません。2の場合、2つのキーワードまたは2つのwhileキーワードまたはforおよびwhileキーワードがあります
Dodo

3
だからループを難読化すると言った。あなたはまだループしていますが、あまり明白でない構文でそれを隠しました。そして、それはあらゆる面でより悪い。
Bloodgain

12

はい、内部forループと外部forループに同じカウンター変数名を使用できます。

forループから:

for ( init_clause ; cond_expression ; iteration_expression ) loop_statement
loop_statement として使用される式ステートメントは、init_clauseのスコープとは異なり、独自のブロックスコープ確立します。

for (int i = 0; ; ) {
    long i = 1;   // valid C, invalid C++
    // ...
}  

範囲loop_statementがされてネストされたの範囲内でinit_clause

C標準から#6.8.5p5反復ステートメント[強調は私のもの]

反復ステートメントは、そのスコープが、それを囲むブロックのスコープの厳密なサブセットであるブロックです。ループ本体は、スコープが反復ステートメントのスコープの厳密なサブセットであるブロックでもあります

C標準から#6.2.1p4識別子のスコープ[強調鉱山]

....内部スコープ内では、識別子は内部スコープで宣言されたエンティティを示します。外側のスコープで宣言されエンティティは、内側のスコープ内で非表示になります(表示されません)。


10

コード/コンパイラの観点からは、これは完全に有効で合法なことです。int i内部で宣言されたfor(int i = 0; i < 10; i++)ループは、新しい小さい範囲にあることを宣言するようにの宣言int i言い換えると外側のループ内(又は、可変の内側のスコープ内のすべてのアクセスiに移動しint i、内側のスコープ内で宣言され、int i外側のスコープはそのままにしておきます)。

とはいえ、コード品質の観点から、これはまったく恐ろしいことです。読みにくく、理解しにくく、誤解しやすい。しないでください。


8

はい、使用できますが、かなり混乱します。最も重要なことは、ループ内のローカル変数のスコープです。変数が関数内で宣言されている限り、その変数のスコープはその関数です。

int a = 5;
// scope of a that has value 5
int func(){
    int a = 10;
   // scope of a that has value 10
}
// scope of a that has value 5

ループの場合と同様に、内部ループ内で宣言された変数のスコープは異なり、外部ループで宣言された変数のスコープは異なります。

for(int i = 0; i < 10; i++){
    // In first iteration, value of i is 0

    for(int i = 1; i < 10; i++){
        // In first iteration, value of i is 1
    }
    // In first iteration, value of i is 0
}

より良い方法は、内部ループと外部ループに異なる変数を使用することです。

for(int i = 0; i < 10; i++){

    for(int j = 1; j < 10; j++){

    }

}

8

はい、間違いなく同じ名前の変数を使用できます。

Cプログラミング変数は、次の3つの場所で宣言できます。
ローカル変数:-関数またはブロック内。
グローバル変数:-すべての関数のうち。
仮パラメーター:-関数パラメーター内。

しかし、あなたの場合、i scope以下のものを気にする必要があります

for(int i = 0; i < 10; i++)
{
     // i means 1st for loop variable
     for(int i = 0; i < 10; i++)
     {
        // but here i means 2nd for loop  variable
     }
     //interesting thing here i means 1st for loop variable
}

注:内部ループと外部ループに異なる変数を使用することをお勧めします


6

はい-さらに興味深いことに、中括弧のセットを開くたびに変数名を再利用できます。これは、診断コードを挿入するときに便利です。左中かっこ「{」を入力し、続けて宣言と変数の使用を入力してから、中かっこを閉じると変数が消えます。これにより、中括弧の外で宣言された変数、クラス、メソッドの利点を維持しながら、本体の何にも干渉しないことが保証されます。


3

スコープルール: forステートメントで宣言された変数は、そのステートメントとループの本体でのみ使用できます。

コードで内部ループにiの複数のインスタンスを定義している場合、各インスタンスは独自のメモリ空間を占有します。したがって、結果は同じですが、心配する必要はありません。

int main(void) {

    int i = 2; //defined with file global scope outside of a function and will remain 2
    if(1)
    {       //new scope, variables created here with same name are different
        int i = 5;//will remain == 5
        for(int i = 0; i < 10; i++)
        {   //new scope for "i"

            printf("i value in first loop: %d \n", i); // Will print 0 in first iteration
            for(int i = 8; i < 15; i++) 
            {   //new scope again for "i", variable with same name is not the same
                printf("i value in nested loop: %d \n", i); // Will print 8 in first iteration
            }
        }

    }

    return 0;
}

ただし、理解が難しく、後で保守不可能なコードになるため、同じ変数名を使用することはお勧めしません。


1

重要な部分は、内部ループパラメータにが含まれていることint iです。iはこの方法で再定義されるため、2つの変数は互いに影響しません。それらのスコープは異なります。これを示す2つの例を次に示します。

for(int i = 0; i < 10; i++) // This code will print "Test" 100 times
{
 for(int i = 0; i < 10; i++)
 {
  puts("Test");
 }
}

上記のコードint iは内部ループパラメータに含まれ、以下のコードはのみを含むことに注意してくださいi

for(int i = 0; i < 10; i++) // This code will print "Test" 10 times
{
 for(i = 0; i < 10; i++)
 {
  puts("Test");
 }
}

0

まあ、スクリプトに問題がなくてもこれを行うことができますが、その構造は避ける必要があります。通常は混乱を招きます

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