Dlaczego zawsze powinienem włączać ostrzeżenia kompilatora?

302

Często słyszę, że podczas kompilacji programów C i C ++ powinienem „zawsze włączać ostrzeżenia kompilatora”. Dlaczego to jest konieczne? Jak mogę to zrobić?

Czasami słyszę też, że powinienem „traktować ostrzeżenia jako błędy”. Czy powinienem? Jak mogę to zrobić?

n. „zaimki” m.
źródło

Odpowiedzi:

341

Po co włączać ostrzeżenia?

C i C ++ kompilatory są notorycznie źle raportowania jakieś błędy powszechne programista domyślnie , takich jak:

  • zapominając o inicjalizacji zmiennej
  • zapominanie returno wartości z funkcji
  • argumenty printfi scanfrodzin nie spełniających ciąg formatu
  • funkcja jest używana bez wcześniejszego zadeklarowania (tylko C)

Można je wykryć i zgłosić, ale zwykle nie są domyślnie; tej funkcji należy jawnie zażądać za pomocą opcji kompilatora.

Jak włączyć ostrzeżenia?

To zależy od twojego kompilatora.

Microsoft C i kompilatory C ++ zrozumieć jak przełączniki /W1, /W2, /W3, /W4i /Wall. Użyj przynajmniej /W3. /W4i /Wallmoże emitować fałszywe ostrzeżenia dla plików nagłówków systemowych, ale jeśli twój projekt kompiluje się czysto z jedną z tych opcji, idź do niego. Te opcje wykluczają się wzajemnie.

Większość innych kompilatory zrozumieć opcje takie jak -Wall, -Wpedantici -Wextra. -Walljest niezbędna, a cała reszta jest zalecana (pamiętaj, że pomimo nazwy -Wallwłącza tylko najważniejsze ostrzeżenia, a nie wszystkie ). Te opcje mogą być używane osobno lub wszystkie razem.

Twoje IDE może mieć sposób na włączenie ich z interfejsu użytkownika.

Po co traktować ostrzeżenia jako błędy? To tylko ostrzeżenia!

Ostrzeżenie kompilatora sygnalizuje potencjalnie poważny problem w kodzie. Wyżej wymienione problemy są prawie zawsze śmiertelne; inni mogą, ale nie muszą, ale chcesz, aby kompilacja nie powiodła się, nawet jeśli okaże się fałszywym alarmem. Zbadaj każde ostrzeżenie, znajdź pierwotną przyczynę i napraw ją. W przypadku fałszywego alarmu obejdź go - to znaczy użyj funkcji lub konstrukcji w innym języku, aby ostrzeżenie nie było już wyzwalane. Jeśli okaże się to bardzo trudne, wyłącz to ostrzeżenie indywidualnie dla każdego przypadku.

Nie chcesz po prostu pozostawiać ostrzeżeń jako ostrzeżeń, nawet jeśli wszystkie są fałszywymi alarmami. Może być OK w przypadku bardzo małych projektów, w których łączna liczba emitowanych ostrzeżeń jest mniejsza niż 7. W każdym razie i nowe ostrzeżenie łatwo zgubić się w zalewie starych znajomych. Nie pozwól na to. Po prostu spraw, aby cały projekt kompilował się czysto.

Uwaga: dotyczy to rozwoju programu. Jeśli wypuszczasz swój projekt na cały świat w formie źródłowej, dobrym pomysłem może być nie dostarczanie -Werrorlub równoważne w wydanym skrypcie kompilacji. Ludzie mogą próbować zbudować projekt przy użyciu innej wersji kompilatora lub innego kompilatora, który może mieć włączony inny zestaw ostrzeżeń. Możesz chcieć, aby ich kompilacja się powiodła. Nadal dobrym pomysłem jest włączenie ostrzeżeń, aby osoby, które widzą komunikaty ostrzegawcze, mogły wysyłać raporty o błędach lub łatki.

Jak traktować ostrzeżenia jako błędy?

To samo dzieje się z przełącznikami kompilatora. /WXjest dla Microsoft, większość używa -Werror. W obu przypadkach kompilacja zakończy się niepowodzeniem, jeśli zostaną wygenerowane jakiekolwiek ostrzeżenia.

n. „zaimki” m.
źródło
107
Wysłałem to pytanie, ponieważ mam dość mówienia ludziom o włączaniu ostrzeżeń. Teraz mogę po prostu wskazać ich tutaj (lub, jeśli jestem w szczególnie złym nastroju, zamknąć ich pytanie jako dupe). Możesz poprawić tę odpowiedź lub dodać własną!
n. „zaimki” m.
16
Możesz także użyć Clang's -Weverything
pmg
9
Jedynym modyfikatorem, który dodam, jest to, że niektóre ostrzeżenia mogą nie być pomocne w twojej aplikacji. (Widziałem ostrzeżenia, że ​​kompilator dodał 2 bajty wypełnienia między elementami w strukturze. Aplikacja była przeznaczona do prototypowania, więc trochę zmarnowanej pamięci nam nie przeszkadzało.) Traktuj wszystkie ostrzeżenia jako błędy, a następnie wyłącz ostrzeżenie tylko, jeśli wiesz, dlaczego to ostrzeżenie ci nie pomoże.
Kyle A,
20
Wadą traktowania ostrzeżeń jako błędów dla osób postępujących zgodnie z domyślnymi instrukcjami kompilacji jest to, że zgniły kod jako kompilatory dodają nowe ostrzeżenia. Użytkownicy, którzy pobiorą Twój kod i spróbują go skompilować w przyszłości, mogą tego nie zrobić, ponieważ ich kompilator jest zbyt nowy i wyświetla ostrzeżenie o dodatkowych nawiasach lub czymś, na czym kompilator się nie przejmował. Użytkownik, który napotka błąd, nie ponosi odpowiedzialności za kod ani system kompilacji i nie ma pojęcia, jak wyłączyć traktowanie ostrzeżeń jako błędów i faktycznie zbudować projekt.
interfect
15
@interfect Tak, zdarzyło mi się to kilka razy. Nic takiego. Jeśli zdecydujesz się zbudować nieobsługiwane oprogramowanie za pomocą kompilatora, z którym nigdy nie został przetestowany, lepiej przygotuj się na samodzielną konserwację.
n. „zaimki” m.
94

