KursyPoradnikiInspirujące DIYForum

3 narzędzia, które powinien znać każdy programista

3 narzędzia, które powinien znać każdy programista

Pisanie programów może być znacznie łatwiejsze. Wszystko za sprawą kilku prostych narzędzi, które mogą przyspieszyć prace programisty.

Tym razem pora na omówienie 3 narzędzi, dzięki którym możliwe jest automatyczne formatowanie kodu oraz jego statyczna i dynamiczna analiza.

ClangFormat – automatyczne formatowanie kodu

Popularny ClangFormat to narzędzie, które pomaga programistom zadbać o poprawne formatowanie kodu. Docenią je osoby, które musiały kiedyś analizować niesformatowany kod, oraz wszyscy ci, którzy brali udział w różnych programistycznych dyskusjach. Czy nawias klamrowy powinien znaleźć się w nowej linii czy w tej samej co nazwa funkcji? Czy podczas deklaracji wskaźników znak gwiazdki powinien znaleźć się przy typie zmiennej czy przy nazwie wskaźnika?

ClangFormat to zestaw narzędzi zbudowanych na bazie LibFormat. Może on być używany podczas pracy z kodem pisanym w C, C++, Java, JavaScript, Objective-C, Protobuf i C#. ClangFormat pozwala na stosowanie predefiniowanych stylów formatowania, np. google, chromium, mozilla, webkit, microsoft.

Możliwe jest też wykorzystanie własnego pliku konfiguracyjnego – za jego pomocą można dostosować narzędzia do naszych potrzeb i przyzwyczajeń. Przykładowy plik konfiguracyjny wygląda następująco:

Jak zainstalować ClangFormat?

Osoby korzystające z Debiana (i podobnych systemów) zainstalują program za pomocą jednej linijki:

Osoby, które korzystają z Windowsa, mogą np. zainstalować LLVM. W trakcie instalacji tego programu trzeba zadbać o to, aby zaznaczona była opcja dodania LLVM do systemowej zmiennej PATH.

Wybór odpowiedniej opcji podczas instalacji LLVM

Wybór odpowiedniej opcji podczas instalacji LLVM

Czasami może się zdarzyć, że pomimo zaznaczenia tej opcji wpis do zmiennej środowiskowej PATH nie zostanie dodany. Warto więc sprawdzić to ręcznie po instalacji programu. W tym celu otwieramy menu start, a następnie w wyszukiwarkę systemową wpisujemy „Edytuj zmienne środowiskowe”. Pojawi się nowe okno – klikamy tam przycisk „Zmienne środowiskowe”, a następnie na liście rozwijanej, która opisana jest jako „Zmienne systemowe”, wybieramy wpis „Path” i klikamy przycisk edycji. Jeśli nie ma tam wpisu dotyczącego LLVM, to dodajemy go ręcznie, np.: „C:\Program Files\LLVM\bin”.

Jak korzystać z ClangFormat?

Korzystanie z programu ogranicza się właściwie do wydania jednego polecenia. Zanim do tego dojdzie, musimy jednak umieścić plik konfiguracyjny w katalogu głównym danego projektu. Może to być nasza konfiguracja lub jeden z dostępnych formatów, który został wspomniany już wcześniej.

Zakładając, że chcemy sformatować plik main.cpp, wywołujemy:

Oczywiście przed wydaniem tego polecenia musimy przejść w konsoli do katalogu, w którym znajduje się nasz projekt. W tym celu na Linuksie i Windowsie możemy posługiwać się poleceniem cd, np.:

To już jest właściwie wszystko. Natychmiast po wydaniu tego polecenia kod zostanie sformatowany zgodnie z dostarczoną konfiguracją. Osoby korzystające z Linuksa mogą też w łatwy sposób uruchomić narzędzie dla całego projektu. W tym celu w głównym katalogu projektu wystarczy wydać polecenie:

Efekt działania ClangFormat (zintegrowanego ze środowiskiem) wygląda następująco:

Jeśli stosujemy własny plik konfiguracyjny, to trzeba pamiętać, że ClangFormat obsługuje konfigurację zapisaną w formacie, który nazywany jest „UTF-8 (without BOM)”. Jeśli nasz plik konfiguracyjny będzie źle zakodowany, to otrzymamy błąd, który będzie wyglądał następująco:

Aby uniknąć tego problemu, trzeba zapisać plik z odpowiednim kodowaniem znaków – można to zrobić np. za pomocą Notepad++ lub Visual Studio Code.

Jak zintegrować ClangFormat z IDE?

Wywoływanie narzędzia ClangFormat jest znacznie wygodniejsze, jeśli zintegrujemy je bezpośrednio z IDE, w którym piszemy programy. Większość popularnych edytorów posiada już taką możliwość.

Na przykład w przypadku Visual Studio Code można zainstalować gotowe rozszerzenie Clang-Format. Gdy jest ono dodane do IDE, wystarczy, że otworzymy plik ustawień użytkownika (Ctrl + Shift + P). Następnie wpisujemy „Preferences: Open Settings”, z listy wybieramy „Preferences: Open Settings (JSON)” i dopisujemy do pliku następującą zawartość:

ClangFormat – jak generować własne konfiguracje?

Warto poświęcić trochę czasu, aby przygotować swoją konfigurację formatowania, zgodnie z którym zadziała ClangFormat – tutaj pomocna będzie strona, na której opisano wszystkie parametry. Można też wykorzystać gotowe style, które są już wbudowane w to narzędzie. Konfigurację dla danego stylu można wygenerować za pomocą poniższego polecenia (trzeba tylko pamiętać o kodowaniu pliku):

Liczba dostępnych parametrów pliku konfiguracyjnego może przytłaczać. Na szczęście nie trzeba tego wszystkiego edytować ręcznie. Można wspomóc się interaktywnym edytorem online, dzięki któremu możliwe jest obserwowanie wszystkich zmian „na żywo”.

Cppcheck – analiza błędów w kodzie

Pierwszym narzędziem, które wskaże błędy w kodzie, jest kompilator. Jeśli napiszemy kod, który nie jest zgodny z danym standardem, to otrzymamy błąd kompilacji. Można też napisać kod, który nie narusza standardu, ale może spowodować tzw. zachowanie niezdefiniowane (ang. undefined behaviour).

Tu przyda się Cppcheck, który potrafi wykonywać analizę statyczną kodu C/C++. Program ten analizuje nieskompilowany kod pod kątem tego, czy występują tam miejsca, które można potencjalnie określić jako mało bezpieczne. Co ciekawe, Cppcheck został zaprojektowany, aby móc analizować kod C/C++, nawet jeśli ma on niestandardową składnię, jak np. w projektach embedded.

Narzędzie to może przydać się również podczas programowania systemów embedded

Narzędzie to może przydać się również podczas programowania systemów embedded

Jak zainstalować Cppcheck?

Podobnie jak w poprzednim przykładzie, użytkownicy Linuksa mogą wydać jedno polecenie:

Dla Windowsa konieczne jest pobranie instalatora ze strony projektu. Następnie należy dodać ręcznie odpowiednią ścieżkę do zmiennej środowiskowej PATH. Proces ten przebiega tak samo jak w przypadku opisanego wcześniej ClangFormat – tym razem jednak zmienna ma wskazywać na ścieżkę Cppcheck, czyli np.: „C:\Program Files\Cppcheck”.

Jak korzystać z Cppcheck?

Korzystanie z Cppcheck jest również bardzo proste, ale tutaj podczas pierwszych testów przyda się jakiś przykładowy kod programu. Posłużmy się więc takim fragmentem:

Czy ten kod jest poprawny? Spróbujmy skompilować taki program. Oczywiście potrzebny do tego jest kompilator. Użytkownicy Linuksa mogą go zainstalować za pomocą poniższej linijki, a osoby pracujące na Windowsie powinny mieć już ten kompilator, bo został zainstalowany razem z LLVM.

