166 ROZDZIAŁ 23. POWSZECHNE PRAKTYKI void free_string(struct string *s) { assert (s != NULL); free(s-data); /* zwalniamy pamięć zajmowaną przez strukturę */ free(s); /* usuwamy samą strukturę */ } Często łączy się destruktory z zerowaniem zwolnionych wskaźników. Czasami dobrze jest ukryÄ deï¬nicjÄ obiektu, żeby mieÄ pewnoÅÄ, że użytkownicy nie utwo- rzÄ
go rÄcznie. Aby to zapewniÄ struktura jest deï¬niowana w pliku źródÅowym (lub prywat- nym nagÅówku niedostÄpnym dla użytkowników) zamiast w pliku nagÅówkowym, a deklaracja wyprzedzajÄ
ca jest umieszczona w pliku nagÅówkowym: struct string; struct string *create_string(const char *initial); void free_string(struct string *s); Zerowanie zwolnionych wskaźników Jak powiedziano już wczeÅniej, po wywoÅaniu free() dla wskaźnika, staje siÄ on âwiszÄ
- cym wskaźnikiemâ. Co gorsze, wiÄkszoÅÄ nowoczesnych platform nie potraï¬ wykryÄ, kiedy taki wskaźnik jest używany zanim zostanie ponownie przypisany. Jednym z prostych rozwiÄ
zaÅ tego problemu jest zapewnienie, że każdy wskaźnik jest zerowany natychmiast po zwolnieniu: free(p); p = NULL; Inaczej niż w przypadku âwiszÄ
cych wskaźnikówâ, na wielu nowoczesnych architektu- rach przy próbie użycia wyzerowanego wskaźnika pojawi siÄ sprzÄtowy wyjÄ
tek. Dodatkowo, programy mogÄ
zawieraÄ sprawdzanie bÅÄdów dla zerowych wartoÅci, ale nie dla âwiszÄ
cych wskaźnikówâ. Aby zapewniÄ, że jest to wykonywane dla każdego wskaźnika, możemy użyÄ makra: #define FREE(p) do { free(p); (p) = NULL; } while(0) (aby zobaczyÄ, dlaczego makro jest napisane w ten sposób, zobacz Konwencje pisania makr) Przy wykorzystaniu tej techniki destruktory powinny zerowaÄ wskaźnik, który przekazuje siÄ do nich, wiÄc argument musi byÄ do nich przekazywany przez referencjÄ. Na przykÅad, oto zaktualizowany destruktor z sekcji Konstruktory i destruktory: void free_string(struct string **s) { assert(s != NULL && *s != NULL); FREE((*s)-data); /* zwalniamy pamiÄÄ zajmowanÄ
przez strukturÄ */ FREE(*s); /* usuwamy strukturÄ */ } Niestety, ten idiom nie jest wstanie pomóc w wypadku wskazywania przez inne wskaźniki zwolnionej pamiÄci. Z tego powodu niektórzy eksperci C uważajÄ
go za niebezpieczny, jako kreujÄ
cy faÅszywe poczucie bezpieczeÅstwa.
(…)
… jest definiowana w pliku źródłowym (lub prywatnym nagłówku niedostępnym dla użytkowników) zamiast w pliku nagłówkowym, a deklaracja
wyprzedzająca jest umieszczona w pliku nagłówkowym:
struct string;
struct string *create_string(const char *initial);
void free_string(struct string *s);
Zerowanie zwolnionych wskaźników
Jak powiedziano już wcześniej, po wywołaniu free() dla wskaźnika, staje się on “wiszącym…
… powinny zerować wskaźnik, który przekazuje
się do nich, więc argument musi być do nich przekazywany przez referencję. Na przykład, oto
zaktualizowany destruktor z sekcji Konstruktory i destruktory:
void free_string(struct
{
assert(s != NULL &&
FREE((*s)->data); /*
FREE(*s);
/*
}
string **s)
*s != NULL);
zwalniamy pamięć zajmowaną przez strukturę */
usuwamy strukturę */
Niestety, ten idiom…
... zobacz całą notatkę
Komentarze użytkowników (0)