Un ciclo "for" all'interno di un ciclo "for" può utilizzare lo stesso nome di variabile contatore?


107

Posso usare la stessa variabile contatore per un forciclo all'interno di un forciclo?

O le variabili si influenzeranno a vicenda? Il codice seguente dovrebbe utilizzare una variabile diversa per il secondo ciclo, ad esempio jo va ibene?

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

72
È fonte di confusione: non mi sfuggirebbe in una revisione del codice. Ma è legittimo. Ci sono due variabili differenti entrambe chiamate i, con ambiti differenti. Utilizzare -Wshadowcon GCC per ottenere automaticamente segnalati tali problemi.
Jonathan Leffler

15
Sono sorpreso che -Wshadownon sia incluso in -Wall.
sinistra intorno al

5
@leftaroundabout -Wshadowavverte anche dello shadowing delle variabili globali, che potrebbe facilmente diventare fastidioso in progetti più grandi.
cubo

9
@leftaroundabout ancora più sorprendentemente, anche -Wextrase non include -Wshadow. Immagino sia abbastanza comune in alcuni progetti, o alcuni sviluppatori gcc amano lo shadowing come stile di codifica, per garantire di essere escluso in questo modo.
hyde

5
@leftaroundabout Facendo eco a ciò che ha detto Cubic, -Wshadowha un orrendo tasso di falsi positivi, rendendolo completamente inutile. L'ambito esiste per una ragione e lo shadowing non è a priori problematico. Ora -Wshadow-local(nota: no -Wshadow=local ) è molto diverso. Ma sfortunatamente GCC finora si è rifiutato di includerlo nel trunk (anche se sembra che ci siano fork di GCC che lo includono).
Konrad Rudolph,

Risposte:


140

Puoi usare lo stesso nome (identificatore). Sarà un oggetto diverso. Non si influenzeranno a vicenda. All'interno del ciclo interno, non c'è modo di fare riferimento all'oggetto utilizzato nel ciclo esterno (a meno che non si prenda disposizioni speciali per questo, ad esempio fornendo un puntatore ad esso).

Questo è generalmente un cattivo stile, è soggetto a confusione e dovrebbe essere evitato.

Gli oggetti sono diversi solo se quello interno è definito a parte, come con quello int iche hai mostrato. Se viene utilizzato lo stesso nome senza definire un nuovo oggetto, i loop utilizzeranno lo stesso oggetto e interferiranno tra loro.


3
l'uso di for (i) e for (j) annidato e all'interno di i ++ aumenterà la variabile del ciclo esterno. Tuttavia, ciò che dici è corretto se usi lo stesso identificatore in entrambi i cicli, perché sono variabili con ambito diverso.
KYL3R

3
@BloodGain: "Object" è un termine tecnico utilizzato nello standard C. L'ho usato deliberatamente qui.
Eric Postpischil

1
@EricPostpischil: Ah, capisco, sì. Non ero a conoscenza di quella definizione nello standard e temevo che sarebbe stata fuorviante per i nuovi programmatori (poiché questa è chiaramente una domanda per principianti), poiché C non ha "oggetti" nel senso che generalmente usiamo il termine. Lo vedo nello standard C11, e ora sono curioso di sapere se fosse definito in questo modo prima di C11.
Bloodgain

1
Era. È 3,14 nello standard C99, invece di 3,15. Quindi nessuna scusa da parte mia. Questo mi insegnerà a interrogarti <Sorry
Bloodgain

1
Più in generale: non c'è nulla che ti impedisca di riutilizzare un nome di variabile in qualsiasi ambito annidato. Tranne, ovviamente, la paura della punizione di Dio per aver scritto un codice confuso.
Isaac Rabinovitch

56

Innanzitutto, questo è assolutamente legale: il codice verrà compilato ed eseguito, ripetendo il corpo del ciclo annidato 10 × 10 = 100 volte. Il contatore del ciclo iall'interno del ciclo annidato nasconderà il contatore del ciclo esterno, quindi i due contatori verranno incrementati indipendentemente l'uno dall'altro.

