Kann eine 'for'-Schleife innerhalb einer' for'-Schleife denselben Zählervariablennamen verwenden?

107

Kann ich dieselbe Zählervariable für eine forSchleife innerhalb einer forSchleife verwenden?

Oder beeinflussen sich die Variablen gegenseitig? Sollte der folgende Code eine andere Variable für die zweite Schleife verwenden, z. B. joder ist in iOrdnung?

for(int i = 0; i < 10; i++)
{
  for(int i = 0; i < 10; i++)
  {
  }
}
Uclydde
quelle
72
Es ist verwirrend - es würde mich bei einer Codeüberprüfung nicht überholen. Aber es ist legitim. Es gibt zwei verschiedene Variablen imit unterschiedlichen Gültigkeitsbereichen. Verwenden Sie diese -WshadowOption mit GCC, um solche Probleme automatisch zu melden.
Jonathan Leffler
15
Ich bin überrascht, dass -Wshadowdas nicht enthalten ist -Wall.
links um den
5
@leftaroundabout -Wshadowwarnt auch vor dem Abschatten globaler Variablen, was bei größeren Projekten leicht ärgerlich werden kann.
Cubic
9
@leftaroundabout noch überraschender, sogar -Wextranicht enthalten -Wshadow. Ich denke, es ist in einigen Projekten häufig genug, oder ein gcc- Entwickler liebt Shadowing als Codierungsstil, um zu rechtfertigen, dass er so ausgelassen wird.
Hyde
5
@leftaroundabout Das Echo von Cubic -Wshadowhat eine schreckliche Falsch-Positiv-Rate, die es völlig unbrauchbar macht. Der Geltungsbereich besteht aus einem bestimmten Grund, und das Abschatten ist von vornherein nicht problematisch. Jetzt -Wshadow-local(Anmerkung: nicht -Wshadow=local ) ist ganz anders. Leider hat sich GCC bisher geweigert, es in den Kofferraum aufzunehmen (obwohl es anscheinend Gabeln von GCC gibt, die es enthalten).
Konrad Rudolph

Antworten:

140

Sie können denselben Namen (Bezeichner) verwenden. Es wird ein anderes Objekt sein. Sie werden sich nicht gegenseitig beeinflussen. Innerhalb der inneren Schleife gibt es keine Möglichkeit, auf das in der äußeren Schleife verwendete Objekt zu verweisen (es sei denn, Sie treffen hierfür besondere Vorkehrungen, indem Sie einen Zeiger darauf bereitstellen).

Dies ist im Allgemeinen ein schlechter Stil, neigt zu Verwirrung und sollte vermieden werden.

Die Objekte unterscheiden sich nur, wenn das innere separat definiert ist, wie bei dem von int iIhnen gezeigten. Wenn derselbe Name verwendet wird, ohne ein neues Objekt zu definieren, verwenden die Schleifen dasselbe Objekt und stören sich gegenseitig.

Eric Postpischil
quelle
3
Wenn Sie for (i) und for (j) verschachtelt und in i ++ verwenden, wird die Variable der äußeren Schleife erhöht. Was Sie jedoch sagen, ist richtig, wenn Sie in beiden Schleifen denselben Bezeichner verwenden, da es sich um Variablen mit unterschiedlichem Gültigkeitsbereich handelt.
KYL3R
3
@BloodGain: "Objekt" ist ein technischer Begriff, der im C-Standard verwendet wird. Ich habe es hier absichtlich benutzt.
Eric Postpischil
1
@ EricPostpischil: Ah, ich verstehe, ja. Ich war mir dieser Definition im Standard nicht bewusst und befürchtete, dass sie für neue Programmierer irreführend sein würde (da dies ganz klar eine Anfängerfrage ist), da C keine "Objekte" in dem Sinne hat, wie wir den Begriff allgemein verwenden. Ich sehe es im C11-Standard, und jetzt bin ich gespannt, ob es vor C11 so definiert wurde.
Blutgewinn
1
Es war. Es ist 3,14 im C99-Standard anstelle von 3,15. Also keine Entschuldigung meinerseits. Das bringt mir bei, Sie zu befragen <: - |
Bloodgain
1
Allgemeiner: Nichts hindert Sie daran, einen Variablennamen in einem verschachtelten Bereich wiederzuverwenden. Außer natürlich der Angst vor Gottes Strafe, verwirrenden Code zu schreiben.
Isaac Rabinovitch
56

Erstens ist dies absolut legal: Der Code wird kompiliert und ausgeführt, wobei der Hauptteil der verschachtelten Schleife 10 × 10 = 100 Mal wiederholt wird. Der Schleifenzähler iinnerhalb der verschachtelten Schleife verbirgt den Zähler der äußeren Schleife, sodass die beiden Zähler unabhängig voneinander inkrementiert werden.

Da das Äußere iausgeblendet ist, hat der Code im Körper der verschachtelten Schleife nur Zugriff auf den Wert ider verschachtelten Schleife, nicht ivon der äußeren Schleife. In Situationen, in denen die verschachtelte Schleife keinen Zugriff auf den äußeren iCode benötigt, kann ein solcher Code durchaus gerechtfertigt sein. Dies führt jedoch wahrscheinlich zu mehr Verwirrung bei den Lesern. Daher ist es eine gute Idee, das Schreiben eines solchen Codes zu vermeiden, um "Wartungsverpflichtungen" zu vermeiden.

