Historia pytania:
Globalne i lokalne zmienne pojawiły się w C w celu zarządzania pamięcią i zakresem widoczności kodu. Globalne zmienne były wcześniej głównym sposobem wymiany danych między funkcjami jeszcze przed pojawieniem się programowania modularnego, zaś lokalne umożliwiły zmniejszenie interakcji i zwiększenie izolacji kodu.
Problem:
Często występuje niedopowiedzenie co do różnicy między globalnymi a lokalnymi zmiennymi: czas ich życia, zakres widoczności, zasady i czas inicjalizacji. Globalne zmienne prowadzą do problemów z synchronizacją i czytelnością, lokalne – do niedostępności potrzebnych danych. Błędy w zrozumieniu tych różnic prowadzą do błędów i utrudniają skalowanie kodu.
Rozwiązanie:
Globalne zmienne są deklarowane poza wszystkimi funkcjami i są dostępne we wszystkich plikach przy użyciu extern. Ich czas życia to cały program, a inicjalizacja odbywa się albo niejawnie zerami (dla zmiennych statycznych), albo jawnie wartością użytkownika. Lokalne zmienne są deklarowane wewnątrz funkcji, ich czas życia jest ograniczony do wywołania funkcji i ich zawartość nie jest automatycznie inicjalizowana.
Przykład kodu:
int g_var = 42; // Zmienna globalna void foo() { int l_var = 5; // Zmienna lokalna }
Kluczowe cechy:
Czy lokalna zmienna jest automatycznie inicjowana zerem, jeśli nie ma podanej wartości początkowej?
Nie. Tylko zmienne globalne i statyczne są inicjowane zerami domyślnie. Zmienne lokalne zawierają „śmieci” (nieokreśloną wartość), jeśli nie zostanie jawnie podana wartość początkowa.
Przykład:
void test() { int a; printf("%d ", a); // Nieokreślone zachowanie }
Czy zawsze można uzyskać dostęp do zmiennej globalnej z różnych plików?
Nie. Jeśli zmienna została zadeklarowana jako static poza funkcją, jest widoczna tylko w tym pliku źródłowym. Jeśli potrzebna jest globalna widoczność, użyj extern.
Czy można zadeklarować zmienną globalną wewnątrz funkcji?
Nie. Wewnątrz funkcji wszystkie deklaracje są lokalne. Tylko poza funkcjami można tworzyć zmienne globalne.
Zmienna globalna jest używana do wymiany danych między funkcjami:
int error_code; void f1() { error_code = 1; } void f2() { if (error_code) ... }
Plusy:
Minusy:
Wszystkie zmienne są lokalne, dane są przekazywane przez parametry funkcji:
void f1(int *err) { *err = 1; } void f2(int err) { if (err) ... }
Plusy:
Minusy: