Bir 'for' döngüsünün içindeki bir 'for' döngüsü aynı sayaç değişkeni adını kullanabilir mi?


107

Bir fordöngünün içindeki döngü için aynı sayaç değişkenini kullanabilir miyim for?

Yoksa değişkenler birbirini mi etkiler? Aşağıdaki kod, ikinci döngü için farklı bir değişken kullanmalı mı j, yoksa iiyi mi?

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

72
Kafa karıştırıcı - bir kod incelemesinde beni geçemez. Ama meşru. Her ikisi de adlandırılan i, farklı kapsamlara sahip iki farklı değişken vardır . Bu -Wshadowtür sorunların otomatik olarak bildirilmesi için GCC ile birlikte kullanın .
Jonathan Leffler

15
Buna -Wshadowdahil olmadığına şaşırdım -Wall.
leftaroundabout

5
@leftaroundabout -Wshadow, küresel değişkenlerin gölgelenmesi konusunda da uyarıyor, bu da daha büyük projelerde kolayca can sıkıcı olabilir.
Kübik

9
@leftaroundabout daha da şaşırtıcı bir şekilde, -Wextraiçermiyor bile -Wshadow. Sanırım bazı projelerde yeterince yaygındır veya bazı gcc geliştiricileri, bu şekilde dışlanmayı garanti etmek için bir kodlama stili olarak gölgelendirmeyi sever.
hyde

5
@leftaroundabout Cubic'in söylediklerini yankılamak, -Wshadowkorkunç bir yanlış pozitif oranına sahip ve tamamen işe yaramaz hale getiriyor. Kapsam bir sebebi var ve gölgeleme olan önsel sorunlu değil. Şimdi -Wshadow-local(not: değil -Wshadow=local ) çok farklı. Ancak ne yazık ki GCC şimdiye kadar onu bagaja dahil etmeyi reddetti (ancak GCC'nin çatalları onu içeriyor gibi görünüyor).
Konrad Rudolph

Yanıtlar:


140

Aynı adı (tanımlayıcı) kullanabilirsiniz. Farklı bir nesne olacak. Birbirlerini etkilemeyecekler. İç döngünün içinde, dış döngüde kullanılan nesneye atıfta bulunmanın bir yolu yoktur (bunun için bir işaretçi sağlamak gibi özel hükümler belirlemediğiniz sürece).

Bu genellikle kötü bir tarzdır, kafa karışıklığına meyillidir ve bundan kaçınılmalıdır.

Nesneler, yalnızca int isizin gösterdiğiniz gibi, iç kısım ayrı olarak tanımlanırsa farklıdır . Yeni bir nesne tanımlanmadan aynı isim kullanılırsa, döngüler aynı nesneyi kullanacak ve birbirlerini engelleyecektir.


3
for (i) ve for (j) yuvalanmış ve i ++ içinde kullanılması, dış döngü değişkenini artıracaktır. Bununla birlikte, her iki döngüde de aynı tanımlayıcıyı kullanırsanız, söyledikleriniz doğrudur, çünkü bunlar farklı kapsamlı değişkenlerdir.
KYL3R

3
@BloodGain: "Object" C standardında kullanılan teknik bir terimdir. Burada bilinçli olarak kullandım.
Eric Postpischil

1
@EricPostpischil: Ah, anlıyorum, evet. Standarttaki bu tanımın farkında değildim ve yeni programcılar için yanıltıcı olacağından korkuyordum (çünkü bu çok açık bir başlangıç ​​sorusu), çünkü C terimi genel olarak kullandığımız anlamda "nesnelere" sahip değil. Bunu C11 standardında görüyorum ve şimdi C11'den önce bu şekilde tanımlanıp tanımlanmadığını merak ediyorum.
Bloodgain

1
Öyleydi. C99 standardında 3.15 yerine 3.14'tür. Yani benim açımdan bahane yok. Bu bana sizi sorgulamayı öğretecek <: - |
Bloodgain

1
Daha genel olarak: herhangi bir iç içe geçmiş kapsamda bir değişken adını yeniden kullanmanızı engelleyecek hiçbir şey yoktur. Elbette, kafa karıştırıcı şifreler yazdığı için Tanrı'nın Cezası korkusu dışında.
Isaac Rabinovitch

56

Birincisi, bu kesinlikle yasaldır: Kod derlenecek ve çalıştırılacak, iç içe geçmiş döngünün gövdesini 10 × 10 = 100 kez tekrarlayacaktır. Döngü karşı iiç içe döngü içinde olacaktır gizlemek iki sayaçları, birbirinden bağımsız olarak artan bu yüzden, dış döngü sayacı.

Dış ikısım gizli olduğundan, iç içe geçmiş döngünün gövdesindeki kod , dış döngüden ideğil i, yalnızca iç içe döngünün değerine erişebilir . İç içe geçmiş döngünün dışarıya erişime ihtiyaç duymadığı durumlarda, bu itür bir kod mükemmel bir şekilde gerekçelendirilebilir. Ancak, bu muhtemelen okuyucularda daha fazla kafa karışıklığı yaratacaktır, bu nedenle "bakım yükümlülüklerinden" kaçınmak için bu tür bir kod yazmaktan kaçınmak iyi bir fikirdir.

Not: Her iki döngü sayacı değişkenleri aynı tanımlayıcıyı olsa bile i, bunlar, iki bağımsız değişkenleri kalır sen edilir yani değil her iki döngüler aynı değişkeni kullanarak. Her iki döngüde de aynı değişkeni kullanmak mümkündür, ancak kodun okunması zor olacaktır. İşte bir örnek:

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

Şimdi her iki döngü de aynı değişkeni kullanıyor. Ancak, bu kodun onu derlemeden ne yaptığını anlamak biraz zaman alır ( demo );


4
Soru "aynı sayaç değişkeni kullanıyor" olarak ifade edildiğinden, gölgelendirmenin yalnızca yeniden tanımlama gerçekleştiğinde gerçekleştiğini de belirtmek isterim. intİçteki for döngüsünü atlamak , yani aslında aynı sayaç değişkenini kullanmak, iç döngü terk edeceği için dış döngünün yalnızca bir kez çalışmasına neden olur i == 10. Bu önemsiz bir şey, ancak sorunun nasıl ifade edildiğine bakıldığında netlik sağladığını düşündü
Easton Bornemeier

@EastonBornemeier Haklısınız, cevabın gövdesinde "aynı değişken" konusuna değinmem gerektiğini düşündüm. Teşekkür ederim!
dasblinkenlight

@EricPostpischil "Değişken gölgeleme" wikipedia'da kendi sayfasıyla tamamlanan resmi bir terimdir . Yine de, standardın ifadesiyle tutarlı olması için yanıtı güncelledim. Teşekkür ederim!
dasblinkenlight

2
@dasblinkenlight: Aslında, yön konusunda beyin spazmım vardı ve iç ad dış adı gölgeliyor. Önceki yorumum bu bakımdan yanlıştı. Özür dilerim. (Bununla birlikte, bu İngilizce anlamındadır, resmi anlamda değildir - Wikipedia, C veya genel olarak programlama için resmi bir yayın değildir ve terimi tanımlayan herhangi bir ofis veya yetkili kurumdan haberdar değilim.) C standardı kullanır. "Sakla", böylece tercih edilir.
Eric Postpischil

Güzel, özellikle "aynı değişken" örneğiyle. Bununla birlikte, bence " kod, beklendiği gibi derlenecek ve çalışacak ", çünkü "kod, onu dikkatlice okuyan ve beklenen tüm sonuçları anlayan biri olarak derlenecek ve çalışacaktır " ... dediğiniz gibi, böyle kodlayın " okuyucularında daha fazla kafa karışıklığı yaratması muhtemeldir "ve sorun şu ki, kafası karışmış bir okuyucu yaptıklarından başka bir şey bekleyebilir .
TripeHound

26

Yapabilirsin. Ancak e-postaların kapsamının farkında olmalısınız i. dış iile i_1ve iç iile çağırırsak, s'nin i_2kapsamı iaşağıdaki gibidir:

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
}

Birbirlerini etkilemediklerini ve sadece tanımlarının farklı olduğunu fark etmelisiniz.


17

Bu tamamen mümkündür, ancak aklınızda bulundurun, ilk beyan edilen i ele alamayacaksınız.

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

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

    }
}

ikinci çocuk döngü içindeki ikinci döngüde

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

İlk i'nin değerini ayarlamanız veya almanız gerekiyorsa, ikinci döngüde j'yi kullanın

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

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

    }
}

ve yeterince yaratıcılığınız varsa, ikisini de tek bir döngüde yapabilirsiniz.

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

6
Bir kod incelemesi sırasında iç içe geçmiş döngülerde gölgeli i değişkenleri yakalasaydım, bunu bir koçluk fırsatı olarak görürdüm. Son örneğiniz gibi (bu bir döngü DEĞİLDİR) iç döngüyü gizleyen birini yakaladıysam, onu bir pencereden atabilirim.
Bloodgain

bir döngüdür, yalnızca bir for döngüsü vardır, 2 olsaydı anahtar kelimeler için iki veya iki while anahtar sözcükleri veya bir for ve while anahtar sözcükleri
Dodo

3
Bu yüzden döngüyü karıştırdığını söyledim . Hala döngü halindesiniz, daha az belirgin sözdizimi ile gizlediniz. Ve bunun için her şekilde daha kötü.
Bloodgain

