Specyfikacje języków, w tym C i C++, są często luźno napisane. Narzędzie zwane lint może pomóc w znalezieniu niebezpiecznych i nieportowalnych konstrukcji w kodzie, zanim kompilator zamieni je w błędy w czasie działania.
Każdy, kto pisał program, musiał debugować kod. W wielu przypadkach, po wpatrywaniu się w kod przez wiele godzin, w końcu zdajemy sobie sprawę, że zrobiliśmy coś głupiego, jak odczyt niezainicjowanej zmiennej lub indeksowanie poza końcem tablicy. Wraz z doświadczeniem, częstotliwość występowania tych „doh!” błędów maleje; ale nadal mogą być one niezwykle frustrujące i kosztowne.
To, czego potrzebujemy w takich momentach, to nieskończenie cierpliwy, analitycznie skrupulatny ekspert, który sprawdzi każdy centymetr naszego kodu. Ten ekspert musi być bardziej dokładny niż jakikolwiek kompilator i powinien w całkowicie beznamiętny sposób raportować wszystko, co jest potencjalnie złe w naszym kodzie. Narzędzie programistyczne o nazwie lint jest najbliższe temu ideałowi. Jeśli nie wiesz czym jest lint, lub go nie używasz, sugeruję abyś przeczytał dalej.
Lint jest narzędziem podobnym do kompilatora w tym sensie, że analizuje pliki źródłowe C/C++. Sprawdza te pliki pod kątem poprawności składniowej. Aby sprawdzić plik o nazwie foo.c, zazwyczaj wpisujemy:
lint foo.c
w wierszu poleceń. Oczywiście, istnieje również wiele opcjonalnych przełączników wiersza poleceń.
Standardowe cechy
Gdy kompilator zajmuje się przede wszystkim generowaniem kodu, lint jest całkowicie poświęcony sprawdzaniu kodu pod kątem niezliczonych możliwych usterek. Kluczowym słowem jest tutaj możliwe. Tylko dlatego, że lint zaznacza fragment kodu do sprawdzenia, nie musi to oznaczać, że problem wystąpi, gdy skompilujesz ten kod za pomocą konkretnego kompilatora.
Lint został zaprojektowany jako kompilator-agnostyk i w rzeczywistości często skupia twoją uwagę na fragmentach kodu, które mogą skutkować innym zachowaniem w zależności od użytego kompilatora.
Szczegółowa lista problemów, które sprawdza lint, różni się w zależności od implementacji i wersji narzędzia. Jednakże, większość odmian linta sprawdza przynajmniej następujące kwestie:
- Możliwość użycia niezainicjowanych zmiennych
- Możliwość indeksowania poza granicami tablicy
- De-
- Nieprawidłowe przypisania (np. if (a = b))
- Niezgodność typów zmiennych (np. foo zadeklarowane jako double w jednym pliku i użyte jako long w innym)
- Niezgodność typów zmiennych (np. w innym)
- Potencjalnie niebezpieczne kombinacje typów danych
- Nieużywane zmienne
- Nieosiągalny kod
- Pliki nagłówkowe dołączane wielokrotnie i/lub niepotrzebnie
- Nie-przenośne konstrukcje
Lint sprawdza tak wiele rzeczy, że zwykle narzędzie to produkuje tyle błędów i ostrzeżeń, ile jest linii kodu w pliku źródłowym, który jest wprowadzany. To bardzo odstrasza wielu potencjalnych użytkowników, ponieważ ich nastawienie jest zwykle takie: „to narzędzie jest tak wybredne, że aż śmieszne”. Jednakże, praca z każdym ostrzeżeniem i poprawianie go może być satysfakcjonującym ćwiczeniem. Jako przykład, rozważ ten pozornie niewinny kod:
main(void){int i;for(i = 0; i
Jaka będzie ostatnia liczba wypisana po uruchomieniu tego programu? Jeśli odpowiedziałeś „100”, to jesteś w błędzie. To jest całkowicie poprawny program i skompiluje się bez ostrzeżenia na większości kompilatorów. Jednakże, lint będzie narzekał na coś. Jeśli nie możesz dostrzec problemu przez inspekcję, to proponuję pobrać kod z www.embedded.com/code.htm i uruchomić go przez swój ulubiony debugger. Uwaga i duża podpowiedź: nie wpisuj tego programu po prostu do swojego edytora. Zaobserwuj, ile czasu zajmie Ci znalezienie tego problemu – a następnie zadaj sobie pytanie, czy przedzieranie się przez ostrzeżenia linta nie jest takie złe.
Wyciąganie linta
Niezależnie od powyższego, nieco wymyślonego przykładu, jakiego rodzaju korzyści w świecie rzeczywistym można się spodziewać po zajęciu się wszystkimi ostrzeżeniami generowanymi przez linta? Moje doświadczenia z typowym projektem dotyczącym małego mikrokontrolera (całkowity rozmiar kodu poniżej 32KB) były następujące:
- Lint znalazł dwa lub trzy oczywiste błędy – zanim jeszcze zacząłem testować kod.
- Dowiedziałem się czegoś o języku C za każdym razem, gdy go uruchamiałem.
- Mój końcowy kod był czystszy, ponieważ lint poinformował mnie o nieużywanych zmiennych, makrach i plikach nagłówkowych, które można było bezpiecznie usunąć.
- Byłem lepiej poinformowany o potencjalnych problemach z przenoszeniem kodu.
Gdy weźmiemy pod uwagę powyższe, prawdopodobnie nie będzie dla was zaskoczeniem, że organizacje, które naprawdę poważnie podchodzą do jakości kodu, często nalegają nie tylko na to, aby cały kod kompilował się bez ostrzeżeń (co jest stosunkowo trywialne do osiągnięcia), ale także, aby był „wolny od ostrzeżeń” – to znaczy, aby nie generował żadnych ostrzeżeń za pomocą linta. Jest to kryterium znacznie trudniejsze do osiągnięcia.
Figura 1: Jak lint pasuje do procesu tworzenia oprogramowania
Warto przyjrzeć się, gdzie lint pasuje do procesu tworzenia oprogramowania. Mój ogólny tok projektowania pokazano na rysunku 1. Gdy mam już kod, który się kompiluje, dodaję mu lint. Jeśli kod przejdzie przez lint w porządku, jest bardzo mało prawdopodobne, że będę zakłopotany podczas przeglądu kodu. Podczas fazy debugowania normalną rzeczą jest wprowadzanie zmian w kodzie. Jednakże, gdy kod jest już zdebugowany, a przed przekazaniem go do testów, zazwyczaj ponownie go lintuję. Dlaczego? Zawsze jestem zdumiony liczbą niechlujnych konstrukcji kodowych, które pojawiają się podczas debugowania kodu. Lint jest świetnym narzędziem do identyfikacji tego niechlujnego kawałka kodu, który został tam umieszczony, aby pomóc w debugowaniu czegoś, a następnie szybko o nim zapomniano.
Źródła linta
Lint jest standardowym narzędziem w większości systemów uniksowych. W przypadku komputerów PC, często trzeba je kupić, albo znaleźć wersję darmową lub shareware. Jeśli kupisz linta, możesz być pewien, że będą to najlepsze pieniądze, jakie kiedykolwiek wydałeś w swojej karierze. Większość wariantów linta jest stosunkowo niedroga (mniej niż 1000$) i warta każdego grosza.
Nawiasem mówiąc, możesz się zastanawiać, jak dobrze lint radzi sobie z tymi wszystkimi paskudnymi, małymi rozszerzeniami kompilatora, które są tak powszechne w rozwoju systemów wbudowanych. Jest to obszar, w którym komercyjne programy przebijają standardowe oferty. W szczególności, niektóre wersje linta pozwalają na znaczne dostosowanie reguł linta, tak aby wszystkie te rozszerzenia były poprawnie obsługiwane. W niektórych przypadkach, definicje kompilatorów są nawet dostarczane przez dostawcę linta. W innych, możesz je uzyskać od dostawcy kompilatora.
Jeśli nie masz dostępu do linta, ale używasz narzędzi GNU (na Uniksie lub PC), po prostu użyj flagi -Wall w gcc, aby osiągnąć około 80% tej samej funkcjonalności.
Więc, dla wszystkich neofitów, kup sobie kopię linta i używaj go. Jeśli nic innego, twój szef będzie pod wrażeniem dojrzałości twojego kodu. Dla wszystkich doświadczonych hacków, którzy nie używają linta – uważaj! Nowi faceci, którzy go używają, mogą ci się pokazać.
Nigel Jones jest konsultantem w Maryland. Kiedy nie jest pod wodą, można go znaleźć pracującego nad wieloma różnymi projektami wbudowanymi. Lubi słuchać czytelników i można się z nim skontaktować pod adresem .