Bu 1988 C kodunun nesi var?


94

Bu kod parçasını "The C Programming Language" (K & R) kitabından derlemeye çalışıyorum. UNIX programının basit bir sürümüdür wc:

#include <stdio.h>

#define IN   1;     /* inside a word */
#define OUT  0;     /* outside a word */

/* count lines, words and characters in input */
main()
{
    int c, nl, nw, nc, state;

    state = OUT;
    nl = nw = nc = 0;
    while ((c = getchar()) != EOF) {
        ++nc;
        if (c == '\n')
            ++nl;
        if (c == ' ' || c == '\n' || c == '\t')
            state = OUT;
        else if (state == OUT) {
            state = IN;
            ++nw;
        }
    }
    printf("%d %d %d\n", nl, nw, nc);
}

Ve şu hatayı alıyorum:

$ gcc wc.c 
wc.c: In function ‘main’:
wc.c:18: error: ‘else’ without a previous ‘if’
wc.c:18: error: expected ‘)’ before ‘;’ token

Bu kitabın 2. baskısı 1988'den ve C'ye oldukça yeniyim. Belki derleyici sürümüyle ilgili olabilir ya da belki sadece saçma konuşuyorum.

Modern C kodunda mainişlevin farklı bir kullanımını gördüm :

int main()
{
    /* code */
    return 0;
}

Bu yeni bir standart mı yoksa hala türsüz bir ana kullanabilir miyim?


4
Bir yanıt değil, daha yakından bakmak için başka bir kod parçası || c = '\t'). Bu, o satırdaki diğer kodla aynı görünüyor mu?
user7116

58
Hata ayıklama + yazım hatası sorusu için 32 olumlu oy ?!
Orbit'te Hafiflik Yarışları

37
@ TomalakGeret'kal: Biliyorsunuz, eski şeyler daha değerli (şarap, tablolar, C kodu)
Sergio Tulentsev

16
@ César: Fikrimi ifade etme hakkım var ve bunu sansürlemediğiniz için size teşekkür ederim. Olduğu gibi, evet, bu, başka hiç kimseye yardımcı olmayacak "yerelleştirilmiş" sorunlar olan kodunuzda hata ayıklamak ve yazım hatalarını çözmek için bir web sitesi değildir. Programlama dilleriyle ilgili sorular için bir web sitesidir , sizin için temel hata ayıklama ve referans çalışmalarınızı yapmak için değil. Beceri seviyesi tamamen alakasız. SSS'yi ve belki de bu meta soruyu okuyun .
Orbit'te Hafiflik Yarışları

11
@ TomalakGeret'kal tabii ki fikrinizi ifade edebilirsiniz ve ben yapıcı olmamasına rağmen yorumunuzu sansürlemeyeceğim. SSS'yi zaten okudum. Ben olduğum meraklısı programcı bir soran karşılaştığım o fiili soruna
César

Yanıtlar:


247

Sorununuz, önişlemci tanımlarınızla INve OUT:

#define IN   1;     /* inside a word */
#define OUT  0;     /* outside a word */

