C/Powszechne praktyki: Różnice pomiędzy wersjami
Usunięta treść Dodana treść
mNie podano opisu zmian |
mNie podano opisu zmian |
||
Linia 8:
Ponieważ C nie jest językiem obiektowym, nie ma wbudowanego wsparcia dla konstruktorów i destruktorów. Często programiści bezpośrednio modyfikują tworzone obiekty i struktury. Jednakże prowadzi to do potencjalnych błędów, ponieważ operacje na obiekcie mogą się nie powieść lub zachować się nieprzewidywalnie, jeśli obiekt nie został prawidłowo zainicjalizowany. Lepszym podejściem jest stworzenie funkcji, która tworzy instancję obiektu, ewentualnie przyjmując pewne parametry:
};
struct string *create_string(const char *initial) {
}
}
Podobnie, bezpośrednie usuwanie obiektów może nie do końca się udać, prowadząc do wycieku zasobów. Lepiej jest użyć destruktora:
{
▲void free_string(struct string *s) {
}
Często łączy się destruktory z [[#Zerowanie zwolnionych wskaźników|zerowaniem zwolnionych wskaźników]].
Linia 37 ⟶ 36:
Czasami dobrze jest ukryć definicję obiektu, żeby mieć pewność, że użytkownicy nie utworzą go ręcznie. Aby to zapewnić struktura 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 *create_string(const char *initial);
void free_string(struct string *s);
== Zerowanie zwolnionych wskaźników ==
Linia 46 ⟶ 45:
Jednym z prostych rozwiązań tego problemu jest zapewnienie, że każdy wskaźnik jest zerowany natychmiast po zwolnieniu:
p = NULL;
Inaczej niż w przypadku "wiszących wskaźników", na wielu nowoczesnych architekturach 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:
<small>(aby zobaczyć dlaczego makro jest napisane w ten sposób, zobacz [[#Konwencje pisania makr]])</small>
Linia 57 ⟶ 56:
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]]:
{
assert(s != NULL && *s != NULL);
FREE((*s)->data); /* zwalniamy pamiec zajmowana przez strukture */ }
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.
Linia 90:
Oprócz tego są także przesunięcia ('''<nowiki><<</nowiki>''' oraz '''<nowiki>>></nowiki>'''). Zastanówmy się teraz, jak je wykorzystać w praktyce. Załóżmy, że zajmujemy się jednobajtową zmienną.
Z matematyki wiemy, że zapis binarny tej liczby wygląda tak (w ośmiobitowej zmiennej):
00000010. Jeśli teraz np. chcielibyśmy "zapalić" drugi bit od lewej (tj. bit, którego zapalenie niejako "doda" do liczby wartość 2<sup>6</sup>) powinniśmy użyć logicznego lub:
Gdzie 64=2<sup>6</sup>. Odczytywanie wykonuje się za pomocą tzw. maski bitowej. Polega to na:
Linia 103:
Do "wyłuskania" odpowiedniego bitu możemy posłużyć się operacją "i" - czyli operatorem '''&'''. Wygląda to analogicznie do posługiwania się operatorem "lub":
Jeśli nie władasz biegle kodem binarnym, tworzenie masek bitowych ułatwią ci przesunięcia bitowe. Aby uzyskać liczbę która ma zapalony bit o numerze <code>n</code> (bity są liczone od zera), przesuwamy bitowo w lewo jedynkę o <code>n</code> pozycji:
Jeśli chcemy uzyskać liczbę, w której zapalone są bity na pozycjach <code>l, m, n</code> - używamy sumy logicznej ("lub"):
Jeśli z kolei chcemy uzyskać liczbę gdzie zapalone są wszystkie bity poza <code>n</code>, odwracamy ją za pomocą operatora logicznej negacji <code>~</code>
Warto władać biegle operacjami na bitach, ale początkujący mogą (po uprzednim przeanalizowaniu) zdefiniować następujące makra i ich używać:
|