12

Evet, bir iç fordöngü için dış fordöngü için aynı sayaç değişkeni adını kullanabilirsiniz .

Gönderen döngü için :

for ( init_clause ; cond_expression ; iteration_expression ) loop_statement
Olarak kullanılan onermesinin loop_statement kendi kurar kapsamından ayrı blok kapsamını, bir init_clause .

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

Kapsamı loop_statement olduğu iç içe kapsamında init_clause .

C Standartlarından # 6.8.5p5 Yineleme ifadeleri [vurgu benim]

Yineleme ifadesi, kapsamı, çevreleyen bloğunun kapsamının kesin bir alt kümesi olan bir bloktur. Döngü gövdesi ayrıca, kapsamı yineleme ifadesinin kapsamının kesin bir alt kümesi olan bir bloktur .

C Standartlarından # 6.2.1p4 Tanımlayıcıların kapsamları [vurgu benim]

.... İç kapsamda, tanımlayıcı iç kapsamda bildirilen varlığı belirtir; Dış kapsamda bildirilen işletme gizli iç kapsamında (ve görülemez).


10

Bir kod / derleyici perspektifinden bakıldığında, bu tamamen geçerli ve yapılması gereken yasal bir şey olacaktır. int iİç bildirilen for(int i = 0; i < 10; i++)döngü, yeni ve daha küçük kapsamda olduğunu beyan böylece gölgeler beyanı int ibaşka deyişle dış döngüde (veya,: değişken iç kapsamında tüm erişimler iiçin halindeyken int iiç kapsamda bildirilen, int idış kapsamda el değmeden bırakılır ).

Bununla birlikte, kod kalitesi perspektifinden bakıldığında bu tamamen korkunç. Okuması zor, anlaşılması zor ve yanlış anlaşılması kolay. Yapma.


8

Evet, kullanabilirsiniz ama oldukça kafa karıştırıcı. En önemli şey, döngü içindeki yerel değişkenin kapsamıdır. Bir fonksiyonun içinde bir değişken bildirilmişse, o değişkenin kapsamı o fonksiyondur.

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

Benzer şekilde, döngüleri olan durum, iç döngü içinde bildirilen değişken farklı kapsama sahiptir ve dış döngüde bildirilen değişken farklı kapsama sahiptir.

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
}

Daha iyi yaklaşım, iç ve dış döngüler için farklı değişkenler kullanmaktır.

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

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

    }

}

8

Evet, kesinlikle aynı isim değişkenini kullanabilirsiniz.

C programlama değişkenleri üç yerde tanımlanabilir:
yerel değişkenler: -Bir fonksiyon veya blok içinde.
Global değişkenler: -Tüm fonksiyonların dışında.
Biçimsel parametreler: -Fonksiyon parametrelerinde.

Ama senin durumunda i scopeaşağıda düşünmek zorunda kalacak şeyler

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
}

Not: İç ve dış döngüler için farklı değişkenler kullanmak en iyi uygulama olacaktır.


6

Evet - ve daha da ilginci, bir küme ayraç kümesini her açışınızda bir değişken adını yeniden kullanabilirsiniz. Bu genellikle teşhis kodunu eklerken kullanışlıdır. Açık bir küme ayracı '{' ve ardından değişkenlerin bildirimi ve kullanımı yazın, ardından küme ayracı kapatın ve değişkenler kaybolur. Bu, parantezlerin dışında bildirilen değişkenlerin, sınıfların ve yöntemlerin avantajını korurken ana gövdede hiçbir şeye müdahale etmemenizi garanti eder.


3

Kapsam Kuralı: Bir for deyiminde bildirilen bir değişken yalnızca bu ifadede ve döngünün gövdesinde kullanılabilir.

Kodunuzda, iç döngülerde birden fazla i örneği tanımladıysanız, her örnek kendi bellek alanını kaplayacaktır. Yani sonuçlar hakkında endişelenecek bir şey yok, yine de aynı olacaktır.

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

Ancak anlaşılması zor olduğundan ve daha sonra bakımı yapılamayan kod haline geldiğinden aynı değişken adının kullanılması önerilmez.


1

Önemli olan kısım, iç döngü parametresinin içermesidir int i. Çünkü ibu şekilde yeniden tanımlandı, iki değişken birbirini etkilemez; kapsamları farklı. İşte bunu gösterecek iki örnek:

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

Yukarıdaki kodun int iiç döngü parametresini içerdiğini ve aşağıdaki kodun yalnızca içerdiğini unutmayın i.

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

0

Bunu komut dosyalarınızda sorun yaşamadan yapabilirsiniz, ancak bu yapıdan kaçınmalısınız. Genellikle kafa karışıklığına yol açar

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.