Menu

C'de For Döngüsü: Söz Dizimi, Örnekler ve Sık Yapılan Hatalar

C for döngüsüyle kodun nasıl tekrarlanacağı: üç parçalı başlık, yukarı ve aşağı sayma, sizeof numarasıyla dizileri dolaşma, iç içe döngüler, sonsuz döngüler ve herkesi yakalayan bir-eksik/bir-fazla ile işaretsiz tür hataları.

Bu sayfada çalıştırılabilir editörler var - düzenle, çalıştır ve sonucu anında gör.

Neden for Döngüsü

if ve switch bir dal seçip onu bir kez çalıştırır. Gerçek programların ise işleri tekrar tekrar yapması gerekir: her puanı yazdırmak, bir sayı listesini toplamak, bir ızgaranın on satırını çizmek, bir metindeki her karakteri denemek. for döngüsü, kodu bilinen sayıda tekrarlamak için C'nin işçi atıdır ve kontrolü sizde olan bir sayacı vardır.

Bir for döngüsünün ihtiyaç duyduğu her şey tek ve derli toplu bir başlıkta durur, böylece "kaç kez ve nasıl" sorusu tek satırda görünür.

Üç Parçalı Başlık

Bir for başlığında noktalı virgülle ayrılmış üç parça vardır: bir başlatıcı, bir koşul ve bir güncelleme.

for (başlatıcı; koşul; güncelleme) {
    // gövde - koşul sıfırdan farklı olduğu sürece çalışır
}

Bunlar belirli bir sırayla çalışır ve bu sırayı bilmek okuyacağınız her for döngüsünü açıklar:

  1. Başlatıcı her şeyden önce, bir kez çalışır.
  2. Koşul test edilir. Sıfırsa döngü hemen biter.
  3. Gövde çalışır.
  4. Güncelleme çalışır.
  5. Tekrar 2. adıma.

Yukarıdaki döngüyü bu listeyle izleyin: int i = 0 bir kez çalışır. 0 < 5 geçerlidir, dolayısıyla gövde i = 0 yazdırır, ardından i++ i'yi 1 yapar. Koşul yeniden test edilir ve böyle devam eder. i değeri 5 olduğunda test başarısız olur, gövde atlanır ve bitti yazdırılır. Gövde tam olarak beş kez çalıştı ve i, 0'dan 4'e kadar değerleri aldı.

Bundan doğrudan iki sonuç çıkar. Koşul ilk geçişten önce kontrol edilir, dolayısıyla koşulu baştan yanlış olan bir döngü sıfır kez çalışır - for (int i = 10; i < 5; i++) gövdeye hiç girmez. Ve güncelleme gövdeden sonra çalışır, yani i gövde boyunca eski değerini tutar.

Sayacı başlığın içinde bildirmek (int i = 0) C99'dan beri geçerlidir ve doğru varsayılandır: i o zaman yalnızca döngünün içinde var olur, kendisinden sonraki hiçbir şeyle çakışamaz ve bir fonksiyondaki her döngü ismi serbestçe yeniden kullanabilir.

Yukarı, Aşağı ve Adımlarla Sayma

Güncelleme i++ ile sınırlı değildir. Aşağı sayın, istediğiniz miktarda adım atın ya da her geçişte ikiye katlayın:

Koşulu güncellemeyle eşleştirin. Yukarı sayma < ya da <= ile gider; aşağı sayma > ya da >= ile. Bu eşleşmeyi yanlış kurmak, asla bitmeyen bir döngü yazmanın yoludur.

İkinci döngünün <= 10 kullandığına dikkat edin, çünkü 10 dahil etmek istediğimiz bir değerdir; ilk örnek ise < 5 kullandı, çünkü 5 dahil değildir. Bir-eksik/bir-fazla hataları tam olarak bu seçimde doğar, o yüzden bilinçli yapın: i < n, 0'dan başlayarak n kez çalışır; i <= n ise n + 1 kez.

Bir Dizi Üzerinde Döngü

Bir sayma döngüsünün en yaygın kullanımı, bir diziyi indeksle dolaşmaktır. Sayaç aynı zamanda okuduğunuz konum görevi görür.

Bundan alınacak iki şey var.

sizeof numarası. sizeof(scores) dizinin tamamının bayt cinsinden boyutudur, sizeof(scores[0]) ise bir elemanın boyutudur; dolayısıyla bölümleri eleman sayısını verir. Bir eleman eklediğinizde kendiliğinden uyum sağlar, sabit yazılmış bir 5 ise sağlamaz. Püf noktası - ve büyük bir püf noktası - yalnızca dizinin kendisi kapsamdayken işe yaramasıdır. Diziyi bir fonksiyona geçirdiğinizde göstericiye dönüşür, böylece oradaki sizeof bir göstericinin boyutunu verir. Bir fonksiyonun içinde uzunluğu her zaman ayrı bir parametre olarak alın.

Koşul i < n'dir, asla i <= n değil. Beş elemanlı bir dizinin geçerli indeksleri 0 ile 4 arasındadır. scores[5] okumak tanımsız davranıştır: çöp yazdırabilir, çökebilir ya da çalışıyormuş gibi görünürken başka bir şeyi sessizce bozabilir. C hiçbir sınır kontrolü yapmaz, dolayısıyla bu tamamen sizin sorumluluğunuzdadır.

Bir diziyi tersten dolaşmak da aynı fikrin tersidir:

n - 1 ile (son geçerli indeks) başlayın, 0 dahil olacak şekilde durun - bu yüzden > değil >=. Burada i'yi işaretli bir int olarak tutun; nedenini bir sonraki bölüm açıklıyor.

İç İçe Döngüler

Izgaralar, tablolar ya da her eleman çiftiyle çalışmak için bir for'u başka bir for'un içine koyun. İç döngü, dış döngünün her tek adımı için baştan sona çalışır.

Dış döngü bir row sabitler; iç döngü o satır için her col'u tarar; iç döngüden sonraki printf("\n") satırı bitirir. O satır sonunu iç döngünün içine taşıyın, tüm tablo tek sütuna dönüşür - denemeye değer bir şey, çünkü iç içe yapıyı somutlaştırır.

Sayaçlara farklı isimler verin. row/col ya da i/j uygundur; ikisi için de i kullanmak dıştaki sayacı gölgeler ve şaşırtıcı sonuçlar üretir. Maliyete de dikkat edin: bir n-döngüsünü başka bir n-döngüsünün içine koymak gövdeyi n * n kez çalıştırır, yani 1.000 yinelemelik iki döngü bir milyon geçiş demektir.

İşte gerçek bir iş yapan iç içe bir döngü - iç döngünün sınırının dış sayaca bağlı olduğu bir yıldız üçgeni:

break ve continue

İki anahtar kelime döngünün ortasında akışı değiştirir. break döngüden hemen çıkar; continue mevcut geçişin geri kalanını atlar ve güncellemeye atlar.

İlk döngü 7'yi bulduğu anda durur ve kalan 93 değeri hiç kontrol etmez. İkincisi, çift sayılar için yazdırmayı atlamak üzere continue kullanır; başlıktaki i++ yine de çalışır, dolayısıyla döngü ilerlemeye devam eder. İkisi hakkında çok daha fazlası break ve continue sayfasında, tek bir break'in yapamadığı iç içe bir döngüden çıkma dahil.

Sonsuz Döngüler ve Boş Parçalar

Başlığın üç parçası da isteğe bağlıdır. Koşulu çıkarın, kalıcı olarak doğru sayılır:

for (;;) {
    /* sonsuza dek çalışır - break ya da return ile çıkın */
}

for (;;) C'nin deyimsel sonsuz döngüsüdür ve noktalı virgüller yine de gereklidir. İçindeki bir şey break, return ya da exit yapabildiği sürece bu bir hata değildir - olay döngüleri ve "giriş geçerli olana dek sormaya devam et" döngüleri tam olarak böyle yazılır.

Diğer parçalar da atılabilir. Sayaç zaten varsa başlatıcıyı atlayın; gövde onu ilerletiyorsa güncellemeyi atlayın:

int i = 0;
for (; i < n; ) {
    /* ... */
    i += step;
}

Bu geçerlidir ama aslında olduğu while döngüsünden daha kötü okunur. Sayaç, sınırı ve adımı başlıkta bir arada duruyorsa for'u, durmuyorsa while döngüsünü tercih edin.

Başlıkta Virgül Operatörü

Başlatıcı ve güncelleme her biri virgülle ayrılmış birkaç ifade tutabilir; iki sayacı aynı anda böyle çalıştırırsınız:

int i = 0, j = len - 1 her iki sayacı bildirir, i++, j-- ikisini de ilerletir. Bu virgül operatörüdür; işlenenlerini sırayla, önce soldakini sonra sağdakini değerlendirir. Onu gerçekten paralel sayaçlar için kullanın; alakasız işleri başlığa tıkıştırmak yalnızca onları gizler.

Sık Yapılan Hatalar

Bir avuç tuzak, C'deki for döngüsü hatalarının çoğunu açıklar.

Bir-eksik/bir-fazla. Sıfır tabanlı bir dizide i <= n, sonun bir ötesindeki elemanı okur. "0'dan başlayarak n kez" için i < n kullanın.

Başlıktan sonra noktalı virgül. Bu derlenir ve neredeyse hiçbir zaman kastettiğiniz şey değildir:

/* HATA: döngü gövdesi boş deyimdir; printf bir kez çalışır */
for (int i = 0; i < 5; i++);
{
    printf("%d\n", i);
}

; gövde hâline gelir, dolayısıyla döngü hiçbir şey yapmadan beş kez döner ve alttaki süslü parantezler sıradan bir blok olarak bir kez çalışır. gcc -Wall bunun için uyarı verir.

Aşağı sayan işaretsiz sayaçlar. Bu bir sonsuz döngüdür:

/* HATA: işaretsiz bir değer asla < 0 olmaz */
for (size_t i = n - 1; i >= 0; i--) {
    process(a[i]);
}

İşaretsiz bir tür için i >= 0 her zaman doğrudur. i değeri 0 olduğunda i-- çok büyük bir pozitif sayıya sarar ve döngü, sahibi olmadığı belleğe doğru devam eder. Aşağı sayımlar için işaretli bir int kullanın ya da koşulu for (size_t i = n; i-- > 0; ) şeklinde yazın; bu tek adımda hem azaltır hem test eder ve sıfırda doğru biçimde durur.

Sayacı iki yerde birden değiştirmek. i'yi başlığın yanı sıra gövdede de değiştirmek yineleme sayısını öngörülemez kılar. Tek bir yer seçin.

Kayan noktalı sayaçlar. for (double x = 0.0; x != 1.0; x += 0.1) asla sonlanmayabilir, çünkü 0.1'in tam bir ikili gösterimi yoktur ve toplam 1.0'a isabet etmeden onu geçer. Tam sayıyla döngü kurun ve değeri içeride hesaplayın:

for (int i = 0; i <= 10; i++) {
    double x = i / 10.0;
    /* ... */
}

Döngü sırasında dizinin uzunluğunu değiştirmek. i < n koşulu n'i her geçişte yeniden okur; dolayısıyla gövde koleksiyonu küçültüyorsa, ikisinin birbirinden kopmasına izin vermek yerine indeksi bilinçli olarak ayarlayın.

Sıkça Sorulan Sorular

C'de for döngüsü nasıl yazılır?

Başlığa noktalı virgülle ayrılmış üç parça koyun - bir başlatıcı, bir koşul ve bir güncelleme: for (int i = 0; i < 5; i++) { printf("%d\n", i); }. Bu, gövdeyi i 0'dan 4'e kadar değerler alacak şekilde beş kez çalıştırır ve koşul yanlış olur olmaz durur.

C'de bir dizide nasıl dolaşırım?

Bir indeksi 0'dan uzunluğa kadar (uzunluk dahil değil) sayın: for (int i = 0; i < n; i++) { sum += a[i]; }. Aynı fonksiyon içinde bildirilmiş bir dizi için uzunluğu sizeof(a) / sizeof(a[0]) ile hesaplayabilirsiniz; dizi bir fonksiyona geçirildikten sonra bu numara işe yaramaz, o yüzden uzunluğu parametre olarak gönderin.

C for döngüm neden bir fazla çalışıyor?

Bu, bir-eksik/bir-fazla (off-by-one) hatasıdır. Boyutu n olan sıfır tabanlı bir dizide geçerli indeksler 0 ile n - 1 arasındadır, dolayısıyla i <= n fazladan bir yineleme çalıştırır ve dizinin sonunun ötesini okur - bu tanımsız davranıştır. Güvenli varsayılan i < n'dir.

C for döngüsünün içinde döngü değişkeni bildirilebilir mi?

Evet, C99'dan beri: for (int i = 0; i < n; i++). Değişken o zaman yalnızca döngünün içinde var olur, ki istediğiniz de budur. gcc -std=c99 ya da daha yenisiyle derleyin (modern GCC ve clang varsayılan olarak C17 kullanır, yani doğrudan çalışır); eski C89 tarzı i'yi döngüden önce bildirirdi.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA