Początkujący programiści sfrustrowani brakiem słownika błędów kompilatora

66

Przyjaciel mojej rodziny poprosił mnie o trochę pomocy, gdy uczy się programować (w języku C). Gdy rozmawialiśmy, wyraził frustrację z powodu trudności w zrozumieniu komunikatów o błędach, które wysyła mu jego kompilator (GCC), gdy popełnia błędy. Nie rozumie wszystkich użytych terminów, a czasem ich kombinacja jest poza jego zrozumieniem. Pytał mnie: „Dlaczego dokumentacja kompilatora nie zawiera dłuższych wyjaśnień komunikatów o błędach?” - i nie miałem dla niego dobrej odpowiedzi.

Ja sam - jako bardziej doświadczony programista - bardzo rzadko jestem w takiej sytuacji, ale zdarzają się te rzadkie zdarzenia - jakiś egzotyczny komunikat o błędzie, z którym się wcześniej nie spotkałem. Udaje mi się znaleźć komunikat o błędzie w wyszukiwarce, ale najwyraźniej nie zawsze to dla niego działa - zwłaszcza, że ​​napotkane błędy są częstsze i występują w wielu różnych przypadkach, z którymi ma problemy związane z jego posiadać.

Jak więc początkujący programista powinien podjąć wyzwanie zrozumienia komunikatów o błędach kompilatora? W szczególności z kombinacją C i GCC?

einpoklum
źródło
7
„Jak więc początkujący programista powinien podjąć wyzwanie zrozumienia komunikatów o błędach kompilatora?” / sarkazm Pierwszą potrzebną umiejętnością jest umiejętność czytania każdego bitu z komunikatu kompilatora, w tym powiązania go z samym kontekstem. sarkazm wyłączony. Rzadko okazuje się, że jest to wada lub błąd w kompilatorze.
πάντα ῥεῖ
10
@MasonWheeler: Nowicjusz często nie wybiera kompilatora, który ma być używany podczas szkolenia. A GCC jest wspólnym mianownikiem wielu, wielu systemów ...
einpoklum
24
Jeśli chodzi o błędy szablonu GCC C ++, stwierdzam, że jeśli przestanę czytać po „Błędzie <plik: linia>” i przestudiuję pliki źródłowe, znajdę błąd szybciej, z dodatkowym efektem ubocznym utrzymania mojego rozsądku, niż jeśli przeczytam rzeczywisty błąd podany przez GCC .....
mattnz
18
Rozwiązanie jest oczywiste: użyj kompilatora z mniej mylącymi danymi wyjściowymi. Sugeruję rmcc . Drukuje się Yes.lub w No.zależności od tego, czy kod został skompilowany, czy nie. Natychmiast usuwa frustrację z powodu niezrozumienia długich i bezcelowych wiadomości!
rura
21
C nie jest dobrym językiem dla początkujących - i natknąłeś się na jeden z powodów. To powiedziawszy, Clang oferuje znacznie lepsze błędy, które mogą być bardziej atrakcyjne dla początkujących.
Theodoros Chatzigiannakis

Odpowiedzi:

164

Kilka przydatnych technik:

  • Włącz -Walli -Werror. Może to wydawać się sprzeczne z intuicją, gdy masz problemy z odszyfrowaniem komunikatów o błędach, aby utworzyć jeszcze więcej komunikatów o błędach, ale ostrzeżenia są zwykle łatwiejsze do zrozumienia i bliższe rzeczywistemu źródłu problemu, a ich zignorowanie może prowadzić do błędów, które są trudne do zrozumienia .
  • Spróbuj naprawić pierwszy błąd na liście. Często błędy nakładają się na siebie, co powoduje, że późniejsze komunikaty o błędach nie są tak naprawdę rzeczywistymi błędami. Napraw jeden i skompiluj ponownie. Będziesz lepszy w naprawianiu wielu komunikatów o błędach, gdy zdobędziesz więcej doświadczenia.
  • Użyj najnowszej możliwej wersji kompilatora. C jest niezwykle stabilnym językiem. Dlatego ogromna część ulepszeń w nowszych kompilatorach nie polega na dodawaniu funkcji językowych, ale na poprawianiu wygody programisty, w tym na lepszych komunikatach o błędach. Wiele powszechnie używanych dystrybucji Linuksa ma domyślnie bardzo stare wersje gcc.
  • Program przyrostowo. Nie próbuj pisać ton kodu przed kompilacją. Napisz najkrótszą możliwą ilość, która nadal będzie się kompilować. Jeśli zmieniłeś tylko jedną linię od czasu ostatniej kompilacji, to dużo łatwiej jest ustalić, która linia zawiera rzeczywisty problem.
  • Napisz testy jednostkowe. Dzięki temu masz większą pewność dokonywania wyjaśnień związanych z refaktoryzacją podczas naprawiania błędów kompilacji.
Karl Bielefeldt
źródło
23
Dobre IDE może również znacznie pomóc w doświadczeniu, np. podkreślanie błędów na czerwono.
BlueRaja - Danny Pflughoeft
86
„Programuj przyrostowo. Nie próbuj pisać tony kodu przed kompilacją. Napisz możliwie najkrótszą ilość, która nadal będzie się kompilować. Jeśli zmieniłeś tylko jedną linię od ostatniej kompilacji, łatwiej jest to rozgryźć która linia zawiera rzeczywisty problem. ” To tyle. Również większość IDE ostrzeże Cię, jeśli napiszesz kod, który się nie skompiluje, i podświetlisz błędy.
Polygnome,
4
@einpoklum: nie lekceważ trzeciej opcji; Komunikaty o błędach kompilatora znacznie się poprawiły. Podobnie, użyj kilku kompilatorów (np. Gcc i clang) - wyłapuje więcej błędów / ostrzeżeń, a jeden z nich może mieć lepszą diagnostykę dla konkretnego problemu niż drugi.
Mat
19
@einpoklum: pisanie rzeczy bardziej stopniowo jest bardzo na miejscu, zwłaszcza dla początkujących. Heck, jeśli uważasz, że „krótkich zadań programistycznych” nie można wykonać przyrostowo, dzieląc je na kilka małych funkcji oraz wdrażając i kompilując je jeden po drugim, powinieneś spróbować doskonalić tę umiejętność dla siebie…
Doc Brown
4
Sztuczka, która mi pomogła: jeśli komunikat o błędzie wspomina o linii N, sprawdź linię N-1. Na przykład, jeśli brakuje linii średnika w linii 17, komunikat o błędzie powie, że coś jest nie tak z linią 18. Jest tak, ponieważ kompilator oczekiwał średnika, ale zamiast tego dostał coś innego, w następnej linii.
user2023861
56

Twój przyjaciel nie potrzebuje słownika. Glosariusz mu nie pomoże. Potrzebuje lepszej intuicji, co zrobić, gdy wystąpią błędy kompilatora.

Błędy kompilatora C nie są tak intuicyjne jak, powiedzmy, błędy kompilatora C #, z wielu powodów głównie związane z „bliską metalem” naturą C. Rozwiązywanie błędów kompilatora w C nie jest ćwiczeniem polegającym na dopasowaniu wzorca, ponieważ błąd Odbieranie może nie mieć nic wspólnego z rzeczywistym problemem. W przeciwieństwie do C # lub Java, gdzie komunikat o błędzie zwykle odwzorowuje dokładną lokalizację kodu i problem, błędy w C mogą być liczne i dalekie.

Przykładem tego jest „oczekiwany średnik” lub dowolna liczba błędów składniowych, które wskazują, że parser zawiesił się na czymś (niekoniecznie średnik). Lub coś w rodzaju „nieoczekiwanej deklaracji przekazania”, błąd, który, gdy go widzę, niezmiennie oznacza, że ​​źle zapisałem wielkie litery w jednym z moich plików .h, ale który nie wskazuje na plik .h jako źródło problemu.