C jest, jak wiadomo, językiem raczej niskiego poziomu, jak HLL. C ++, choć może wydawać się językiem znacznie wyższego poziomu niż C, nadal ma wiele cech. Jedną z tych cech jest to, że języki zostały zaprojektowane przez programistów dla programistów - a konkretnie programistów, którzy wiedzieli, co robią.

[Do końca tej odpowiedzi skupię się na C. Większość tego, co powiem, dotyczy również C ++, choć może nie tak mocno. Chociaż, jak słynie Bjarne Stroustrup, „C ułatwia strzelanie sobie w stopę; C ++ utrudnia, ale kiedy to robisz, zdmuchuje całą nogę”. ]

Jeśli wiesz, co robisz - naprawdę wiesz, co robisz - czasem może być konieczne „złamanie zasad”. Ale przez większość czasu większość z nas zgadza się, że dobre intencje chronią nas przed kłopotami i że bezmyślne łamanie tych zasad przez cały czas jest złym pomysłem.

Ale w C i C ++ istnieje zaskakująco duża liczba rzeczy, które możesz zrobić, które są „złymi pomysłami”, ale które nie są formalnie „niezgodne z regułami”. Czasami są czasem złym pomysłem (ale czasem można je obronić); czasami są to zły pomysł praktycznie przez cały czas. Ale tradycją zawsze nie było ostrzeganie o tych rzeczach - ponieważ ponownie założono, że programiści wiedzą, co robią, nie robiliby tych rzeczy bez dobrego powodu, byliby zirytowani przez grupę niepotrzebnych ostrzeżeń.

Ale oczywiście nie wszyscy programiści naprawdę wiedzą, co robią. W szczególności każdy programista C (bez względu na to, jak doświadczony) przechodzi fazę bycia początkującym programistą C. Nawet doświadczeni programiści C mogą być nieostrożni i popełniać błędy.

Wreszcie, doświadczenie pokazało nie tylko, że programiści popełniają błędy, ale że błędy te mogą mieć realne, poważne konsekwencje. Jeśli popełnisz błąd, a kompilator nie ostrzeże Cię o tym, a w jakiś sposób program nie zawiesi się natychmiast lub nie zrobi z tego powodu czegoś oczywistego niewłaściwego, błąd może czaić się tam, ukryty, czasami przez lata, dopóki nie spowoduje naprawdę duży problem.

Okazuje się więc, że w większości przypadków ostrzeżenia są dobrym pomysłem. Nawet doświadczeni programiści nauczyli się (tak naprawdę „ szczególnie doświadczeni programiści nauczyli się”), że w sumie ostrzeżenia przynoszą więcej korzyści niż szkody. Za każdym razem, gdy celowo zrobiłeś coś złego, a ostrzeżenie było uciążliwe, prawdopodobnie co najmniej dziesięć razy zrobiłeś coś nie tak przez przypadek, a ostrzeżenie uratowało cię od dalszych kłopotów. I większość ostrzeżeń można wyłączyć lub obejść kilka razy, gdy naprawdę chcesz zrobić coś „złego”.

(Klasycznym przykładem takiego „błędu” jest test if(a = b). Przez większość czasu jest to błąd, więc większość kompilatorów obecnie ostrzega przed nim - niektóre nawet domyślnie. Ale jeśli naprawdę chcesz zarówno przypisać bdo niego, jak ai przetestować w rezultacie możesz wyłączyć ostrzeżenie, pisząc if((a = b)).)

Drugie pytanie brzmi: dlaczego chcesz poprosić kompilator o traktowanie ostrzeżeń jako błędów? Powiedziałbym, że to z powodu ludzkiej natury, a szczególnie zbyt łatwej reakcji powiedzenia „Och, to tylko ostrzeżenie, to nie jest tak ważne, posprzątam to później”. Ale jeśli jesteś prokrastynatorem (a ja nie wiem o tobie, ale jestem okropnym prokrastynatorem), łatwo jest odłożyć niekoniecznie porządki w zasadzie na zawsze - a jeśli masz zwyczaj ignorowania ostrzeżeń, coraz łatwiej przeoczyć ważny komunikat ostrzegawczy, który siedzi tam, niezauważony, pośród wszystkich, które ignorujesz.

Poproszenie kompilatora, aby traktował ostrzeżenia jako błędy, jest małą sztuczką, w którą możesz się zagrać, aby obejść ten ludzki pomysł.

Osobiście nie nalegam na traktowanie ostrzeżeń jak błędów. (W rzeczywistości, jeśli mam być szczery, mogę powiedzieć, że praktycznie nigdy nie włączam tej opcji w moim „osobistym” programowaniu). Ale możesz być pewien, że mam włączoną tę opcję w pracy, gdzie nasz przewodnik po stylu (który ja napisał) nakazuje jego użycie. I powiedziałbym - podejrzewam, że powiedziałby większość profesjonalnych programistów - że każdy sklep, który nie traktuje ostrzeżeń jako błędów w C, zachowuje się nieodpowiedzialnie, nie przestrzega powszechnie przyjętych najlepszych praktyk branżowych.