Hinweis: Obwohl die Zählervariablen beider Schleifen denselben Bezeichner haben i, bleiben sie zwei unabhängige Variablen, dh Sie verwenden nicht dieselbe Variable in beiden Schleifen. Die Verwendung derselben Variablen in beiden Schleifen ist ebenfalls möglich, der Code ist jedoch schwer zu lesen. Hier ist ein Beispiel:

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

Jetzt verwenden beide Schleifen dieselbe Variable. Es dauert jedoch eine Weile, um herauszufinden, was dieser Code tut, ohne ihn zu kompilieren ( Demo ).

dasblinkenlight
quelle
4
Da die Frage so formuliert ist, dass "dieselbe Zählervariable verwendet wird", möchte ich auch darauf hinweisen, dass die Abschattung nur erfolgt, wenn die Neudefinition erfolgt. Wenn Sie die intinnere for-Schleife weglassen , dh tatsächlich dieselbe Zählervariable verwenden , wird die äußere Schleife nur einmal ausgeführt, da die innere Schleife verlassen wird i == 10. Dies ist trivial, aber gedacht, es bietet
Klarheit
@ EastonBornemeier Sie haben Recht, ich dachte, ich sollte das Problem der "gleichen Variablen" im Hauptteil der Antwort ansprechen. Danke dir!
Dasblinkenlight
@EricPostpischil "Variable Shadowing" ist ein offizieller Begriff mit einer eigenen Seite auf Wikipedia . Ich habe die Antwort jedoch so aktualisiert, dass sie mit dem Wortlaut des Standards übereinstimmt. Danke dir!
Dasblinkenlight
2
@dasblinkenlight: Eigentlich hatte ich einen Gehirnkrampf um die Richtung und der innere Name beschattet den äußeren Namen. Mein vorheriger Kommentar war in dieser Hinsicht falsch. Entschuldigen Sie. (Dies ist jedoch im englischen Sinne, nicht im offiziellen Sinne - Wikipedia ist keine offizielle Veröffentlichung für C oder Programmierung im Allgemeinen, und mir sind keine Ämter oder maßgeblichen Stellen bekannt, die den Begriff definieren.) Der C-Standard verwendet "Verstecken", das ist also vorzuziehen.
Eric Postpischil
Schön, besonders mit dem Beispiel "gleiche Variable". Ich denke jedoch, dass " der Code wie erwartet kompiliert und ausgeführt wird " besser wäre als "der Code wird kompiliert und ausgeführt als jemand, der ihn sorgfältig gelesen hat und alle erwarteten Auswirkungen versteht " ... wie Sie sagen, Code wie dieser " wird wahrscheinlich mehr Verwirrung bei seinen Lesern hervorrufen "und das Problem ist, dass ein verwirrter Leser möglicherweise etwas anderes erwartet als das, was er tut.
TripeHound
26

Sie können. Sie sollten sich jedoch des Umfangs der is bewusst sein . Wenn wir das Äußere imit i_1und das Innere imit nennen i_2, ist der Umfang des is wie folgt:

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
}

Sie sollten beachten, dass sie sich nicht gegenseitig beeinflussen und nur ihr Definitionsumfang unterschiedlich ist.

Oh mein Gott
quelle
17

Das ist durchaus möglich, aber denken Sie daran, dass Sie das erste deklarierte i nicht ansprechen können

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

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

    }
}

in der zweiten Schleife innerhalb der zweiten untergeordneten Schleife

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
    }
}

Wenn Sie den Wert des ersten i anpassen oder abrufen müssen, verwenden Sie j in der zweiten Schleife

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

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

    }
}

und wenn Sie kreativ genug sind, können Sie beide in einer Schleife ausführen

for(int i ,j= 0; i < 10; (j>9) ? (i++,j=0) : 0 ,j++)
{
    printf("%d %d\n",i,j);
}
Dodo
quelle
6
Wenn ich während einer Codeüberprüfung schattierte i-Variablen in verschachtelten Schleifen abfangen würde, würde ich dies als Coaching-Gelegenheit betrachten. Wenn ich jemanden erwische, der die innere Schleife wie Ihr letztes Beispiel verschleiert (das ist NICHT eine Schleife), könnte ich ihn aus einem Fenster werfen.
Blutgewinn
es ist eine Schleife, es hat nur eine für Schleife, wenn es 2 wäre, hätte es zwei für Schlüsselwörter oder zwei während Schlüsselwörter oder ein für und während Schlüsselwörter
Dodo
3
Deshalb habe ich gesagt, Sie haben die Schleife verschleiert . Sie schleifen immer noch, Sie haben es nur mit weniger offensichtlicher Syntax ausgeblendet. Und es ist in jeder Hinsicht schlimmer.
Blutgewinn
12

Ja, Sie können für eine innere forSchleife denselben Zählervariablennamen verwenden wie für die äußere forSchleife.