Strategią twojego przyjaciela nie powinno być dopasowanie wzoru do listy błędów i rozwiązań; powinno być wystarczająco dobrze rozumieć składnię i specyfikację języka C, aby dowiedzieć się, na czym polega rzeczywisty problem.

Robert Harvey
źródło
17
Nie. Chodzi o to, aby znać język wystarczająco dobrze, aby wiedzieć, że nie można przypisać wyrażenia liczbowego, a jedynie zmiennej. Nie musisz wcale wiedzieć, co to jest wartość, dlatego nie uczą tego na kursach dla początkujących.
Robert Harvey,
18
Zasadniczo tak. Ale praktycznie nie jest to możliwe. Robię to od bardzo dawna i wciąż otrzymuję niejasne komunikaty o błędach kompilatora za każdym razem, gdy piszę program C. Jednak rzadko staram się czytać te wiadomości i próbować je zrozumieć; zamiast tego patrzę na to, gdzie wskazuje komunikat o błędzie, a ponieważ wiem, jak powinna wyglądać podstawowa struktura składniowa programu w języku C, mogę stosunkowo szybko wykryć problem bez poświęcania czasu na rozszyfrowywanie komunikatów o błędach.
Robert Harvey,
36
Innymi słowy, proszenie o słowniczek pozwalający zrozumieć błędy kompilatora przypomina trochę czytanie słownika w celu zrozumienia języka angielskiego; to nie do końca tak działa. Uczysz się i rozumiesz angielski, czytając go i pisząc, a nie czytając słownik.
Robert Harvey,
14
[wzrusza ramionami] Jeśli nie używasz słownika, aby uzupełnić swoją już istniejącą znajomość języka angielskiego, sugerowałbym, że robisz to źle. Ostatnią rzeczą, którą zasugerowałbym, jest wszystko, co powoduje, że początkujący programiści rozłączają się ze słownictwem bardziej niż są. Programiści nie potrzebują więcej słów; potrzebują więcej umiejętności.
Robert Harvey,
13
@einpoklum, glosariusz tutaj nie pomoże. Opis słowa „lvalue” może być albo zbyt techniczny dla początkującego, albo podobny do „tego, co może znajdować się po lewej stronie zadania”, co jest równie nieprzydatne.
Bart van Ingen Schenau
26

Istotną techniką, o której warto wspomnieć, jest użycie drugiego kompilatora. Na przykład Clang zainwestował w lepsze komunikaty o błędach, ale każdy alternatywny sposób sformułowania błędu może być pouczający.

Jest to szczególnie ważne w przypadku najbardziej skomplikowanych rodzajów błędów. Na przykład, gdy pomieszasz dwie podobne konstrukcje (co nie jest niczym niezwykłym dla początkujących), kompilatory zwykle mają problem z wygenerowaniem odpowiedniego komunikatu o błędzie. Może to powodować zamieszanie, gdy kompilator wyświetli komunikat o błędzie dotyczący niepoprawnego użycia konstrukcji A, gdy rzeczywiście zamierzałeś zbudować B. Drugi kompilator może wywnioskować, że zamierzałeś B.

MSalters
źródło
13

Ktoś podjął próbę glosariusza błędów GCC na Wikibooks jakiś czas temu, ale wygląda na to, że nigdy nie wystartował i nie został zaktualizowany.

Sekcja „Błędy” jest znacznie dalej niż sekcja „Ostrzeżenia”. Wygląda na to, że był przeznaczony dla G ++, ale nadal może tam być kilka informacji przydatnych dla twojego przyjaciela.

nBurn
źródło
12

Oprócz powyższych odpowiedzi, zauważ, że większość kompilatorów nie ma kompleksowych glosariuszy błędów - byłoby to dużo pracy, ponieważ same komunikaty często się zmieniają, a jest ich całkiem sporo.

