2009-11-02 19 views
10

Mam wskaźnik znaku, który byłby używany do przechowywania ciąg znaków. Jest używany później w programie.Inicjalizacja znaków znakowych

mam zadeklarowane i zainicjowany tak:

char * p = NULL; 

jestem po prostu zastanawiasz się, czy jest to dobra praktyka. Używam gcc 4.3.3.

+3

Jakie jest twoje pytanie? –

+1

Chyba pyta, czy powinien zainicjować "ciąg" na NULL (lub 0), na "", czy nie zainicjować go w ogóle. tutaj –

+0

konieczna jest pewną ostrożnością, NULL jest _usually_ 0 ale według standardu może być różny: 'makro NULL, zdefiniowanego w którymkolwiek z , , , , , lub , jest zdefiniowane w implementacji Stała wskaźnika pustego C++ w niniejszym standardzie międzynarodowym. Na pewno bym się trzymał NULL, poza tym jest bardziej "zorientowany na typ" i bardziej opisowy w kodzie źródłowym niż 0. – RedGlyph

Odpowiedz

10

Tak, to jest dobry pomysł. Google Code Style poleca:

  1. Aby zainicjować wszystkie zmienne nawet jeśli nie trzeba ich teraz.
  2. Inicjalizacja wskaźników przez NULL, int przez 0 i float przez 0,0 - tylko dla lepszej czytelności.

    int i = 0; 
    double x = 0.0; 
    char* c = NULL; 
    
+1

Przy okazji, jest to przewodnik w stylu C++. Nie mówi nam, co (jeśli w ogóle) Google mówi o C. –

+0

@onebyone: Nawet nadal, jego dobrą praktyką jest inicjowanie wskaźników do wartości NULL, a następnie ustawienie ich na NULL po ich uwolnieniu. Ułatwia to przetestowanie ich przed dereferencją, szczególnie w skomplikowanym kodzie. Dotyczy to C lub C++. –

+0

@onebyone masz rację, jestem programistą C++ i widziałem składnię C++ w pytaniu =) Ale myślę, że zasada o wartości NULL jest również dobrą praktyką dla C. – f0b0s

4

Dobrą praktyką jest inicjowanie wszystkich zmiennych.

3

Nie można zapisać łańcucha w wskaźniku.

Twoja definicja mgt_dev_name jest dobra, ale musisz wskazać to gdzieś z miejscem na napis. Albo malloc() to miejsce lub użyj wcześniej zdefiniowanej tablicy znaków.

char *mgt_dev_name = NULL; 
char data[4200]; 

/* ... */ 

mgt_dev_name = data; /* use array */ 

/* ... */ 

mgt_dev_name = malloc(4200); 
if (mgt_dev_name != NULL) { 
    /* use malloc'd space */ 
    free(mgt_dev_name); 
} else { 
    /* error: not enough memory */ 
} 
+0

Nie wiem, czy o to pytał, ale chciałem to wiedzieć. W szczególności, jak zainicjować NULL, a następnie przypisać spację dla ciągu. –

3

Jeśli pytasz, czy to konieczne, czy jest to dobry pomysł, aby zainicjować zmienną NULL przed ustawieniem go na coś innego później: To nie jest konieczne, aby zainicjować go NULL, to won” • nie ma znaczenia dla funkcjonalności twojego programu.

Należy pamiętać, że w programowaniu ważne jest zrozumienie każdej linii kodu - dlaczego tak jest i co dokładnie robi. Nie rób rzeczy bez znajomości ich znaczenia lub bez zrozumienia, dlaczego je robisz.

+1

Właściwie powinien zainicjować go NULL. Technicznie, nawet wskazywanie na pamięć, która nie należy do twojego programu sprawia, że ​​jest źle sformułowana, więc ustawiając ją na NULL, unikasz tego. (I to jest dobra praktyka kodowania) – GManNickG

+1

Ale bardziej błędne jest nie zainicjowanie zmiennych, według mnie. –

+0

@GMan - Ale jeśli zainicjuje zmienną na inną wartość kilka linii później, bez robienia czegokolwiek ze zmienną pośrednią, to jest niepotrzebne. – Jesper

0

Istnieje kilka dobrych odpowiedzi na to pytanie, jedna z nich została przyjęta. I tak mam zamiar odpowiedzieć, aby rozszerzyć praktyczność.

