C/Powszechne praktyki: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
Nie podano opisu zmian
Nie podano opisu zmian
Linia 6:
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:
 
<sourcesyntaxhighlight lang="c">
struct string {
size_t size;
Linia 21:
return new_string;
}
</syntaxhighlight>
</source>
 
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:
 
<sourcesyntaxhighlight lang="c">
void free_string(struct string *s)
{
Linia 32:
free(s); /* usuwamy samą strukturę */
}
</syntaxhighlight>
</source>
 
Często łączy się destruktory z [[#Zerowanie zwolnionych wskaźników|zerowaniem zwolnionych wskaźników]].
Linia 38:
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:
 
<sourcesyntaxhighlight lang="c">
struct string;
struct string *create_string(const char *initial);
void free_string(struct string *s);
</syntaxhighlight>
</source>
{{Porada|Jeśli znasz język [[C++]], mógłbyś użyć klas, a dzięki nim konstruktorów i destruktorów.}}
 
Linia 50:
Jednym z prostych rozwiązań tego problemu jest zapewnienie, że każdy wskaźnik jest zerowany natychmiast po zwolnieniu:
 
<sourcesyntaxhighlight lang="c">
free(p);
p = NULL;
</syntaxhighlight>
</source>
 
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:
 
<sourcesyntaxhighlight lang="c">
#define FREE(p) do { free(p); (p) = NULL; } while(0)
</syntaxhighlight>
</source>
 
<small>(aby zobaczyć dlaczego makro jest napisane w ten sposób, zobacz [[#Konwencje pisania makr]])</small>
Linia 65:
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|Konstruktory i destruktory]]:
 
<sourcesyntaxhighlight lang="c">
void free_string(struct string **s)
{
Linia 72:
FREE(*s); /* usuwamy strukturę */
}
</syntaxhighlight>
</source>
 
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 101:
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ą.
 
<sourcesyntaxhighlight lang="c">
unsigned char i = 2;
</syntaxhighlight>
</source>
 
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:
 
<sourcesyntaxhighlight lang="c">
unsigned char i = 2;
i |= 64;
</syntaxhighlight>
</source>
 
Gdzie 64=2<sup>6</sup>. Odczytywanie wykonuje się za pomocą tzw. maski bitowej. Polega to na:
Linia 118:
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":
 
<sourcesyntaxhighlight lang="c">
unsigned char i = 3; /* bitowo: ''00000011'' */
unsigned char temp = 0;
Linia 128:
printf ("bit zgaszony");
}
</syntaxhighlight>
</source>
 
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:
 
<sourcesyntaxhighlight lang="c">
1 << n
</syntaxhighlight>
</source>
 
Jeśli chcemy uzyskać liczbę, w której zapalone są bity na pozycjach <code>l, m, n</code> - używamy sumy logicznej ("lub"):
 
<sourcesyntaxhighlight lang="c">
(1 << l) | (1 << m) | (1 << n)
</syntaxhighlight>
</source>
 
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>
 
<sourcesyntaxhighlight lang="c">
~(1 << n)
</syntaxhighlight>
</source>
 
Warto władać biegle operacjami na bitach, ale początkujący mogą (po uprzednim przeanalizowaniu) zdefiniować następujące makra i ich używać:
 
<sourcesyntaxhighlight lang="c">
/* Sprawdzenie czy w liczbie k jest zapalony bit n */
#define IS_BIT_SET(k, n) ((k) & (1 << (n)))
Linia 159:
/* Zgaszenie bitu n w zmiennej k */
#define RESET_BIT(k, n) (k &= ~(1 << (n)))
</syntaxhighlight>
</source>
 
== Skróty notacji ==
Istnieją pewne sposoby ograniczenia ilości niepotrzebnego kodu. Przykładem może być wykonywanie jednej operacji w razie wystąpienia jakiegoś warunku, np. zamiast pisać:
 
<sourcesyntaxhighlight lang="c">
if (warunek) {
printf ("Warunek prawdziwy\n");
}
</syntaxhighlight>
</source>
 
możesz skrócić notację do:
 
<sourcesyntaxhighlight lang="c">
if (warunek)
printf ("Warunek prawdziwy\n");
</syntaxhighlight>
</source>
 
Podobnie jest w przypadku pętli for:
 
<sourcesyntaxhighlight lang="c">
for (;warunek;)
printf ("Wyświetlam się w pętli!\n");
</syntaxhighlight>
</source>
 
Niestety ograniczeniem w tym wypadku jest to, że można w ten sposób zapisać tylko jedną instrukcję.