Najlepszym zamiennikiem glosariusza jest dostęp do Internetu. Ilekroć kompilator generuje błąd, którego nie rozumiesz, pociesz się, że jest mało prawdopodobne, abyś jako pierwszy go spotkał i był zdezorientowany. Szybka wiadomość Google z dokładną wiadomością często wystarcza, aby uzyskać mnóstwo informacji w łatwym do odczytania formacie, często z przykładowym kodem bardzo podobnym do Twojego.

Poza tym potrzebujesz czasu i znajomości języka oraz kompilatora. To i dobra rada udzielona przez Karla Bielefeldta .

Dúthomhas
źródło
1
Nie sądzę, że utrzymanie tego byłoby dużo pracy. Ponadto może być dodawany przez społeczeństwo, takie jak Stackoverflow lub Wiki, z zaufanymi osobami posiadającymi uprawnienia redaktora.
einpoklum
6
@einpoklum Czy widziałeś kiedyś dokumenty PHP? Tak się dzieje, kiedy pozwalasz społeczności zajmować się tymi sprawami.
Kevin
4
Dawno, dawno temu opublikowane (drukowane) instrukcje były jedynym dostępnym zasobem. Zazwyczaj były one wystarczająco dobrze napisane, aby dostarczyć niezbędnych informacji / wskazówek w celu rozwiązania problemu. Wraz z ewolucją Internetu nikt już nie publikuje w formie drukowanej (jeśli w ogóle, to on-line). Jakość „oficjalnych” materiałów referencyjnych (online lub innych) znacznie spadła w ciągu dziesięcioleci, które programuję, więc najlepszym dostępnym zasobem jest często Google, a najbardziej przydatne wyniki często pojawiają się w Stackoverflow.
Zenilogix,
2
Nawet jeśli słowniczek nie istnieje, wyszukiwarki mogą być najlepszym sposobem do nich dostęp. Przydają się także w wykrywaniu, gdy znajdujesz się na nieznanym terytorium: gdy jedynym wynikiem wyszukiwania jest kod źródłowy, który określa komunikat o błędzie;)
Warbo
2
Na studiach raz dostałem błąd kompilatora: „Dave nie sądzi, że tak się powinno stać. Napisz do niego na adres <[email protected]>”. Wysłałem mu e-maila, a tak naprawdę jako pierwszy trafiłem na ten konkretny błąd!
user1118321
6

Standard C używa wielu terminów, takich jak „lvalue” i „object” w sposób odmienny od innych języków programowania, a komunikaty kompilatora są często pisane w takich terminach. Użycie terminologii jest niespójne w niektórych częściach Standardu, ale każdy, kto chce się nauczyć języka C, powinien zapoznać się z projektami standardów C89, C99 i / lub C11, a także dokumentami uzasadniającymi je. Wyszukiwanie np. „Wersji roboczej C99” lub „uzasadnienia C89” powinno działać całkiem dobrze, chociaż może być konieczne uzyskanie dokumentu, którego oczekujesz. Chociaż większość kompilatorów obsługuje standard C99, warto wiedzieć, czym różni się on od standardu C89, a uzasadnienie C89 może oferować pewne historyczne tło, którego nie mają późniejsze wersje.