Tak, dobrą praktyką jest inicjowanie wskaźników do wartości NULL, a także ustawianie wskaźników na wartość NULL po tym, jak nie są już potrzebne (tj. Zwolnione).

W obu przypadkach bardzo praktyczna jest możliwość przetestowania wskaźnika przed dereferencją. Powiedzmy, że masz strukturę, która wygląda tak:

struct foo { 
    int counter; 
    unsigned char ch; 
    char *context; 
}; 

następnie napisać aplikację, która spawns kilka wątków, z których wszystkie działają na jednej przydzielonej strukturze Foo (bezpiecznie) dzięki zastosowaniu wzajemnego wykluczania.

Wątek A otrzymuje blokadę foo, licznik inkrementów i sprawdza wartość w pkt.Nie znajduje takiego, więc nie przydziela (ani modyfikuje) kontekstu. Zamiast tego przechowuje wartość w ch, dzięki czemu wątek B może zamiast tego wykonać tę pracę.

Wątek B Widzi, że licznik został zwiększony, odnotowuje wartość w ale nie jest pewien, czy wątek A zrobił cokolwiek z kontekstem. Jeśli kontekst został zainicjowany jako NULL, wątek B nie musi już dbać o to, co robił wątek A, wie, że kontekst jest bezpieczny dla dereferencji (jeśli nie jest NULL) lub alokować (jeśli NULL) bez przecieków.

Wątek B wykonuje swoją pracę, wątek A odczytuje jego kontekst, uwalnia go, a następnie ponownie go inicjalizuje na wartość NULL.

To samo rozumowanie dotyczy zmiennych globalnych, bez użycia wątków. Dobrze jest móc przetestować je w różnych funkcjach przed ich dereferencją (lub próbując je przydzielić, powodując wyciek i niezdefiniowane zachowanie w programie).

Kiedy robi się głupio, oznacza to, że zasięg wskaźnika nie wykracza poza pojedynczą funkcję. Jeśli masz jedną funkcję i nie możesz śledzić wskaźników w niej zawartych, zwykle oznacza to, że funkcja powinna zostać ponownie przeanalizowana. Jednak nie ma nic złego w inicjowaniu wskaźnika w pojedynczej funkcji, choćby po to, aby zachować jednolite nawyki.

Jedyny czas, jaki kiedykolwiek widział „brzydki” przypadek opierając się na zainicjowany wskaźnik (przed i po użyciu) jest mniej więcej tak:

void my_free(void **p) 
{ 
    if (*p != NULL) { 
     free(*p); 
     *p = NULL; 
    } 
} 

Nie tylko dereferencji wskaźnika typu punned Zaskoczony na sztywnych platformach, powyższy kod czyni wolnym() jeszcze bardziej niebezpiecznym, ponieważ rozmówcy będą mieli pewne złudzenie bezpieczeństwa. Nie możesz polegać na praktyce "hurtowej", chyba że masz pewność, że każda operacja jest zgodna.

Prawdopodobnie o wiele więcej informacji, niż faktycznie chciałeś.

3

Inną opcją jest nie definiowanie zmiennej aż do miejsca w kodzie, w którym masz dostęp do jego początkowej wartości. Więc raczej wtedy robi:

char *name = NULL; 

... 

name = initial_value; 

chciałbym zmienić na:

... 

char *name = initial_value; 

Kompilator będzie następnie uniemożliwić odwołanie do zmiennej w tej części kodu, w którym ma ona żadnej wartości. W zależności od specyfiki twojego kodu nie zawsze może to być możliwe (na przykład wartość początkowa jest ustawiona w wewnętrznym zasięgu, ale zmienna ma inny czas życia), przesunięcie definicji tak późno, jak to możliwe w kodzie zapobiega błędom.

To powiedziawszy, jest to dozwolone tylko od standardu c99 (jest to również poprawne C++). Aby włączyć funkcje C99 w gcc, trzeba albo zrobić:

gcc -std=gnu99 

lub jeśli nie chcesz gcc rozszerzenia standardu:

gcc -std=c99 
3

Nie, to nie jest dobra praktyka , jeśli dobrze zrozumiałem twój kontekst.

Jeśli twój kod rzeczywiście zależy od mgt_dev_name o początkowej wartości wskaźnika zerowego, to oczywiście włączenie inicjatora do deklaracji jest bardzo dobrym pomysłem. To znaczy.jeśli trzeba by to zrobić w każdym razie