Poiché l'esterno iè nascosto, il codice all'interno del corpo del ciclo annidato avrebbe accesso solo al valore di idel ciclo annidato, non idal ciclo esterno. In situazioni in cui il ciclo annidato non necessita dell'accesso al icodice esterno, tale codice potrebbe essere perfettamente giustificabile. Tuttavia, è probabile che questo crei più confusione nei suoi lettori, quindi è una buona idea evitare di scrivere tale codice per evitare "responsabilità di manutenzione".

Nota: anche se le variabili contatore di entrambi i cicli hanno lo stesso identificatore i, rimangono due variabili indipendenti, ovvero non si utilizza la stessa variabile in entrambi i cicli. È anche possibile utilizzare la stessa variabile in entrambi i cicli, ma il codice sarebbe difficile da leggere. Ecco un esempio:

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

Ora entrambi i cicli usano la stessa variabile. Tuttavia, ci vuole un po 'per capire cosa fa questo codice senza compilarlo ( demo );


4
Poiché la domanda è formulata come "utilizzo della stessa variabile contatore", vorrei anche sottolineare che l'ombreggiatura avviene solo quando si verifica la ridefinizione. Omettendo il intciclo for interno, cioè utilizzando effettivamente la stessa variabile contatore, il ciclo esterno verrà eseguito solo una volta, poiché il ciclo interno se ne andrà i == 10. Questo è banale, ma penso che fornisca chiarimenti dato come è stata formulata la domanda
Easton Bornemeier,

@EastonBornemeier Hai ragione, ho pensato di dover affrontare la questione della "stessa variabile" nel corpo della risposta. Grazie!
dasblinkenlight

@EricPostpischil "Variable shadowing" è un termine ufficiale, completo di una propria pagina su wikipedia . Tuttavia, ho aggiornato la risposta per renderla coerente con la formulazione dello standard. Grazie!
dasblinkenlight

2
@dasblinkenlight: In realtà, ho avuto uno spasmo cerebrale sulla direzione e il nome interno ombreggia il nome esterno. Il mio precedente commento era sbagliato in questo senso. Mie scuse. (Tuttavia, questo è in un senso inglese, non in un senso ufficiale: Wikipedia non è una pubblicazione ufficiale per C o programmazione in generale, e non sono a conoscenza di alcun ufficio o ente autorevole che definisce il termine.) Lo standard C usa "Nascondi", quindi è preferibile.
Eric Postpischil

Bello, soprattutto con l'esempio della "stessa variabile". Tuttavia, penso che " il codice verrà compilato ed eseguito come previsto " sarebbe meglio in quanto "il codice verrà compilato ed eseguito come qualcuno che lo ha letto attentamente e comprende tutte le ramificazioni previste" ... come dici tu, codice come questo " è probabile che crei più confusione nei suoi lettori "e il problema è che un lettore confuso potrebbe aspettarsi qualcosa di diverso da quello che fa.
TripeHound

26

Puoi. Ma dovresti essere consapevole dell'ambito di is. se chiamiamo l'esterno icon i_1e l'interno icon i_2, lo scopo della is è il seguente:

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
}

Dovresti notare che non si influenzano a vicenda e solo il loro ambito di definizione è diverso.


17

È del tutto possibile, ma tieni presente che non sarai in grado di affrontare il primo i dichiarato

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

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

    }
}

nel secondo ciclo all'interno del secondo ciclo figlio

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

se è necessario regolare o ottenere il valore del primo i, utilizzare j nel secondo ciclo

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

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

    }
}

e se sei abbastanza creativo puoi farli entrambi in un ciclo

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

6
Se rilevassi le variabili i shadow in cicli annidati durante una revisione del codice, la vedrei come un'opportunità di coaching. Se beccassi qualcuno che offusca il loop interno come il tuo ultimo esempio (che NON è un loop), potrei lanciarlo fuori da una finestra.
Bloodgain

è un ciclo, ne ha solo un ciclo for , se fosse 2 ne avrebbe due parole chiave o due parole chiave while o una parola chiave for e while
Dodo

3
Ecco perché ho detto che hai offuscato il loop. Stai ancora ripetendo il ciclo, l'hai appena nascosto con una sintassi meno ovvia. Ed è peggio in ogni modo per questo.
Bloodgain

12

Sì, puoi usare lo stesso nome della variabile contatore per un forciclo interno come per il forciclo esterno .