supercat
źródło
11
Standard C to bardzo gęsty i ciężki tekst. Początkujący nie ma szans na jego zrozumienie.
NieDzejkob
4
@NieDzejkob: Terminologia stosowana przez kompilatory - która wydaje się być tym, o co chodzi w tym pytaniu - wywodzi się ze standardu. Chociaż masz rację, że części standardu są niezrozumiałe (częściowo dlatego, że zostały opracowane przez komitet, a autorzy wydają się nie mieć spójnego zrozumienia, co to znaczy), ale każdy, kto chce zrozumieć, co terminy takie jak „lvalue” oznaczają, że należy pamiętać, skąd pochodzą. Co więcej, jeśli ktoś chce zrozumieć, dlaczego coś takiego x=0x1e-xpowoduje błąd, to tak naprawdę nie wiem nic innego niż Standard ...
supercat
3
Zgadzam się z @NieDzejkob: Standard C nie jest rodzajem tekstu, z którym chcesz skonfrontować nowicjusza. Początkujący potrzebują szybkich pozytywnych doświadczeń . I muszą się uczyć nowych rzeczy, jedna po drugiej, gdy się pojawią. Czytanie standardu lub uzasadnienia zajmuje zbyt dużo czasu, całkowicie obciążając nowicjusza informacjami.
cmaster
2
@cmaster: Zacząłem od C89 Standard przed wiekami i nie było tak źle, nawet w czasach przed przeglądarkami z przydatną funkcją „znajdź tekst”. Przyznaję, że późniejsze standardy były coraz gorsze. Chociaż nikt nie powinien opierać się na standardzie jako jedynym odnośniku, ważne jest, aby rozpoznać rozbieżność między ludową mądrością na temat tego, jak zachowują się kompilatory mikrokomputerowe, a sposobami, w jakie standard pozwala na zachowanie niskiej jakości, więc można się przygotować, jeśli ktoś ma radzić sobie z tym ostatnim.
supercat
3
@cmaster: W każdym razie ktoś, kto programuje C, powinien wiedzieć o standardzie i wiedzieć, jak się z nim skonsultować w razie potrzeby, nawet jeśli nie będzie próbował przeczytać całości. Jeśli na przykład wyszukujesz w Internecie standardową funkcję biblioteczną, możesz znaleźć odniesienie, które opisuje zachowanie jednej implementacji w niektórych przypadkach narożnych, nie wspominając, że z punktu widzenia standardu te przypadki narożne wywołują zachowanie niezdefiniowane, a inne implementacje mogą nie działa w ten sam sposób. Jeśli zamiast tego przeszukuje się Standard, można uniknąć tego problemu.
supercat
5

Dziwi mnie, że nikt nie podał oczywistej odpowiedzi i, jak podejrzewam, najczęściej używanej w praktyce: po prostu nie czytaj komunikatów o błędach.

Zdecydowana większość wartości większości komunikatów o błędach polega po prostu na tym, że coś jest nie tak w takim i takim wierszu. Przez większość czasu po prostu patrzę na numer linii i przechodzę do tej linii. Moje „odczytanie” komunikatu o błędzie w tym momencie jest zwykle dokładnie tym, co przykuwa moje oko, nawet śladem. Jeśli nie jest od razu jasne, co jest nie tak na linii lub w jej pobliżu, wtedy faktycznie przeczytam wiadomość. Ten przepływ pracy jest jeszcze lepszy z IDE lub oprzyrządowaniem, które natychmiast rozpoznaje błędy i automatycznie realizuje sugestię Karla Bielefeldta, aby rozważyć tylko niewielkie zmiany.

Oczywiście komunikaty o błędach nie zawsze wskazują odpowiednią linię, ale często nie wskazują też właściwej przyczyny źródłowej, więc nawet pełne zrozumienie komunikatu o błędzie byłoby bardzo pomocne. Nie trwa długo, aby zorientować się, które komunikaty o błędach są bardziej niezawodne w zlokalizowaniu właściwej linii.

Z jednej strony większość błędów, które może popełnić nowicjusz, może być boleśnie oczywista dla doświadczonego programisty bez konieczności korzystania z kompilatora. Z drugiej strony są znacznie mniej prawdopodobne, że będą tak oczywiste dla początkującego (choć wielu będzie oczywistych, większość błędów to głupie błędy). W tym momencie całkowicie zgadzam się z Robertem Harveyem, nowicjusz musi po prostu lepiej poznać język. Nie da się tego uniknąć. Błędy kompilatora, które odnoszą się do nieznanych pojęć lub wydają się zaskakujące, powinny być postrzegane jako zachęta do pogłębienia znajomości języka. Podobnie w przypadkach, gdy kompilator narzeka, ale nie można zrozumieć, dlaczego kod jest nieprawidłowy.