char *mgt_dev_name; 

/* ... and soon after */ 
mgt_dev_name = NULL; 

to jest zawsze lepszy pomysł do wykorzystania inicjalizacji zamiast przypisania

char *mgt_dev_name = NULL; 

Jednak inicjalizacji jest dobre tylko, kiedy można zainicjować obiektu z znacząceprzydatna wartość:. Wartość, której naprawdę potrzebujesz. Zwykle jest to możliwe tylko w językach, które pozwalają na deklaracje w dowolnym punkcie kodu, a C99 i C++ są dobrymi przykładami takich języków. Do czasu, gdy potrzebujesz swojego obiektu, zwykle znasz już odpowiedni inicjator dla tego obiektu, a zatem możesz łatwo wymyślić eleganckie oświadczenie z dobrym inicjatorem.

Z kolei w C89/90 deklaracje można umieszczać tylko na początku bloku. W tym momencie, w ogólnym przypadku, nie będziesz miał znaczących inicjalizatorów dla wszystkich twoich obiektów. Powinieneś zainicjować je czymś, cokolwiek (np. 0 lub NULL), aby je zainicjować ? Nie!!! Nigdy nie rób nic nie znaczącego w swoim kodzie. To niczego nie poprawi, niezależnie od tego, co powiedzą Ci różni "przewodnicy stylu". W rzeczywistości bezsensowna inicjalizacja może faktycznie obejmować błędy w kodzie, przez co trudniej je wykryć i naprawić.

Należy pamiętać, że nawet w C89/90 zawsze korzystne jest dążenie do lepszej lokalizacji deklaracji. To znaczy. dobrze znana wytyczna dobrych praktyk stwierdza: zawsze twórz zmienne tak lokalne, jak tylko mogą być. Nie układaj wszystkich deklaracji obiektów lokalnych na samym początku funkcji, lecz przenieś je na początek najmniejszego bloku, który obejmuje cały cykl życia obiektu tak mocno, jak to tylko możliwe. Czasami może być nawet dobrym pomysłem wprowadzenie fikcyjnego, w przeciwnym razie niepotrzebnego bloku, tylko po to, by poprawić lokalizację deklaracji. Postępując zgodnie z tą praktyką, w wielu (jeśli nie w większości) przypadkach pomożesz w zapewnieniu dobrych użytecznych inicjatorów dla twoich obiektów. Ale niektóre obiekty pozostaną niezainicjalizowane w C89/90 tylko dlatego, że nie będziesz mieć dobrego inicjatora dla nich w momencie deklaracji. Nie próbuj inicjować ich z "czymś" tylko ze względu na ich inicjalizację. To nie przyniesie absolutnie nic dobrego i może mieć negatywne konsekwencje.

Należy zauważyć, że niektóre nowoczesne narzędzia programistyczne (np. MS Visual Studio 2005) na przykład przechwytują dostęp w czasie rzeczywistym do niezainicjowanych zmiennych w wersji debugowania kodu. Oznacza to, że te narzędzia mogą pomóc wykryć sytuacje, gdy uzyskujesz dostęp do zmiennej, zanim uzyska ona szansę na uzyskanie znaczącej wartości, wskazując błąd w kodzie. Ale wykonując bezwarunkową przedwczesną inicjalizację twoich zmiennych, zasadniczo zabijasz tę zdolność narzędzia i usuwasz te błędy pod dywan.

0

Preferowane style:

w C: char * c = NULL;

w C++: char * c = 0;

0

Moja Uzasadnieniem jest to, że jeśli nie zainicjować z NULL, a następnie zapomnieć zainicjować zupełnie The rodzaje błędów, które dostaniesz w swoim kodzie, gdy dialogi są trudniejsze do wyśledzenia ze względu na potencjalne śmieci przechowywane w pamięci w tym momencie. Z drugiej strony, jeśli zainicjujesz na NULL, przez większość czasu otrzymasz tylko segmentation fault, co jest lepsze, biorąc pod uwagę alternatywę.

0

Inicjowanie zmiennych nawet wtedy, gdy nie jest potrzebne ich natychmiastowe zainicjowanie, jest dobrą praktyką.Zwykle inicjalizujemy wskaźniki na wartość NULL, int na 0 i na konwencję przechodzimy do 0.0.

int* ptr = NULL; 
int i = 0; 
float r = 0.0;