Steve Summit
źródło
9
„programiści, którzy wiedzieli, co robią” - LOL; istnieje błąd „nie prawdziwy Szkot”, jeśli go kiedykolwiek widziałem :)
Dancrumb,
3
@Dancrumb LOL z powrotem atcha. Nigdy nie jestem pewien, czy rozumiem błąd No true Scotsman , ale podoba mi się to, więc będzie to dla mnie dobre ćwiczenie. Wydaje mi się, że ta aplikacja jest taka: „Żaden programista C nigdy by nie napisał if(a = b), dlatego nie musimy o tym ostrzegać”. (Następnie ktoś tworzy listę 10 krytycznych błędów w 10 wydanych produktach, które wynikają z tego konkretnego błędu.) „Dobra, żaden doświadczony programista C nigdy nie napisałby tego ...”
Steve Summit
3
@ SteveSummit, ale naprawdę doświadczony programista C może pisać if (returnCodeFromFoo = foo(bar))i oznaczać to, aby przechwycić i przetestować kod w jednym miejscu (Załóżmy, że jedynym celem foojest uzyskanie efektów ubocznych!) Fakt, że naprawdę naprawdę doświadczony programista może wiedzieć, że to nie jest dobry styl kodowania nie ma znaczenia;)
alephzero,
5
Chodzi o to, że najbardziej doświadczeni programiści włączają większość, jeśli nie wszystkie, ostrzeżenia. Jeśli chcą użyć czegoś podobnego if (returnCodeFromFoo = foo(bar)), to umieszczają komentarz i wyłączają ostrzeżenie (tak, że gdy programista konserwacji spojrzy na to 4 lata później, zrozumie, że kod jest celowy. To powiedziawszy, pracowałem z kimś, kto w (w krainie Microsoft C ++) upierał się, że najlepszym rozwiązaniem jest połączenie / Wall z traktowaniem ostrzeżeń jako błędów. Nie jest (chyba że chcesz wprowadzić wiele komentarzy tłumiących)
Flydog57
3
Jako osoba, która nie pisze kodu na co dzień, ale kiedy to robię , wydaje się być czystym metalem (często na płycie własnego projektu) uważam ostrzeżenia za nieocenione. Gdy upychasz wartości do rejestrów wewnętrznych (na przykład lokalizacją deskryptora DMA) ostrzeżenie o konwersji na wskaźnik oznacza, że ​​wykonuję rzut, aby usunąć ostrzeżenie. Nie byłby to błąd, ale jeśli ktoś inny (lub nawet ja!) Przejmie ten kod w ciągu kilku miesięcy, może to być mylące. Poza tym stosuję również zasadę braku ostrzeżeń do wyników moich narzędzi CAD.
Peter Smith
39

Ostrzeżenia składają się z najlepszych porad, które niektórzy z najbardziej wykwalifikowanych programistów C ++ mogą upiec w aplikacji. Warto je trzymać.

C ++, jako kompletny język Turinga, ma wiele przypadków, w których kompilator musi po prostu zaufać, że wiesz, co robisz. Istnieje jednak wiele przypadków, w których kompilator może zdać sobie sprawę, że prawdopodobnie nie zamierzałeś pisać tego, co napisałeś. Klasycznym przykładem są kody printf (), które nie pasują do argumentów, lub std :: strings przekazywane do printf (nie to, że mi się to kiedykolwiek przytrafia!). W takich przypadkach napisany kod nie jest błędem. Jest to poprawne wyrażenie C ++ z poprawną interpretacją dla kompilatora. Ale kompilator ma silne przeczucie, że po prostu przeoczyłeś coś, co jest łatwe do wykrycia przez nowoczesny kompilator. To są ostrzeżenia. Są to rzeczy oczywiste dla kompilatora, wykorzystujące wszystkie surowe reguły języka C ++, które mogą być przeoczone.

Wyłączenie ostrzeżeń lub zignorowanie ich jest jak zignorowanie bezpłatnych porad od osób bardziej wykwalifikowanych niż ty. Jest to lekcja huberi, która kończy się albo, gdy lecisz zbyt blisko słońca i skrzydła się stopią, lub wystąpi błąd uszkodzenia pamięci. Między nimi każdego dnia spadnę z nieba!

„Traktuj ostrzeżenia jako błędy” to ekstremalna wersja tej filozofii. Chodzi o to, że rozwiązujesz każde ostrzeżenie, które daje ci kompilator - słuchasz każdej darmowej porady i postępujesz zgodnie z nią. To, czy jest to dobry model rozwoju, zależy od zespołu i od jakiego produktu pracujesz. Takie ascetyczne podejście może mieć mnich. Dla niektórych działa świetnie. Dla innych tak nie jest.

W wielu moich aplikacjach nie traktujemy ostrzeżeń jako błędów. Robimy to, ponieważ te konkretne aplikacje muszą się kompilować na kilku platformach z kilkoma kompilatorami w różnym wieku. Czasami okazuje się, że naprawienie ostrzeżenia z jednej strony nie jest niemożliwe bez ostrzeżenia na innej platformie. Jesteśmy więc tylko ostrożni. Szanujemy ostrzeżenia, ale nie pochylamy się za nimi.

Cort Ammon
źródło
11
Z tym wiąże się to, że C ++ jest kompletny. Wiele języków jest kompletnych i nie ufam, jeśli zrobisz coś złego ....
Do widzenia SE
3
@KamiKaze każdy język będzie miał błędy idiomatyczne (np. Java nie może powstrzymać Cię przed niespójnym equals/ hashCode), a który z nich jest zgłaszany, to problem z jakością implementacji.
Caleth,
2
@KamiKaze Bit kompletności Turinga pokazuje, że istnieją przypadki, w których kompilator nie może udowodnić, że Twój kod nie będzie działał zgodnie z planem. Jest to ważne, ponieważ kompilatory nie mogą powodować, że cały „zły” kod jest błędem. Błędy można rezerwować tylko dla zachowań, które według projektantów języka zawsze będą „błędne”. (zwykle dlatego, że prowadzi niespójnymi ścieżkami).
Cort Ammon
2
Co również wskazuje na wyzwanie z „wszystkie ostrzeżenia są błędami”. Ostrzeżenia są z założenia bardziej oportunistyczne i uruchamiają się na potencjalnie poprawnym kodzie w zamian za częstsze uruchamianie złego kodu. Ostrzeżenia jako błędy prowadzą do niemożności pełnego korzystania z możliwości językowych.
Cort Ammon
2
Jednak ogólnie rzecz biorąc, programiści chcą języka, który robi coś więcej niż „bezpieczną” rzecz. Chcemy języka, który robi to, co naszym zdaniem powiedzieliśmy. Dlatego ostrzeżenia pozostają ważne, ponieważ rzeczywista klasa rzeczy, które chcemy, aby komputer robił, jest klasą semantyczną. Kompilator może się na to zdecydować, definiując „niepoprawny” lub „niebezpieczny”, ale w końcu nadal masz nadklasę zachowań, które programista chciał zrobić. Ostrzeżenia pomagają zawęzić tę nadklasę.
Cort Ammon
19

Obsługa ostrzeżeń nie tylko poprawia kod, ale także czyni cię lepszym programistą. Ostrzeżenia powiedzą ci o rzeczach, które dziś mogą ci się wydawać mało, ale pewnego dnia ten zły nawyk powróci i odgryzie ci głowę.

Użyj poprawnego typu, zwróć tę wartość, oceń tę zwracaną wartość. Poświęć trochę czasu i zastanów się: „Czy to naprawdę poprawny typ w tym kontekście?” „Czy muszę to zwrócić?” I biggie; „Czy ten kod będzie przenośny przez następne 10 lat?”

Nabierz nawyku pisania kodu ostrzegawczego.

RedSonja
źródło
17

Pozostałe odpowiedzi są doskonałe i nie chcę powtarzać tego, co powiedzieli.

Innym aspektem „dlaczego włączyć ostrzeżenia”, który nie został właściwie dotknięty, jest to, że ogromnie pomagają w utrzymaniu kodu. Kiedy piszesz program o znacznych rozmiarach, niemożliwe staje się trzymanie tego wszystkiego w głowie na raz. Zazwyczaj masz funkcję lub trzy, o których aktywnie piszesz i myślisz, i być może plik lub trzy na ekranie, do których możesz się odwoływać, ale większość programu istnieje gdzieś w tle i musisz mu zaufać kontynuuje pracę.

Włączanie ostrzeżeń i używanie ich tak energicznie, jak to tylko możliwe, pomaga ostrzec cię, jeśli coś, co zmienisz, powoduje kłopoty z powodu czegoś, czego nie widzisz.

Weźmy na przykład ostrzeżenie o klanie -Wswitch-enum. Powoduje to ostrzeżenie, jeśli użyjesz przełącznika na wyliczeniu i pominiesz jedną z możliwych wartości wyliczenia. Jest to coś, co mogłoby się wydawać mało prawdopodobnym błędem: prawdopodobnie przynajmniej spojrzałeś na listę wartości wyliczeniowych, pisząc instrukcję switch. Możesz nawet mieć IDE, które wygenerowało dla ciebie opcje przełączania, nie pozostawiając miejsca na ludzkie błędy.

To ostrzeżenie naprawdę działa, gdy sześć miesięcy później dodajesz kolejny możliwy wpis do wyliczenia. Ponownie, jeśli myślisz o danym kodzie, prawdopodobnie nic ci nie będzie. Ale jeśli to wyliczenie jest używane do wielu różnych celów i jest jednym z tych, które wymagają dodatkowej opcji, bardzo łatwo zapomnieć o aktualizacji przełącznika w pliku, którego nie dotknąłeś przez 6 miesięcy.

Możesz myśleć o ostrzeżeniach w taki sam sposób, jak myślisz o zautomatyzowanych przypadkach testowych: pomagają ci upewnić się, że kod jest rozsądny i robi to, czego potrzebujesz, gdy go piszesz, ale pomagają jeszcze bardziej, aby się upewnić, że kod cały czas robisz to, czego potrzebujesz, kiedy to robisz. Różnica polega na tym, że przypadki testowe działają bardzo wąsko w stosunku do wymagań twojego kodu i musisz je napisać, podczas gdy ostrzeżenia działają zasadniczo zgodnie z rozsądnymi standardami dla prawie całego kodu i są bardzo hojnie dostarczane przez trumny, które tworzą kompilatory.

Josiah
źródło
9
Innym sposobem, w jaki pomagają w utrzymaniu, jest przeglądanie kodu innej osoby i nie można stwierdzić, czy efekt uboczny był zamierzony. Po włączeniu ostrzeżeń wiesz, że byli przynajmniej świadomi problemu.
Robin Bennett,
Lub w moim przypadku importujesz plik z systemu osadzonego, który zawiera ponad 3000 instrukcji przełączania wierszy w wyliczeniu z kilkoma tysiącami wartości. Ostrzeżenia „przedostaje się” (unikane przez użycie goto) zamaskowały wiele „nieobsługiwanych” błędów ... osadzony kompilator nie wyemitował żadnego z nich, ale błędy były jednak ważne.
Móż
15

Niestosowane ostrzeżenia wcześniej czy później doprowadzą do błędów w kodzie .


Na przykład debugowanie błędu segmentacji wymaga od programisty wyśledzenia przyczyny (przyczyny) błędu, który zwykle znajduje się na wcześniejszym miejscu w kodzie niż w linii, która ostatecznie spowodowała błąd segmentacji.

To bardzo typowe, że przyczyną jest linia, dla której kompilator wydał ostrzeżenie, które zignorowałeś, a linia, która spowodowała błąd segmentacji, linia, która ostatecznie zgłosiła błąd.

Naprawienie ostrzeżenia prowadzi do rozwiązania problemu. Klasyk!

Demonstracja powyższego. Rozważ następujący kod:

#include <stdio.h>

int main(void) {
  char* str = "Hello world!";
  int idx;

  // Colossal amount of code here, irrelevant to 'idx'

  printf("%c\n", str[idx]);

  return 0;
}

który po skompilowaniu z flagą „Wextra” przekazaną do GCC daje:

main.c: In function 'main':
main.c:9:21: warning: 'idx' is used uninitialized in this function [-Wuninitialized]
    9 |   printf("%c\n", str[idx]);
      |                     ^

który i tak mógłbym zignorować i wykonać kod. A potem byłbym świadkiem „wielkiej” usterki segmentacji, jak mawiał mój profesor epikurusa IP:

Błąd segmentacji

Aby debugować to w scenariuszu w świecie rzeczywistym, należy zacząć od linii, która powoduje błąd segmentacji i spróbować ustalić, co jest przyczyną tej przyczyny. Musieliby szukać tego, co się stało ii strwewnątrz tej kolosalnej ilości kodu tam ...

Aż pewnego dnia znaleźli się w sytuacji, w której odkrywają, że idxjest używany niezainicjowany, a zatem ma wartość śmieci, co powoduje indeksowanie łańcucha (drogi) poza jego granicami, co prowadzi do błędu segmentacji.

Gdyby tylko nie zignorowali ostrzeżenia, natychmiast znaleźliby błąd!

gsamaras
źródło
4
Do twojego tytułu: niekoniecznie. Na przykład ostrzeżenie sugerujące użycie nawiasów w formule, która tak naprawdę ich nie potrzebuje, wskazuje na problem, który nigdy nie spowoduje błędu. Pierwszeństwa operatorów w danym języku programowania nie ulegają zmianie. Zawsze.
Marc van Leeuwen,
4
@MarcvanLeeuwen Przywołana przez Ciebie instancja może przerodzić się w błąd, na przykład jeśli programista, który nie pamięta pierwszeństwa operatora, nieco zmodyfikuje formułę. Ostrzeżenie mówi: „może być dla kogoś niejasne, dodaj nawiasy, aby było bardziej jasne”. Chociaż trzeba się zgodzić, że tytuł oryginalnego postu nie zawsze jest prawdziwy.
Hubert Jasieniecki
^ Wszystko może zostać zamienione w błąd. Tak samo łatwo wprowadzić błąd w częściowo nawiasowanym kodzie, jak w całkowicie nawiasowanym kodzie.
Jim Balter
... Nawiasem mówiąc, mam pytania do debuggera, którego pierwsza reakcja na „Och, to oddzieliło się od str[idx]nie jest „Okej, gdzie są stri idxzdefiniowano?”
Justin Time - Przywróć Monikę
2
Masz szczęście, jeśli dostaniesz błąd segmentacji. Jeśli masz mniej szczęścia, być może przypadkowo idxjest to wartość oczekiwana na teście (nie jest to mało prawdopodobne, jeśli oczekiwaną wartością jest 0) i faktycznie wskazuje na niektóre wrażliwe dane, które nigdy nie powinny być drukowane po wdrożeniu.
celtschk
14

Traktowanie ostrzeżeń jako błędów jest tylko środkiem do samodyscypliny: skompilowałeś program, aby przetestować tę błyszczącą nową funkcję, ale nie możesz tego zrobić, dopóki nie naprawisz niechlujnych części. Nie ma żadnych dodatkowych informacji Werror, po prostu bardzo wyraźnie określa priorytety:

Nie dodawaj nowego kodu, dopóki nie naprawisz problemów w istniejącym kodzie

To naprawdę sposób myślenia jest ważny, a nie narzędzia. Dane diagnostyczne kompilatora to narzędzie. MISRA (dla wbudowanego C) to kolejne narzędzie. Nie ma znaczenia, którego używasz, ale prawdopodobnie ostrzeżenia kompilatora są najłatwiejszym dostępnym narzędziem (można ustawić tylko jedną flagę), a stosunek sygnału do szumu jest bardzo wysoki. Więc nie ma powodu, aby go nie używać.

Żadne narzędzie nie jest niezawodne. Jeśli piszesz const float pi = 3.14;, większość narzędzi nie powie ci, że zdefiniowałeś π ze złą precyzją, co może prowadzić do problemów na drodze. Większość narzędzi nie podnosi brwi if(tmp < 42), nawet jeśli powszechnie wiadomo, że podawanie zmiennych bez znaczenia i używanie magicznych liczb to sposób na katastrofę w dużych projektach. Musisz zrozumieć, że każdy napisany przez ciebie „szybki test” jest po prostu: testem i musisz go przygotować, zanim przejdziesz do innych zadań, a jednocześnie dostrzeżesz jego wady. Jeśli pozostawisz ten kod bez zmian, debugowanie, jeśli po upływie dwóch miesięcy dodanie nowych funkcji będzie znacznie trudniejsze.

Gdy przejdziesz do właściwego sposobu myślenia, nie ma sensu z niego korzystać Werror. Posiadanie ostrzeżeń jako ostrzeżeń pozwoli Ci podjąć świadomą decyzję, czy nadal warto uruchomić sesję debugowania, którą właśnie miałeś rozpocząć, lub przerwać ją i najpierw naprawić ostrzeżenia.

Dmitrij Grigoriew
źródło
3
Na dobre lub na złe clippynarzędzie do szorowania Rust będzie ostrzegać o stałej wartości „3.14”. To właściwie przykład w dokumentacji . Ale jak można się domyślić po nazwie, clippyjest dumny z tego, że jest agresywnie pomocny.
emk
1
@emk Dzięki za ten przykład, być może powinienem przeformułować moją odpowiedź w sposób nigdy nie mówiąc „nigdy” . Nie chciałem powiedzieć, że sprawdzanie nieprecyzyjnych wartości π jest niemożliwe, po prostu samo usunięcie ostrzeżeń nie gwarantuje przyzwoitej jakości kodu.
Dmitry Grigoryev
Ostrzeżenia, które dają błędy, to jedno: automatyczne kompilacje zakończą się niepowodzeniem, ostrzegając w ten sposób, że coś poszło źle. Zautomatyzowane kompilacje pozwalają również na automatyzację strzępienia (eksplozja to 3 ... 2 ... 1 .. :)
Móż
8

Jako ktoś, kto pracuje ze starszym osadzonym kodem C, włączenie ostrzeżeń kompilatora pomogło pokazać wiele słabości i obszarów do zbadania podczas proponowania poprawek. W gcc wykorzystując -Walla -Wextranawet -Wshadowstały się niezbędne. Nie zamierzam podejmować każdego zagrożenia, ale wymienię kilka wyskakujących okienek, które pomogły pokazać problemy z kodem.

Zmienne są pozostawione

Można to łatwo wskazać na niedokończoną pracę i obszary, które mogą nie wykorzystywać wszystkich przekazywanych zmiennych, co może być problemem. Spójrzmy na prostą funkcję, która może to spowodować:

int foo(int a, int b)
{
   int c = 0;

   if (a > 0)
   {
        return a;
   }
   return 0;
}

Kompilowanie tego bez opcji -Wall lub -Wextra nie zwraca żadnych problemów. -Wall powie ci, że cto nigdy nie jest używane:

foo.c: W funkcji „foo”:

foo.c: 9: 20: ostrzeżenie: nieużywana zmienna „c” [-Wunused-zmienna]

-Wextra powie ci również, że twój parametr b nic nie robi:

foo.c: W funkcji „foo”:

foo.c: 9: 20: ostrzeżenie: nieużywana zmienna „c” [-Wunused-zmienna]

foo.c: 7: 20: ostrzeżenie: nieużywany parametr „b” [-Wunused-parameter] int foo (int a, int b)

Globalne zacienianie

Ten nieco trudny i nie pojawił się, dopóki nie -Wshadowzostał użyty. Zmodyfikujmy powyższy przykład, aby dodać, ale zdarza się, że istnieje globalny o tej samej nazwie co lokalny, co powoduje wiele zamieszania przy próbie użycia obu.

int c = 7;

int foo(int a, int b)
{
   int c = a + b;
   return c;
}

Po włączeniu opcji -Wshadow łatwo zauważyć ten problem.

foo.c: 11: 9: ostrzeżenie: deklaracja „c” przesłania globalną deklarację [-Wshadow]

foo.c: 1: 5: uwaga: ukryta deklaracja jest tutaj

Formatuj ciągi

Nie wymaga to żadnych dodatkowych flag w gcc, ale nadal było źródłem problemów w przeszłości. Prosta funkcja próbująca wydrukować dane, ale z błędem formatowania może wyglądać następująco:

void foo(const char * str)
{
    printf("str = %d\n", str);
}

Nie powoduje to wydrukowania łańcucha, ponieważ flaga formatowania jest niepoprawna, a gcc z radością powie ci, że prawdopodobnie nie tego chciałeś:

foo.c: W funkcji „foo”:

foo.c: 10: 12: ostrzeżenie: format „% d” oczekuje argumentu typu „int”, ale argument 2 ma typ „const char *” [-Wformat =]


To tylko trzy z wielu rzeczy, które kompilator może dla ciebie dwukrotnie sprawdzić. Istnieje wiele innych, takich jak używanie niezainicjowanej zmiennej, na którą zwracali uwagę inni.

Dom
źródło
7
W świecie osadzonym ostrzeżenia, które najbardziej mnie martwią, to ostrzeżenia „ possible loss of precision” i „ comparison between signed and unsigned”. Trudno mi zrozumieć, ilu „programistów” zignorowało je (w rzeczywistości nie jestem pewien, dlaczego nie są błędami)
Mawg mówi, że przywrócenie Moniki
3
W drugim przypadku @Mawg uważam, że głównym powodem, dla którego nie jest to błąd, jest to, że wynik sizeofjest niepodpisany, ale domyślny typ liczby całkowitej jest podpisany. Typ sizeofwyniku,, size_tjest zwykle używany do wszystkiego związanego z rozmiarem typu, takiego jak np. Wyrównanie lub liczba elementów tablicy / kontenera, podczas gdy liczby całkowite są ogólnie przeznaczone do użycia jako „ intchyba że jest to wymagane inaczej”. Biorąc pod uwagę, ilu ludzi uczy się w ten sposób intiteracji po kontenerach (w porównaniu intdo size_t), popełnienie błędu zepsułoby z grubsza wszystko. ; P
Justin Time - Przywróć Monikę
6

To jest konkretna odpowiedź na C i dlaczego jest to o wiele ważniejsze dla C niż dla czegokolwiek innego.

#include <stdio.h>
int main()
{
   FILE *fp = "some string";
}

Ten kod kompiluje się z ostrzeżeniem . Jakie są i powinny być błędy w prawie każdym innym języku na planecie (z wyjątkiem języka asemblera) są ostrzeżenia w C. Ostrzeżenia w C prawie zawsze są błędami w przebraniu. Ostrzeżenia należy naprawić, a nie ukryć.

Z gcc, robimy to jak gcc -Wall -Werror.

To był również powód wysokiej niechęci do korzystania z niektórych ostrzeżeń API niezabezpieczonych w MS. Większość osób programujących w C nauczyła się ciężko traktować ostrzeżenia jako błędy i pojawiły się te rzeczy, które po prostu nie były tego samego rodzaju i chciały nieprzenośnych poprawek.

Jozuego
źródło
5

OSTRZEŻENIA KOMPILERA JEST TWOIM PRZYJACIELEM (nie krzyczy, wielkie litery dla podkreślenia).

Pracuję na starszych systemach Fortran-77. Kompilator mówi mi cenne rzeczy: niedopasowanie typu danych argumentu w wywołaniu podprogramu, przy użyciu zmiennej lokalnej przed ustawieniem wartości w zmiennej, jeśli mam zmienną lub argument podprogramu, który nie jest używany. To prawie zawsze są błędy.

Unikanie długiego postu: Gdy mój kod kompiluje się czysto, 97% działa. Drugi facet, z którym pracuję, kompiluje się z wyłączonymi wszystkimi ostrzeżeniami, spędza godziny lub dni w debuggerze, a następnie prosi mnie o pomoc. Po prostu kompiluję jego kod z włączonymi ostrzeżeniami i mówię mu, co naprawić.

Mark Diaz
źródło
5

Zawsze należy włączać ostrzeżenia kompilatora, ponieważ kompilator często może powiedzieć ci, co jest nie tak z twoim kodem. Aby to zrobić, przekazujesz -Wall -Wextrado kompilatora.

Zazwyczaj należy traktować ostrzeżenia jako błędy, ponieważ zwykle oznaczają one, że coś jest nie tak z twoim kodem. Często jednak bardzo łatwo jest zignorować te błędy. Dlatego traktowanie ich jako błędów spowoduje niepowodzenie kompilacji, więc nie można zignorować błędów. Aby traktować ostrzeżenia jako błędy, przejdź -Werrordo kompilatora.

SS Anne
źródło
4

Ostrzeżenia kompilatora w C ++ są bardzo przydatne z kilku powodów.

1 - Pozwala pokazać, gdzie możesz popełnić błąd, który może wpłynąć na końcowy wynik twoich operacji. Na przykład, jeśli nie zainicjowałeś zmiennej lub wstawiłeś „=” zamiast „==” (istnieją tylko przykłady)

2 - Pozwala również pokazać, gdzie twój kod nie jest zgodny ze standardem c ++. Jest to przydatne, ponieważ jeśli kod jest zgodny z rzeczywistym standardem, łatwo będzie na przykład przenieść kod na inny format płyty.

Ogólnie rzecz biorąc, ostrzeżenia są bardzo przydatne, aby pokazać, gdzie masz błędy w kodzie, które mogą wpłynąć na wynik algorytmu lub zapobiec błędom, gdy użytkownik będzie korzystał z twojego programu.

Fizik26
źródło
4

Ignorowanie ostrzeżeń oznacza pozostawienie niechlujnego kodu, który nie tylko może powodować problemy w przyszłości dla kogoś innego, ale także sprawi, że ważne wiadomości kompilowane będą mniej zauważane przez Ciebie. Im więcej danych wyjściowych kompilatora, tym mniej ktokolwiek zauważy lub przeszkadza. Im czystsze tym lepiej. Oznacza to również, że wiesz, co robisz. Ostrzeżenia są bardzo nieprofesjonalne, nieostrożne i ryzykowne.

Kirk Augustin
źródło
4

Kiedyś pracowałem dla dużej firmy (Fortune 50), która produkowała elektroniczne urządzenia testujące.

Podstawowym produktem mojej grupy był program MFC, który z biegiem lat generował dosłownie setki ostrzeżeń. Które zostały zignorowane w prawie wszystkich przypadkach.

To cholerny koszmar, gdy pojawiają się błędy.

Po tym stanowisku miałem szczęście, że zostałem zatrudniony jako pierwszy programista w nowym startupie.

Zachęcałem do stosowania zasady „brak ostrzeżeń” dla wszystkich wersji, z poziomami ostrzeżeń kompilatora ustawionymi na dość głośny.

Naszą praktyką było używanie ostrzeżenia #pragma - push / disable / pop dla kodu, który deweloper był pewien, że jest w porządku, wraz z instrukcją dziennika na poziomie debugowania, na wszelki wypadek.

Ta praktyka działała dla nas dobrze.

Jim In Texas
źródło
1
Oddelegowany. #pragma warningnie tylko tłumi ostrzeżenia, służy podwójnym celom szybkiego komunikowania innym programistom, że coś jest zamierzone i nieprzypadkowe, i działa jako tag wyszukiwania do szybkiego lokalizowania potencjalnie problematycznych obszarów, gdy coś się psuje, ale naprawianie błędów / ostrzeżeń nie napraw to.
Justin Time - Przywróć Monikę
Masz rację, Justin, tak właśnie widziałem ostrzeżenie #pragma
Jim In Texas
3

Ostrzeżenie to błąd, który czeka na wystąpienie. Musisz więc włączyć ostrzeżenia kompilatora i uporządkować kod, aby usunąć wszelkie ostrzeżenia.

josyb
źródło
3

Jest tylko jeden problem z traktowaniem ostrzeżeń jako błędów: kiedy używasz kodu pochodzącego z innych źródeł (np. Bibliotek micro $ ** t, projektów open source), nie wykonali poprawnie swojej pracy, a skompilowanie kodu generuje tony ostrzeżeń.

I zawsze napisać kod, więc nie generuje żadnych ostrzeżeń lub błędów, i oczyścić go aż kompiluje bez generowania hałasu obcych. Śmieci, z którymi muszę pracować, przerażają mnie i jestem zdumiony, gdy muszę zbudować duży projekt i oglądać strumień ostrzeżeń, w którym kompilacja powinna jedynie ogłaszać, które pliki przetwarzała.

Dokumentuję również swój kod, ponieważ wiem, że rzeczywisty koszt oprogramowania wiąże się głównie z konserwacją, a nie z jego początkowym napisaniem, ale to inna historia ...

FKEinternet
źródło
Nie pukaj, są dobre pieniądze na konsultacje dla osób, które mogą czytać ostrzeżenia kompilatora głośno klientom.
Móż
Kod z innych źródeł generujących ostrzeżenia nie musi oznaczać, że autorzy byli niechlujni. Może to również oznaczać, że skompilowali kod z innym kompilatorem, który wygenerował inny zestaw ostrzeżeń. Kod może kompilować się bez ostrzeżeń na jednym kompilatorze i generować ostrzeżenia na innym. A może to po prostu inny zestaw opcji ostrzegawczych; np. używali, -Walla ty korzystasz -Wall -Wextra.
celtschk
2

Niektóre ostrzeżenia mogą oznaczać możliwy błąd semantyczny w kodzie lub możliwy UB. Np. ;Po if(), nieużywana zmienna, zmienna globalna maskowana lokalnie lub porównanie podpisanych i niepodpisanych. Wiele ostrzeżeń związanych jest z analizatorem kodu statycznego w kompilatorze lub z naruszeniami normy ISO wykrywanymi w czasie kompilacji, które „wymagają diagnostyki”. Chociaż takie przypadki mogą być zgodne z prawem w jednym konkretnym przypadku, w większości przypadków byłyby wynikiem problemów projektowych.

Niektóre kompilatory, np. Gcc, mają opcję wiersza poleceń do aktywacji trybu „ostrzeżenia jako błędy”, jest to miłe, choć okrutne narzędzie do edukacji początkujących programistów.

Swift - piątek ciasto
źródło
1

Fakt, że kompilatory C ++ akceptują kod kompilacyjny, który w oczywisty sposób powoduje w ogóle nieokreślone zachowanie, jest główną wadą kompilatorów. Powodem, dla którego tego nie naprawiają, jest prawdopodobnie uszkodzenie niektórych kompilacji.

Większość ostrzeżeń powinna być krytycznymi błędami, które uniemożliwiają ukończenie kompilacji. Domyślne wyświetlanie błędów i kompilacja mimo wszystko są niepoprawne, a jeśli nie przesłonisz ich, aby traktować ostrzeżenia jako błędy i pozostawić niektóre ostrzeżenia, prawdopodobnie zakończy się awaria programu i wykonywanie losowych czynności.

Jason Livesay
źródło
Jak na ironię, wiele niezdefiniowanych zachowań nie powoduje ostrzeżeń, ale po cichu kompiluje się dobrze w paskudną małą bombę zegarową. ; P
Justin Time - Przywróć Monikę
1
Problem polega na tym, że jeśli standard wymaga komunikatu o błędzie, ten komunikat o błędzie musi zostać wyświetlony we wszystkich przypadkach, w których występuje problem, ale nigdy, jeśli problem nie występuje. Ale w przypadkach takich jak niezdefiniowane zachowanie decyzja może być niemożliwa. Rozważmy na przykład następujący kod: int i; if (fun1()) i=2; if (fun2()) i=3; char s="abcde"[i];Ten kod eksponatów zachowanie niezdefiniowane wtedy i tylko wtedy, gdy oba fun1()i fun2()może powrócić falsena tym samym realizacji funkcji. Co może, ale nie musi, być prawdą, ale jak ma to powiedzieć kompilator?
celtschk
-2

Zdecydowanie powinieneś włączyć ostrzeżenia kompilatora, ponieważ niektóre kompilatory źle zgłaszają niektóre typowe błędy programistyczne, w tym następujące:

-> inicjalizacja zmiennej zostaje zapomniana -> zwracana wartość z funkcji jest pomijana -> proste argumenty w rodzinach printf i scanf, które nie pasują do ciągu formatu, funkcja jest używana bez uprzedniego zadeklarowania, choć dzieje się tak tylko w c

Ponieważ funkcje te można wykryć i zgłosić, zazwyczaj nie są one domyślnie; więc tej funkcji należy jawnie zażądać za pomocą opcji kompilatora.

Hasan Raza
źródło
-32

Uspokój się: nie musisz, nie jest to konieczne. -Wall i -Werror zostały zaprojektowane przez maniaków refaktoryzujących kod: zostały wymyślone przez programistów kompilatorów, aby uniknąć zepsucia istniejących kompilacji po aktualizacji kompilatora po stronie użytkownika . Ta funkcja to nic, ale wszystko o decyzji o przerwaniu lub nie, aby przerwać kompilację.

Korzystanie z niego, czy nie, zależy wyłącznie od twoich preferencji. Używam go cały czas, ponieważ pomaga mi to naprawić błędy.

sqr163
źródło
19
Chociaż nie jest to obowiązkowe , zaleca się ich stosowanie
Spikatrix
18
-Wall and -Werror was designed by code-refactoring maniacs for themselves.[potrzebne źródło]
YSC
22
Wygląda na to, że sam sobie przeczysz. Jeśli „używasz go cały czas, ponieważ pomaga on naprawiać [swoje] błędy”, to czy nie warto uczyć nowych programistów, aby robili to wszędzie od samego początku? Nie sądzę, aby to pytanie dotyczyło możliwości kompilacji bez, -Walla -Werrorjedynie pytanie, czy to dobry pomysł. Które z ostatniego zdania brzmi tak, jakbyś powiedział, że tak.
scohe001,
12
Gdy uzyskasz więcej doświadczenia w utrzymywaniu kodu, który nie został napisany przez Ciebie, ponownie przeczytaj tę odpowiedź.
Thorbjørn Ravn Andersen
To nie jest pomocna odpowiedź. W pytaniu PO są 4 znaki zapytania. Ile odpowiedzi odpowiada?
Anders