Bunların her birinde nasıl bir noktalı virgül olduğuna dikkat edin. Önişlemci bunları genişlettiğinde, kodunuz kabaca şöyle görünecektir:

    if (c == ' ' || c == '\n' || c == '\t')
        state = 0;; /* <--PROBLEM #1 */
    else if (state == 0;) { /* <--PROBLEM #2 */
        state = 1;;

Bu ikinci noktalı virgül , ayraç kullanmadığınız için eşleşme olarak öncekinin elseolmamasına neden olur if. Yani, önişlemci tanımlamalardan noktalı virgül kaldırmak INve OUT.

Burada öğrenilen ders, önişlemci ifadelerinin noktalı virgülle bitmesi gerekmediğidir.

Ayrıca her zaman diş teli kullanmalısınız!

    if (c == ' ' || c == '\n' || c == '\t') {
        state = OUT;
    } else if (state == OUT) {
        state = IN;
        ++nw;
    }

elseYukarıdaki kodda asılı belirsizlik yoktur .


8
Netlik için, sorun boşluk değil, noktalı virgüllerdir. Önişlemci ifadelerinde bunlara ihtiyacınız yok.
Dan

@Dan açıklama için teşekkürler! Ve noktalı virgüller gerçekten de problemdi! Teşekkürler beyler!
César

2
@ César: rica ederim. Destek önerisi umarım gelecekte sizi beladan uzak tutar, kesinlikle bana yardımcı oldu!
user7116

5
@ César: Makroların etrafına parantez koymaya alışmak da iyi bir fikir çünkü genellikle makronun ilk olarak değerlendirilmesini istiyorsun. Bu durumda, değer tek bir belirteç olduğu için önemli değildir, ancak parantezlerin dışarıda bırakılması, bir ifade tanımlanırken beklenmedik sonuçlara yol açabilir.
styfle

7
"onlara gerek yok"! = "onlara sahip olmamalı". ilki her zaman doğrudur; ikincisi bağlama bağlıdır ve bu senaryoda daha uygun bir konudur.
Orbit'te Hafiflik Yarışları

63

Bu kodla ilgili temel sorun , K & R'den gelen kod olmamasıdır . Diğerlerinin de belirttiği gibi anlamı değiştiren makro tanımlarından sonra, kitapta yer almayan noktalı virgül içerir.

Kodu anlamak için bir değişiklik yapmanın dışında, anlayana kadar onu yalnız bırakmalısınız. Yalnızca anladığınız kodu güvenli bir şekilde değiştirebilirsiniz.

Bu muhtemelen sizin açınızdan bir yazım hatasıydı, ancak programlama sırasında ayrıntıları anlama ve dikkat etme ihtiyacını gösteriyor.


9
Tavsiyeniz, programlamayı öğrenen biri için çok yapıcı değil. Kodu değiştirmek, programlamanın ayrıntılarını tam olarak nasıl anladığınızdır.
user7116

12
@sixlettervariables: Ve bunu yaparken, hangi değişiklikleri yaptığınızı bilmeli ve mümkün olduğunca az değişiklik yapmalısınız. OP, değişiklikleri kasıtlı olarak yapmış ve olabildiğince az değişiklik yapmış olsaydı, muhtemelen bu soruyu sormazdı, çünkü ona neler olup bittiğini açıkça görebilirdi. IN için makroyu hatasız bir şekilde ve sonra OUT için makroyu iki hatayla değiştirirdi, ikincisi eklediği noktalı virgülden şikayet ederdi.
jmoreno

5
Öyle görünüyor ki, bir önişlemci yönerge satırının sonuna noktalı virgül ekleme hatasını yapmazsanız, bunları eklemeyeceğinizi muhtemelen bilemezsiniz. Görünüşe göre değerlendirebilir, çok sayıda kod okuyabilir ve asla orada görünmediklerini fark edebilirsiniz. Veya OP onları dahil ederek batırabilir, "tuhaf" hatayı sorabilir ve şunu öğrenebilir: oops, önişlemci yönergeleri için noktalı virgül gerekmez! Bu programlama, Scared Straight'in bir bölümü değil.
user7116

14
@sixlettervariables: Evet, ancak kod çalışmadığında, ilk adım, "oh, tamam, o zaman C'nin mucidi tarafından bir kitapta yazılan koddan herhangi bir neden olmaksızın değiştirdiğim şey, muhtemelen sorun. O zaman bunu geri alacağım. "
Orbit'te Hafiflik Yarışları


34

Makrolardan sonra noktalı virgül olmamalıdır,

#define IN   1     /* inside a word */
#define OUT  0     /* outside a word */

ve muhtemelen olmalı

if (c == ' ' || c == '\n' || c == '\t')

Teşekkürler, sorun noktalı virgüldü. İkincisi bir yazım hatasıydı!
César

21
Bir dahaki sefere yapıştırın lütfen tam metin editörü doğrudan, kullanmak kod.
Orbit'te Hafiflik Yarışları

@ TomalakGeret'kal iyi yapmadım ve yapacağım ama nasıl buldun?
gün

1
@onemach: ;Sorunu etkilemeyen bir yazım hatası olduğunu söylediniz , bu da aslında kullandığınız kodda değil, sorunuzda bir yazım hatası anlamına gelir .
Orbit'te Hafiflik Yarışları

24

GİRİŞ ve ÇIKIŞ'ın tanımları şu şekilde görünmelidir:

#define IN   1     /* inside a word  */
#define OUT  0     /* outside a word */

Noktalı virgül soruna neden oluyordu! Açıklama basittir: Hem IN hem de OUT önişlemci direktifleridir, esasen derleyici tüm IN oluşumlarını 1 ile ve OUT'un tüm oluşumlarını kaynak kodunda 0 ile değiştirir.

Orijinal kod, 1 ve 0'dan sonra noktalı virgül içerdiğinden, kodda GİRİŞ ve ÇIKIŞ değiştirildiğinde, numaradan sonraki fazladan noktalı virgül geçersiz kod üretti, örneğin bu satır:

else if (state == OUT)

Böyle görünerek sona erdi:

else if (state == 0;)

Ama istediğin şuydu:

else if (state == 0)

Çözüm: Orijinal tanımdaki sayılardan sonraki noktalı virgülü kaldırın.


8

Gördüğünüz gibi makrolarda bir sorun vardı.

GCC, ön işlemeden sonra durdurma seçeneğine sahiptir . (-E) Bu seçenek, ön işlemenin sonucunu görmek için kullanışlıdır. Aslında, c / c ++ 'da büyük kod tabanı ile çalışıyorsanız, teknik önemli bir tekniktir. Tipik olarak makefiles, ön işlemeden sonra durdurulacak bir hedefe sahip olacaktır.

Hızlı başvuru için: SO sorusu seçenekleri kapsar - Visual Studio'da önişlemeden sonra bir C / C ++ kaynak dosyasını nasıl görebilirim? . Vc ++ ile başlar, ancak aşağıda belirtilen gcc seçeneklerine de sahiptir .


7

Tam olarak bir sorun değil ama beyanı main()da tarihli, böyle bir şey olmalı.

int main(int argc, char** argv) {
    ...
    return 0;
}

Derleyici, tek olmayan bir işlev için bir int dönüş değeri varsayacaktır ve derleyici / bağlayıcının argc / argv için bildirim eksikliği ve dönüş değeri eksikliği etrafında çalışacağına eminim, ancak orada olmaları gerekir.


3
Bu iyi bir kitap - bildiğim kadarıyla C ile ilgili iki değerli kitaptan biri. Yeni sürümlerin ANSI C uyumlu (muhtemelen C99 ANSI C öncesi) olduğundan oldukça eminim. C kitabının diğer bir değeri, Peter van der Linden'in yazdığı Uzman C Programlama Derin C Sırları'dır.
Bill

Öyle olduğunu hiç söylemedim. Ben basitçe, bugünkü işlerin yapılma şekline uygun hale getirmek için, esasın değiştirilmesi gerektiği şeklinde yorumlanmıştım.
Bill

4

Kod bloklarının etrafına açık parantez eklemeyi deneyin. K & R tarzı belirsiz olabilir.

18. satıra bakın. Derleyici size sorunun nerede olduğunu söylüyor.

    if (c == '\n') {
        ++nl;
    }
    if (c == ' ' || c == '\n' || c == '\t') { // You're missing an "=" here; should be "=="
        state = OUT;
    }
    else if (state == OUT) {
        state = IN;
        ++nw;
    }

2
Teşekkürler! Aslında, kod ikinci ifte parantezler olmadan çalıştı :)
César

