Bezpieczne operowanie łańcuchami C
Kiedy piszesz w języku C zapewne nierzadko musisz korzystać z łańcuchów typu char* lub char[]. Czy zdarza Ci się, że pospiesznie wstawiasz proste kopiowanie strcpy ? Czy jesteś świadomy tego, iż w przypadku kiedy łańcuch źródłowy nie ma ściśle określonej długości (czyli w zasadzie nie jest stałą) takie proste kopiowanie może być niebezpieczne i skończyć się nadpisaniem danych w pamięci, a nawet wyłażeniem się aplikacji ? Czy chcesz poznać kilka rad jak zabezpieczać swój kod przed takim sytuacjami ?Przyczyną dla których funkcje takie jak strcpy czy sprintf są niebezpieczne jest fakt, że nie posiadają one kontroli zapełnienia bufora docelowego. Istnieją jednak inne wersje tych funkcji. W ich nazwie pojawia się litera ‘n’ (strncpy, snprintf), która oznacza, że funkcja nigdy nie wstawi do bufora więcej znaków niż podałeś w jednym z parametrów.
No dobrze dosyć teorii. Popatrz jak skorzystać z tego w praktyce.
#define STR_MAX_LEN 20 char buf[STR_MAX_LEN + 1]; // 20 znaków + NULL strncpy(buf, sSourceString, STR_MAX_LEN); buf[STR_MAX_LEN] = 0;// ostatni znak zawsze musi być NULL
Zadeklaruj zmienną buf tak aby mogła przechować 20 znaków i oczywiście nie możesz zapomnieć o znaku NULL kończącym łańcuch (ang. Null-Terminated string). Następnie skopiuj maksymalnie 20 znaków. Mógłbyś się także posłużyć wyrażeniem “sizeof buf - 1” zamiast stałej STR_MAX_LEN ale ta wersja wydaje mi się krótsza. Dzięki temu łatwo możesz ustalić maksymalny rozmiar przetwarzanego łańcucha robiąc to w jednym miejscu bez konieczności przeczesywania kodu w poszukiwaniu użycia rozmiaru tablicy. Ostatnia operacja jest szczególnie istotna w sytuacji, gdy łańcuch źródłowy będzie miał 20 lub więcej znaków. W takim przypadku do zmiennej buf nie zostanie skopiowany znak NULL, więc musisz go tam sam wstawić na pozycji 20 (ostatni znak w tablicy docelowej). W innych przypadkach, czyli gdy łańcuch jest krótszy niż 20 znaków, strncpy skopiuje także znak ‘\0′.
Funkcja snprintf może także stanowić bezpieczną alternatywę dla swojej siostry. Posłużysz się nią za chwilę podczas tworzenia nazwy pliku na podstawie danych binarnych. Funkcja ta jednak działa nieco inaczej i przekazywany maksymalny rozmiar powinien już uwzględniać miejsce na znak NULL. Funkcja ta zawsze zakańcza łańcuch, a dla potrzeb formatowania danych używa nie więcej niż przekazany rozmiar - 1 znaków.
#define MAX_FILE_NAME_SIZE 10 char sFileName[MAX_FILE_NAME_SIZE + 1]; int iCharsPrinted; FILE* fHandle; iCharsPrinted = snprintf(sFileName, sizeof sFileName, "%lu", ulCurrentDay); fHandle = fopen(sFileName, "r+b");
Podobnie jak to było w poprzednim przykładzie zadeklaruj zmienną, która tym razem przechowa nazwę pliku. Nazwę pliku stworzysz z numeru określającego aktualny dzień jest to liczba typu unsigned long (stąd przedrostek ‘ul’ przed CurrentDay). Ciekawą i czasami przydatną cechą tej rodziny funkcji jest to, że zwracają one ilość znaków wydrukowanych.
Pewnie zastanawiasz się po co taka informacja ?
Wyobraź sobie, że musisz przesyłać takie różnie sformatowane łańcuchy przez sieć lub przez port szeregowy do jakiegoś urządzenia ? Po dynamicznym utworzeniu łańcucha od razu wiesz ile bajtów masz do wysłania. Nie musisz korzystać z funkcji określającej długość łańcucha strlen.
A teraz czas na podsumowanie porad:
[1] Staraj się używać funkcji z kontrolą ilości znaków (strncpy, snprintf) - szczególnie tam gdzie masz do czynienia z danymi wprowadzanymi przez użytkownika
[2] Właściwie określaj rozmiar łańcucha, a unikniesz błędów ucinania - unsigned long może dać liczbę o 10-ciu znakach
[3] Nigdy nie zapominaj o znaku NULL na końcu łańcucha
Tags: C++
Najświeższe komentarze