Kompilację na Ubuntu wykonujemy za pomocą wywołania:

A na Windowsie przy użyciu LLVM:

Kolejne wywołania przedstawione zostaną za pomocą g++, jednak będą one tak samo działać w przypadku clang++ na Windowsie, gdy zmienimy g++ na clang++. Niezależnie od używanego systemu kompilator nie znajdzie w tym kodzie żadnych błędów, więc w katalogu z kodem źródłowym pojawi się nasz program, który na Linuksie będzie nazywał się a.out, a na Windowsie a.exe

Taki program można już uruchamiać jak każdy inny. Na Ubuntu:

Na Windowsie:

W wyjściu konsoli w przypadku Ubuntu możemy zobaczyć błąd „Segmentation fault (core dumped)”, czyli naruszenie pamięci i wkroczenie do akcji systemu, który wyłączył nasz program, zanim ten narobił bałaganu. W przypadku Windowsa nie dostajemy jednak żadnej informacji o naruszeniu pamięci.

Sprawdźmy, co do powiedzenia ma Cppcheck. Najpierw musimy w terminalu przejść do katalogu z tym projektem. Następnie wywołujemy poniższe polecenie (identycznie dla Ubuntu i Windowsa):

W wyniku wywołania tego polecenia powinniśmy otrzymać następujący wynik:

Otrzymaliśmy całą listę problemów z tym programem. Osoby początkujące mogą czuć się trochę zagubione, ale bardziej doświadczeni programiści wykorzystają tego typu informacje. Spróbujmy teraz poprawić ten kod. Mógłby on wyglądać następująco (jeśli taki był zamiar autora).

Tym razem wskaźnikowi przypisaliśmy adres zmiennej, dzięki czemu nie dokonujemy już dereferencji wskaźnika nullptr (undefined behaviour). Następnie usunęliśmy linię, w której dynamicznie zaalokowaliśmy pamięć o rozmiarze jednego inta (i tak z niej nie korzystaliśmy).

W dalszej kolejności zmiennej b przypisujemy wartość przekazaną przez użytkownika – dzięki temu kod nie wykonuje już warunkowego skoku, który jest zależny od niezainicjowanej zmiennej. Po dokonaniu zmian i wywołaniu narzędzia nie otrzymujemy już żadnych problemów, a nasz program zadziała prawidłowo.

Poznaj więcej możliwości Cppcheck

Cppcheck możemy też oczywiście wywołać dla całego projektu, uruchamiając go w katalogu głównym projektu i podając ścieżki do nagłówków projektowych:

Oczywiście przedstawione powyżej wywołanie narzędzia to wersja podstawowa. Wszystkie opcje, które są wbudowane w Cppcheck, znajdziemy w dokumentacji lub w pomocy ($ cppcheck --help). Warto też pamiętać, że program ten posiada graficznego klienta:

Valgrind – analiza dynamiczna kodu

Wspomniany wcześniej Cppcheck jest narzędziem do analizy statycznej. Oznacza to, że dokonuje ono analizy na podstawie kodu i nie wymaga w tym celu uruchomienia testowanego programu. Natomiast Valgrind jest narzędziem do analizy dynamicznej w tzw. runtimie, czyli analiza odbywa się w czasie działania programu (kosztem jego szybkości).

Jak zainstalować Valgrind?

Valgrind nie jest dostępny na Windowsa, ale istnieją alternatywne rozwiązania tego typu. W przypadku Linuksa instalacja jest oczywiście bardzo prosta i sprowadza się do jednej linijki:

Jak korzystać z Valgrinda?

Również tutaj przyda nam się jakiś kod testowy – wykorzystajmy więc dokładnie ten sam program co w przypadku eksperymentów z Cppcheck.

Aby uruchomić ten program pod Valgrindem, musimy go najpierw skompilować, i to najlepiej w trybie debug (dodając flagę g). Następnie uruchamiamy analizę dynamiczną:

W wyniku działania tego programu otrzymamy taki raport:

Otrzymujemy w ten sposób szereg informacji – najistotniejsze to:

Zgłoszenie to mówi nam, że w programie jest warunek, który działa na podstawie niezainicjalizowanej zmiennej – rzeczywiście tak jest, zmienna b nie ma przypisanej wartości.

Druga ważna uwaga wygląda następująco:

Tłumacząc na polski, w linii 20 mamy do czynienia z niepoprawnym odczytem pamięci – rzeczywiście, w tej linii próbujemy wykonać dereferencje na wskaźniku nullptr. Następnie Valgrind informuje nas, jakim sygnałem system operacyjny zakończył nasz program:

To jednak jeszcze nie koniec problemów – ostatnim jest brak zwolnienia pamięci, którą zaalokowaliśmy dynamicznie. Valgrind informuje nas o tym we fragmencie:

Dynamiczna alokacja pamięci za pomocą operatora new nie jest niczym złym, ale brak jej zwolnienia – już tak. Jeśli wywołamy Valgrinda z dwoma dodatkowymi argumentami:

Otrzymamy dodatkową informację o miejscu wycieku pamięci:

Posiadając takie informacje, jesteśmy w stanie sprawdzić poprawność naszego programu i znaleźć przyczynę ewentualnego problemu. Błędy związane z zarządzaniem pamięcią potrafią być bardzo trudne do znalezienia, więc pomoc tego typu narzędzi jest niezwykle cenna.

Narzędzia wspomagające pracę programisty

Narzędzia wspomagające pracę programisty

Valgrind – co jeszcze warto wiedzieć?

W niektórych sytuacjach warto wcześniej sprawdzić program, uruchamiając go w trybie debugowania pod debuggerem, który powinien wskazać znajdujące się w nim błędy. Gdy ta metoda zawiedzie, wtedy w drugiej kolejności powinniśmy skorzystać z Valgrinda. Narzędzie to możemy też wykorzystać do sprawdzenia poprawności gotowego programu. Możemy wówczas uruchomić go pod Valgrindem, sprawdzić każdą ścieżkę i przejrzeć raport z analizy.

Niestety za pomocą Valgrinda nie uda nam się zdebugować kodu, który działa na uC. Możemy jednak postarać się podczas pisania kodu dla mikrokontrolera, aby był on modułowy. Dzięki temu można będzie przetestować jego fragmenty na komputerze.

Podsumowanie

Celem tego wpisu było krótkie przedstawienie przydatnych narzędzi, które warto znać, bo mogą się przydać nawet w amatorskich, hobbystycznych projektach. ClangFormat będzie pomocny nawet dla osób początkujących. Z kolei korzystanie z narzędzi do statycznej i dynamicznej analizy programu wymaga trochę większego doświadczenia, ale warto chociaż pamiętać o takiej możliwości. 

Czy wpis był pomocny? Oceń go:

Średnia ocena 4.1 / 5. Głosów łącznie: 48

Nikt jeszcze nie głosował, bądź pierwszy!

Artykuł nie był pomocny? Jak możemy go poprawić? Wpisz swoje sugestie poniżej. Jeśli masz pytanie to zadaj je w komentarzu - ten formularz jest anonimowy, nie będziemy mogli Ci odpowiedzieć!

Programy opisane w tym artykule mogą być też świetnym narzędziem edukacyjnym. Nie ma nic lepszego od nauki na własnych błędach. Podczas analizy napisanego przez nas kodu zostaniemy poinformowani o złych praktykach i problematycznych miejscach. Dzięki takim informacjom od razu wiadomo, w jakich aspektach warto się jeszcze trochę podszkolić.

Autor: Mateusz Patyk

3 różnice w programowaniu: hobbystycznie vs. komercyjnie
3 różnice w programowaniu: hobbystycznie vs. komercyjnie

W pewnym momencie każdy programista musi przestawić się z hobbystycznego kodowania na bardziej profesjonalne podejście do tematu. Czym różni... Czytaj dalej »

formatowanie, kod, narzędzia, programowanie

Trwa ładowanie komentarzy...