5
+1. Sadece belirsiz değil, biraz tehlikeli. Daha ifsonra bloğunuza bir satır eklediğinizde (eğer) bloğunuz artık birden fazla satır olduğu için parantez eklemeyi unutursanız, bu hatayı ayıklamak biraz zaman alabilir ...
The111

8
@ The111 Asla, asla, bana olmadı. Hala bunun gerçek bir sorun olduğuna inanmıyorum. On yıldan fazla bir süredir korsesiz stili kullanıyorum, bir bloğun gövdesini genişletirken bir kez bile parantez eklemeyi unutmadım.
Konrad Rudolph

1
@ The111: Bu durumda, birkaç SO katılımcısı birkaç dakika sürdü: P Ve eğer bir cümleye ifadeler ekleyebilen ifve parantezleri güncellemeyi "unutabilen" bir programcıysanız , o zaman değilsiniz çok iyi bir programcı.
Orbit'te Hafiflik Yarışları

3

Basit bir yol, her biri için {} gibi parantez kullanmaktır ifve else:

if (c == '\n'){
    ++nl;
}
if (c == ' ' || c == '\n' || c == '\t')
{
    state = OUT;
}
else if (state == OUT) {
    state = IN;
    ++nw;
}

2

Diğer yanıtların da işaret ettiği gibi, sorun içinde #defineve noktalı virgüldür. Bu sorunları en aza indirmek için sayı sabitlerini her zaman şu şekilde tanımlamayı tercih ederim const int:

const int IN = 1;
const int OUT = 0;

Bu şekilde birçok sorundan ve olası sorundan kurtulursunuz. Sadece iki şeyle sınırlıdır:

  1. Derleyicinizin desteklemesi gerekiyor const- ki bu 1988'de genel olarak doğru değildi, ancak şimdi yaygın olarak kullanılan tüm derleyiciler tarafından destekleniyor. (AFAIK, constC ++ 'dan "ödünç alınmıştır".)

  2. Bu sabitleri dizge benzeri bir sabite ihtiyaç duyacağınız bazı özel yerlerde kullanamazsınız. Ama bence programınız öyle değil.


Tercih ettiğim bir alternatif numaralandırmalar - const intC'de kullanılamayan özel yerlerde (dizi bildirimleri gibi) kullanılabilirler
Michael Burr
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.