Mam taki prosty program:
#include <stdio.h>
struct S
{
int i;
};
void swap(struct S *a, struct S *b)
{
struct S temp;
temp = *a /* Oops, missing a semicolon here... */
*a = *b;
*b = temp;
}
int main(void)
{
struct S a = { 1 };
struct S b = { 2 };
swap(&a, &b);
}
Jak widać na przykład na ideone.com, powoduje to błąd:
prog.c: In function 'swap': prog.c:12:5: error: invalid operands to binary * (have 'struct S' and 'struct S *') *a = *b; ^
Dlaczego kompilator nie wykrywa brakującego średnika?
Uwaga: to pytanie i odpowiedź na nie są motywowane tym pytaniem . Chociaż są inne pytania podobne do tego, nie znalazłem nic wspominającego o swobodnej pojemności języka C, która jest przyczyną tego i powiązanych błędów.
Odpowiedzi:
C jest językiem o dowolnej formie . Oznacza to, że możesz go sformatować na wiele sposobów i nadal będzie to legalny program.
Na przykład stwierdzenie takie jak
można napisać jak
lub jak
Więc kiedy kompilator zobaczy linie
myśli, że to znaczy
Nie jest to oczywiście prawidłowe wyrażenie i kompilator będzie narzekał na to zamiast na brakujący średnik. Przyczyną niepoprawności jest to, że
a
jest wskaźnikiem do struktury, więc*a * a
próbuje pomnożyć instancję struktury (*a
) za pomocą wskaźnika do struktury (a
).Chociaż kompilator nie może wykryć brakującego średnika, zgłasza również całkowicie niezwiązany błąd w niewłaściwym wierszu. Jest to ważne, aby zauważyć, ponieważ bez względu na to, jak bardzo patrzysz na wiersz, w którym zgłoszony jest błąd, nie ma tam żadnego błędu. Czasami takie problemy wymagają spojrzenia na poprzednie wiersze, aby sprawdzić, czy są w porządku i bez błędów.
Czasami trzeba nawet zajrzeć do innego pliku, aby znaleźć błąd. Na przykład, jeśli plik nagłówkowy definiuje strukturę jako ostatnią w pliku nagłówkowym i brakuje średnika kończącego strukturę, to błąd nie będzie w pliku nagłówkowym, ale w pliku, który zawiera plik nagłówkowy.
A czasami jest jeszcze gorzej: jeśli dołączysz dwa (lub więcej) pliki nagłówkowe, a pierwszy zawiera niepełną deklarację, najprawdopodobniej błąd składni zostanie wskazany w drugim pliku nagłówkowym.
Wiąże się z tym pojęcie dalszych błędów. Niektóre błędy, zazwyczaj spowodowane brakującymi średnikami, są zgłaszane jako błędy wielokrotne . Dlatego ważne jest, aby przy naprawianiu błędów zaczynać od góry, ponieważ naprawienie pierwszego błędu może spowodować zniknięcie wielu błędów.
Może to oczywiście prowadzić do naprawiania jednego błędu na raz i częstych ponownych kompilacji, co może być kłopotliwe w przypadku dużych projektów. Rozpoznawanie takich błędów uzupełniających jest jednak czymś, co wiąże się z doświadczeniem, a po kilkukrotnym ich zobaczeniu łatwiej jest odszukać prawdziwe błędy i naprawić więcej niż jeden błąd na rekompilację.
źródło
temp = *a * a = *b
mogłoby to być prawidłowe wyrażenie, gdybyoperator*
zostało przeciążone. (Pytanie jest jednak oznaczone jako „C”).Należy pamiętać o trzech rzeczach.
*
w C może być operatorem jednoargumentowym i binarnym. Jako operator jednoargumentowy oznacza „wyłuskiwanie”, jako operator binarny oznacza „mnożenie”.Wynikiem tych dwóch faktów jest analiza.
Pierwsza i ostatnia
*
są interpretowane jako jednoargumentowe, ale druga*
jest interpretowana jako binarna. Z punktu widzenia składni wygląda to OK.Błąd jest widoczny dopiero po przeanalizowaniu, gdy kompilator próbuje zinterpretować operatory w kontekście ich typów operandów.
źródło
Kilka dobrych odpowiedzi powyżej, ale opiszę je szczegółowo.
W rzeczywistości jest to przypadek, w
x = y = z;
którym obax
iy
mają przypisaną wartośćz
.To, co mówisz, jest
the contents of address (a times a) become equal to the contents of b, as does temp
.Krótko mówiąc,
*a *a = <any integer value>
jest to ważne oświadczenie. Jak wcześniej wspomniano, pierwsza*
wyłuskuje wskaźnik, podczas gdy druga mnoży dwie wartości.źródło
y
nie jest nawet zmienną, jest to wyrażenie*a *a
i nie można przypisać wyniku mnożenia.Większość kompilatorów parsuje pliki źródłowe po kolei i zgłasza wiersz, w którym odkrywają, że coś jest nie tak. Pierwsze 12 wierszy twojego programu w C może być początkiem prawidłowego (wolnego od błędów) programu w C. Pierwsze 13 wierszy twojego programu nie może. Niektóre kompilatory zanotują lokalizację napotkanych rzeczy, które same w sobie nie są błędami iw większości przypadków nie będą powodować błędów w dalszej części kodu, ale mogą nie być poprawne w połączeniu z czymś innym. Na przykład:
Sama deklaracja
int foo;
byłaby w porządku. Podobnie deklaracjafloat foo;
. Niektóre kompilatory mogą zapisać numer linii, w której pojawiła się pierwsza deklaracja, i skojarzyć komunikat informacyjny z tą linią, aby pomóc programiście zidentyfikować przypadki, w których wcześniejsza definicja jest faktycznie błędna. Kompilatory mogą również przechowywać numery wierszy związane z czymś takim jak ado
, co może zostać zgłoszone, jeśli skojarzonywhile
nie pojawi się we właściwym miejscu. Jednak w przypadkach, w których prawdopodobna lokalizacja problemu znajdowałaby się bezpośrednio przed wierszem, w którym wykryto błąd, kompilatory generalnie nie zawracają sobie głowy dodawaniem dodatkowego raportu dla pozycji.źródło