Dal ciclo for :

for ( init_clause ; cond_expression ; iteration_expression ) loop_statement
L'istruzione dell'espressione usata come loop_statement stabilisce il proprio ambito di blocco, distinto dall'ambito di init_clause .

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

L'ambito di loop_statement è annidato nell'ambito di init_clause .

Dagli standard C # 6.8.5p5 Dichiarazioni di iterazione [enfasi mia]

Un'istruzione di iterazione è un blocco il cui ambito è un sottoinsieme rigoroso dell'ambito del blocco che lo racchiude. Il corpo del ciclo è anche un blocco il cui ambito è un sottoinsieme rigoroso dell'ambito dell'istruzione di iterazione .

Dagli standard C # 6.2.1p4 Scopo degli identificatori [enfasi mia]

.... All'interno dell'ambito interno, l'identificatore designa l'entità dichiarata nell'ambito interno; l' entità dichiarata nell'ambito esterno è nascosta (e non visibile) nell'ambito interno.


10

Dal punto di vista del codice / compilatore questa sarebbe una cosa perfettamente valida e legale da fare. Il int idichiarato nel for(int i = 0; i < 10; i++)ciclo interno è in uno scope nuovo e più piccolo, in modo che la dichiarazione offuschi la dichiarazione di int inel ciclo esterno (o, con altre parole: Nell'ambito interno tutti gli accessi alla variabile ivanno al int idichiarato nell'ambito interno, lasciando int iintatto il campo di applicazione esterno).

Detto questo, dal punto di vista della qualità del codice questo è assolutamente orribile. È difficile da leggere, difficile da capire e facile da fraintendere. Non farlo.


8

Sì, puoi usarlo ma è abbastanza confuso. La cosa più importante è l'ambito della variabile locale all'interno del ciclo. Per quanto riguarda la dichiarazione di una variabile all'interno di una funzione, l'ambito di tale variabile è quella funzione.

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

Analogamente, nel caso dei cicli, le variabili dichiarate all'interno del ciclo interno hanno un ambito diverso e il ciclo esterno dichiarato variabile ha un ambito diverso.

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
}

L'approccio migliore consiste nell'usare variabili diverse per i cicli interni ed esterni.

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

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

    }

}

8

Sì, sicuramente puoi usare la stessa variabile del nome.

Le variabili di programmazione C possono essere dichiarate in tre posizioni:
variabili locali: -Interno una funzione o un blocco.
Variabili globali: -Su tutte le funzioni.
Parametri formali: -Nei parametri di funzione.

Ma nel tuo caso i scopedovrà badare di seguito alle cose

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
}

Nota: sarebbe buona norma utilizzare variabili diverse per i cicli interni ed esterni


6

Sì, e ancora più interessante è che puoi riutilizzare un nome di variabile ogni volta che apri una serie di parentesi graffe. Questo è spesso utile quando si inserisce il codice diagnostico. Digita una parentesi graffa aperta "{" seguita dalla dichiarazione e dall'uso delle variabili, quindi chiudi la parentesi graffa e le variabili scompaiono. Ciò garantisce che non interferirai con nulla nel corpo principale pur mantenendo il vantaggio di eventuali variabili, classi e metodi dichiarati al di fuori delle parentesi graffe.


3

Regola dell'ambito: una variabile dichiarata in un'istruzione for può essere utilizzata solo in tale istruzione e nel corpo del ciclo.

Se nel codice sono state definite più istanze di i nei cicli interni, ciascuna istanza occuperà il proprio spazio di memoria. Quindi non c'è nulla di cui preoccuparsi per i risultati comunque sarebbe lo stesso.

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

Ma non è consigliabile utilizzare lo stesso nome di variabile poiché è difficile da capire e diventa codice non gestibile in seguito.


1

La parte importante è che il parametro del ciclo interno contiene int i. Poiché iviene ridefinito in questo modo, le due variabili non si influenzano a vicenda; i loro scopi sono diversi. Ecco due esempi per dimostrarlo:

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

Si noti che il codice precedente include int inel parametro del ciclo interno e il codice seguente include solo i.

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

0

Bene, puoi farlo senza che i tuoi script abbiano problemi, ma dovresti evitare quella struttura. Di solito porta a confusione

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.