Ponownie zgadzam się z Robertem Harveyem, że potrzebna jest lepsza strategia wykorzystania błędów kompilatora. Przedstawiłem niektóre aspekty powyżej, a odpowiedź Roberta Harveya podaje inne aspekty. Nie jest nawet jasne, co twój przyjaciel ma nadzieję zrobić z takim „glosariuszem”, i jest bardzo mało prawdopodobne, że taki „glosariusz” byłby bardzo przydatny dla twojego przyjaciela. Komunikaty kompilatora z pewnością nie są miejscem na wprowadzenie do pojęć języka 1, a „glosariusz” nie jest dla niego lepszym miejscem. Nawet z jasnym opisem, co oznacza komunikat o błędzie, nie powie ci, jak rozwiązać problem.

1 Próbuje tego jednak dokonać w kilku językach, takich jak Elm i Dhall (i prawdopodobnie Racket), a także kilka implementacji języków dla początkujących. W tym duchu doradztwo MSalters dotyczące zastosowania innej implementacji jest bezpośrednio istotne. Osobiście uważam takie rzeczy za nieprzekonujące i niezupełnie ukierunkowane na właściwy problem. Nie oznacza to, że nie ma sposobów na poprawianie komunikatów o błędach, ale dla mnie mają one tendencję do objaśniania przekonań kompilatora i ich podstaw.

Derek Elkins
źródło
4

Jak więc początkujący programista powinien podjąć wyzwanie zrozumienia komunikatów o błędach kompilatora? W szczególności z kombinacją C i GCC?

Poinformuj swojego przyjaciela, aby wykonał następujące czynności, gdy napotka błąd, którego nie rozumie:

  • Usuń / skomentuj kod dodany od ostatniej udanej kompilacji.
  • Umieść z powrotem małe części i skompiluj
  • Powtarzaj aż do wystąpienia błędu

Błędy kompilatora mówią tylko o tym, czego kompilator nie rozumie o twoim kodzie, a nie o tym, co jest z nim nie tak. Takie podejście zajmuje mniej więcej tyle samo czasu, co w Google, przeglądając błąd i czytając niektóre dokumenty lub post StackOverflow, ale daje znacznie lepsze zrozumienie tego, co robisz źle.

Zmuszaj je również do częstej kompilacji, dopóki nie zaczną pracować nad projektami, których budowa zajmuje kilka minut, wykrywanie błędów przed dodaniem zbyt dużej ilości innego kodu bardzo pomaga.

Na koniec powiedz im, aby pracowali nad jedną rzeczą na raz, nie pracuj w wielu plikach bez kompilacji pomiędzy nimi, nie wprowadzaj wielu zależności jednocześnie itp.

Kevin
źródło
4

Inną techniką byłoby, gdyby przyjaciel napisał z czasem własny słownik, gdy napotka różne komunikaty o błędach. Często najlepszym sposobem na nauczenie się czegoś jest nauczenie tego. Oczywiście, zanim skończy się słownik, prawdopodobnie już go nie będzie potrzebował.

Moje osobiste doświadczenie z GCC polega na tym, że każdy komunikat o błędzie odnosi się do „zwykłego” zestawu błędów. Na przykład, gdy GCC mówi „czy zapomniałeś &”, zwykle oznacza to, że zapomniałem nawiasów. Oczywiście, które błędy odpowiadają, które komunikaty o błędach będą zależeć od programisty, kolejny dobry powód dla znajomego, aby napisał własny słownik.

piekarnik
źródło
1
Dokument ten ma niezwykle ważną zaletę, którą można umieścić w Internecie (nawet jeśli zawiera tylko 5-10 wpisów) i byłby świetnym wyróżnikiem podczas ubiegania się o staż.
Josh Rumbut