Próbuję skompilować ten fragment kodu z książki „Język programowania C” (K&R). Jest to podstawowa wersja programu UNIX 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);
}
Otrzymuję następujący błąd:
$ gcc wc.c
wc.c: In function ‘main’:
wc.c:18: error: ‘else’ without a previous ‘if’
wc.c:18: error: expected ‘)’ before ‘;’ token
Drugie wydanie tej książki pochodzi z 1988 roku i jestem całkiem nowy w C. Może ma to coś wspólnego z wersją kompilatora, a może po prostu mówię bzdury.
Widziałem we współczesnym kodzie C inne użycie main
funkcji:
int main()
{
/* code */
return 0;
}
Czy to nowy standard, czy nadal mogę używać głównego typu bez typu?
|| c = '\t')
. Czy to wygląda tak samo jak inny kod w tej linii?Odpowiedzi:
Twój problem dotyczy definicji preprocesora
IN
iOUT
:Zwróć uwagę, że w każdym z nich znajduje się końcowy średnik. Gdy preprocesor je rozwinie, Twój kod będzie wyglądał mniej więcej tak:
Ten drugi średnik powoduje, że
else
nie ma poprzedniej wartościif
jako dopasowania, ponieważ nie używasz nawiasów klamrowych. Dlatego usuń średniki z definicji preprocesoraIN
iOUT
.Wyciągnięta tutaj lekcja jest taka, że instrukcje preprocesora nie muszą kończyć się średnikiem.
Zawsze powinieneś używać szelek!
W
else
powyższym kodzie nie ma zawieszania się - niejednoznaczności.źródło
Głównym problemem przy tego kodu jest to, że nie kod z K i R. Zawiera średniki po definicjach makr, których nie było w książce, co, jak zauważyli inni, zmienia znaczenie.
Z wyjątkiem sytuacji, gdy wprowadzasz zmiany w celu zrozumienia kodu, powinieneś zostawić to w spokoju, dopóki go nie zrozumiesz. Możesz bezpiecznie modyfikować tylko kod, który rozumiesz.
Prawdopodobnie była to tylko literówka z Twojej strony, ale ilustruje potrzebę zrozumienia i zwracania uwagi na szczegóły podczas programowania.
źródło
Po makrach nie powinno być średników,
i prawdopodobnie powinno być
źródło
;
to literówka, która nie wpłynęła na problem, co oznacza literówkę w Twoim pytaniu, a nie w kodzie, którego faktycznie użyłeś.Definicje IN i OUT powinny wyglądać następująco:
Problem był spowodowany średnikami! Wyjaśnienie jest proste: zarówno IN, jak i OUT są dyrektywami preprocesora, zasadniczo kompilator zamieni wszystkie wystąpienia IN na 1, a wszystkie wystąpienia OUT na 0 w kodzie źródłowym.
Ponieważ oryginalny kod miał średnik po 1 i 0, gdy IN i OUT zostały zastąpione w kodzie, dodatkowy średnik po numerze generował nieprawidłowy kod, na przykład ten wiersz:
Skończyło się tak:
Ale chciałeś tego:
Rozwiązanie: usuń średnik po liczbach w oryginalnej definicji.
źródło
Jak widać, wystąpił problem w makrach.
GCC ma opcję zatrzymania po wstępnym przetworzeniu. (-E) Ta opcja jest przydatna, aby zobaczyć wynik przetwarzania wstępnego. W rzeczywistości technika ta jest ważna, jeśli pracujesz z dużą bazą kodu w języku c / c ++. Zazwyczaj pliki makefile będą miały cel zatrzymania po wstępnym przetworzeniu.
Dla skróconej informacji: pytanie SO dotyczy opcji - Jak wyświetlić plik źródłowy C / C ++ po wstępnym przetworzeniu w programie Visual Studio? . Zaczyna się od vc ++, ale ma również opcje gcc wymienione poniżej .
źródło
Nie do końca problem, ale deklaracja
main()
też jest datowana, powinno być coś takiego.Kompilator przyjmie wartość zwracaną int dla funkcji bez jednej i jestem pewien, że kompilator / konsolidator obejdzie brak deklaracji dla argc / argv i brak wartości zwracanej, ale powinny tam być.
źródło
Spróbuj dodać wyraźne nawiasy klamrowe wokół bloków kodu. Styl K&R może być niejednoznaczny.
Spójrz na wiersz 18. Kompilator mówi ci, gdzie jest problem.
źródło
if
bloku później, jeśli zapomnisz dodać nawiasy klamrowe, ponieważ Twój blok zawiera teraz więcej niż jedną linię, debugowanie tego błędu może zająć trochę czasu ...if
klauzuli i „zapominać” o aktualizowaniu nawiasów klamrowych, to nie jesteś bardzo dobry programista.Prostym sposobem jest użycie nawiasów, takich jak {}, dla każdego
if
ielse
:źródło
Jak wskazywały inne odpowiedzi, problem znajduje się w
#define
średnikach. Aby zminimalizować te problemy, zawsze wolę definiować stałe liczbowe jakoconst int
:W ten sposób pozbywasz się wielu problemów i możliwych problemów. Ograniczają go tylko dwie rzeczy:
Twój kompilator musi obsługiwać
const
- co w 1988 roku generalnie nie było prawdą, ale teraz jest obsługiwany przez wszystkie powszechnie używane kompilatory. (AFAIKconst
jest „zapożyczony” z C ++.)Nie możesz użyć tych stałych w niektórych specjalnych miejscach, w których potrzebujesz stałej podobnej do łańcucha. Ale myślę, że twój program nie jest taki.
źródło
const int
nie można w C