Von for-Schleife :

for ( init_clause ; cond_expression ; iteration_expression ) loop_statement
Die als loop_statement verwendete Ausdrucksanweisung legt einen eigenen Blockbereich fest, der sich vom Bereich von init_clause unterscheidet .

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

Der Bereich von loop_statement ist im Bereich von init_clause verschachtelt .

Aus C-Standards # 6.8.5p5 Iterationsanweisungen [Hervorhebung von mir]

Eine Iterationsanweisung ist ein Block, dessen Gültigkeitsbereich eine strikte Teilmenge des Gültigkeitsbereichs des umschließenden Blocks ist. Der Schleifenkörper ist auch ein Block, dessen Gültigkeitsbereich eine strikte Teilmenge des Gültigkeitsbereichs der Iterationsanweisung ist .

Aus C-Standards # 6.2.1p4 Identifikationsbereiche [Schwerpunkt Mine]

.... Innerhalb des inneren Bereichs bezeichnet der Bezeichner die im inneren Bereich deklarierte Entität. Die im äußeren Bereich deklarierte Entität ist im inneren Bereich verborgen (und nicht sichtbar).

HS
quelle
10

Aus Sicht des Codes / Compilers wäre dies eine absolut gültige und legale Angelegenheit. Die int iin der inneren erklärt for(int i = 0; i < 10; i++)Schleife ist in einem neuen, kleineren Umfang, so dass in der Erklärung shadows die Erklärung der int iin der äußeren Schleife (oder, mit anderen Worten: In dem Innenumfang alle Zugriffe auf die Variable iShow die int iin dem inneren Umfang erklärt, das int iim äußeren Bereich unberührt lassen).

Aus Sicht der Codequalität ist dies jedoch absolut schrecklich. Es ist schwer zu lesen, schwer zu verstehen und leicht zu missverstehen. Tu es nicht.

CharonX
quelle
8

Ja, Sie können es verwenden, aber es ist ziemlich verwirrend. Das Wichtigste ist der Umfang der lokalen Variablen innerhalb der Schleife. Wenn eine Variable innerhalb einer Funktion deklariert ist, ist der Umfang dieser Variablen diese Funktion.

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

In ähnlicher Weise haben Variablen, die innerhalb der inneren Schleife deklariert wurden, unterschiedliche Gültigkeitsbereiche und Variablen, die innerhalb der inneren Schleife deklariert wurden, unterschiedliche Gültigkeitsbereiche.

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
}

Der bessere Ansatz besteht darin, unterschiedliche Variablen für innere und äußere Schleifen zu verwenden.

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

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

    }

}
Safwan Shaikh
quelle
8

Ja, definitiv können Sie dieselbe Namensvariable verwenden.

C-Programmiervariablen können an drei Stellen deklariert werden:
lokale Variablen: -In einer Funktion oder einem Block.
Globale Variablen: -Aus allen Funktionen.
Formale Parameter: -In den Funktionsparametern.

Aber in Ihrem Fall i scopemüssen Sie unten Dinge beachten

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
}

Hinweis: Es wird empfohlen, unterschiedliche Variablen für innere und äußere Schleifen zu verwenden

Zaynul Abadin Tuhin
quelle
6

Ja - und noch interessanter ist, dass Sie einen Variablennamen jedes Mal wiederverwenden können, wenn Sie eine Reihe von Klammern öffnen. Dies ist häufig beim Einfügen von Diagnosecode hilfreich. Geben Sie eine offene Klammer '{' ein, gefolgt von der Deklaration und Verwendung von Variablen, schließen Sie die Klammer und die Variablen verschwinden. Dies garantiert, dass Sie nichts im Hauptteil stören, während Sie den Vorteil von Variablen, Klassen und Methoden behalten, die außerhalb der geschweiften Klammern deklariert sind.

SuwaneeCreek
quelle
3

Geltungsbereichsregel: Eine in einer for-Anweisung deklarierte Variable kann nur in dieser Anweisung und im Hauptteil der Schleife verwendet werden.

Wenn Sie in Ihrem Code mehrere Instanzen von i in inneren Schleifen definiert haben, belegt jede Instanz ihren eigenen Speicherplatz. Es gibt also nichts, worüber man sich Sorgen machen müsste, es wäre das gleiche.

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;
}

Es wird jedoch nicht empfohlen, denselben Variablennamen zu verwenden, da dieser schwer zu verstehen ist und später zu nicht wartbarem Code wird.

Subash J.
quelle
1

Der wichtige Teil ist, dass der innere Schleifenparameter enthält int i. Da idies auf diese Weise neu definiert wird, beeinflussen sich die beiden Variablen nicht gegenseitig. Ihre Bereiche sind unterschiedlich. Hier sind zwei Beispiele, um dies zu zeigen:

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

Beachten Sie, dass der obige Code int iim Parameter der inneren Schleife enthalten ist und der folgende Code nur enthält i.

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

Nun, Sie können dies tun, ohne dass Ihre Skripte ein Problem haben, aber Sie sollten diese Struktur vermeiden. Dies führt normalerweise zu Verwirrung

Lagerfeuer
quelle