We wczesnych dniach C ++, kiedy był przykręcony do C, nie można było używać NULL, tak jak zostało zdefiniowane jako (void*)0
. Nie można przypisać wartości NULL do żadnego wskaźnika innego niż void*
, co czyni go w pewnym sensie bezużytecznym. W tamtych czasach przyjęto, że użyłeś 0
(zero) wskaźników zerowych.
Do dzisiaj nadal używam zera jako wskaźnika zerowego, ale osoby wokół mnie nalegają na użycie NULL
. Osobiście nie widzę żadnej korzyści z nadania nazwy ( NULL
) istniejącej wartości - a ponieważ lubię także testować wskaźniki jako wartości prawdy:
if (p && !q)
do_something();
wtedy użycie zera ma większy sens (tak jakbyś używał NULL
, nie możesz logicznie używać p && !q
- musisz wyraźnie porównać NULL
, chyba że przyjmujesz, że NULL
zero, w takim przypadku dlaczego warto użyć NULL
).
Czy istnieje jakikolwiek obiektywny powód, aby preferować zero niż NULL (lub odwrotnie), czy też wszystkie są tylko osobistymi preferencjami?
Edycja: Powinienem dodać (i pierwotnie powiedzieć), że z RAII i wyjątkami rzadko używam wskaźników zero / NULL, ale czasami nadal ich potrzebujesz.
Odpowiedzi:
Oto podejście Stroustrupa do tego: C ++ FAQ i styl i technika
To powiedziawszy, nie przejmuj się małymi rzeczami.
źródło
Istnieje kilka argumentów (z których jeden jest stosunkowo nowy), które moim zdaniem są sprzeczne ze stanowiskiem Bjarne'a w tej sprawie.
Użycie
NULL
pozwala na wyszukiwanie jego użycia, a także podkreśla, że deweloper chciał użyćNULL
wskaźnika, niezależnie od tego, czy jest on interpretowany przez kompilator,NULL
czy nie.Przykład, który wszyscy cytują to:
Jednak przynajmniej moim zdaniem problem z powyższym nie polega na tym, że używamy NULL dla stałej wskaźnika zerowego, ale na tym, że mamy przeciążenia „foo”, które przyjmują bardzo różne rodzaje argumentów. Parametr musi być
int
również, ponieważ jakikolwiek inny typ spowoduje niejednoznaczne wywołanie i wygeneruje pomocne ostrzeżenie kompilatora.Nawet przy braku C ++ 0x dostępne są dziś narzędzia, które sprawdzają, czy
NULL
są używane dla wskaźników i czy0
są używane dla typów integralnych.std::nullptr_t
typ.To najnowszy argument w tabeli. Problem
0
iNULL
jest aktywnie rozwiązywany dla C ++ 0x, i możesz zagwarantować, że dla każdej implementacji, która zapewniaNULL
, pierwszą rzeczą, którą zrobią, jest:Dla tych, którzy używają
NULL
zamiast0
zmiana będzie poprawa bezpieczeństwa typu z małym lub bez wysiłku - jeśli coś może również złapać kilka błędów gdzie oni wykorzystywaneNULL
do0
. Dla każdego, kto używa0
dzisiaj ... eee ... mam nadzieję, że mają dobrą znajomość wyrażeń regularnych ...źródło
#include
i zachowaj bezpieczeństwo przez całą drogę.#define NULL nullptr
wydaje się niebezpieczne. Na lepsze lub gorsze, wiele starszych kodów używa wartości NULL dla rzeczy innych niż 0. Na przykład uchwyty są często implementowane jako integralny typ, a ustawienie ich naNULL
nie jest rzadkie. Widziałem nawet nadużycia, takie jakNULL
ustawianiechar
na zero-terminator.NULL
są w rzeczywistości nieprawidłowe. Wiele interfejsów API od dawna jest używanychNULL
z uchwytami, a tak naprawdę jest to udokumentowane użycie wielu z nich. Nagłe ich złamanie i stwierdzenie, że robią to źle, nie jest pragmatyczne.Użyj NULL. NULL pokazuje twoje zamiary. To, że wynosi 0, jest szczegółem implementacji, który nie powinien mieć znaczenia.
źródło
Zawsze używam:
NULL
dla wskaźników'\0'
dla znaków0.0
dla pływaków i debligdzie 0 byłoby dobrze. Jest to kwestia zamiaru sygnalizowania. To powiedziawszy, nie jestem analny na ten temat.
źródło
Już dawno przestałem używać NULL na korzyść 0 (jak również większości innych makr). Zrobiłem to nie tylko dlatego, że chciałem jak najbardziej unikać makr, ale także dlatego, że NULL wydaje się być nadmiernie używany w kodzie C i C ++. Wydaje się, że jest używany, gdy potrzebna jest wartość 0, nie tylko dla wskaźników.
W nowych projektach umieszczam to w nagłówku projektu:
Teraz, gdy pojawią się kompilatory zgodne z C ++ 0x, wszystko, co muszę zrobić, to usunąć tę linię. Zaletą tego jest to, że Visual Studio rozpoznaje nullptr jako słowo kluczowe i odpowiednio je wyróżnia.
źródło
Morał tej historii. Powinieneś używać NULL, kiedy masz do czynienia ze wskaźnikami.
1) Deklaruje twój zamiar (nie zmuszaj mnie do przeszukiwania całego twojego kodu, próbując dowiedzieć się, czy zmienna jest wskaźnikiem, czy jakimś typem liczbowym).
2) W niektórych wywołaniach API, które oczekują zmiennych argumentów, użyją wskaźnika NULL do wskazania końca listy argumentów. W takim przypadku użycie „0” zamiast NULL może powodować problemy. Na platformie 64-bitowej wywołanie va_arg potrzebuje 64-bitowego wskaźnika, a jednak przekażesz tylko 32-bitową liczbę całkowitą. Wydaje mi się, że polegasz na pozostałych 32-bitach, które zostaną dla ciebie wyzerowane? Widziałem niektóre kompilatory (np. Icpc Intela), które nie są tak łaskawe - co spowodowało błędy w czasie wykonywania.
źródło
NULL
być może nie jest przenośny i nie jest bezpieczny. Mogą istnieć platformy, które nadal#define NULL 0
(zgodnie z często zadawanymi pytaniami Stroustrup: Czy powinienem używać NULL lub 0? Cytowanych w głównym pytaniu i jest to jeden z pierwszych wyników wyszukiwania). Przynajmniej w starszym C ++0
ma specjalne znaczenie pojęciowe w kontekście wskaźnika. Nie powinieneś konkretnie myśleć o bitach. Należy również pamiętać, że w różnych kontekstach Integer (short
,int
,long long
) „sizeof(0)
” będzie inna. Myślę, że ta odpowiedź jest nieco myląca.NULL
zamiast(char *)0
,(const char *)0
albo(struct Boo *)0
albo(void *)0
albo cokolwiek się wyraża zamiar jaśniej. - bez (moim zdaniem) zbyt uciążliwe)Jeśli dobrze pamiętam, NULL jest inaczej zdefiniowany w nagłówkach, których użyłem. Dla C jest zdefiniowany jako (void *) 0, a dla C ++ jest zdefiniowany jako tylko 0. Kod wyglądał mniej więcej tak:
Osobiście nadal używam wartości NULL do reprezentowania zerowych wskaźników, co wyraźnie wskazuje, że używasz wskaźnika zamiast jakiegoś integralnego typu. Tak wewnętrznie wartość NULL wynosi wciąż 0, ale nie jest reprezentowana jako taka.
Ponadto nie polegam na automatycznej konwersji liczb całkowitych na wartości logiczne, ale wyraźnie je porównuję.
Na przykład wolisz używać:
zamiast:
Wystarczy powiedzieć, że to wszystko naprawiono w C ++ 11, w którym można po prostu użyć
nullptr
zamiastNULL
, a takżenullptr_t
taki jest typnullptr
.źródło
Powiedziałbym, że historia przemówiła, a ci, którzy opowiadali się za użyciem 0 (zero), byli w błędzie (w tym Bjarne Stroustrup). Argumenty na korzyść 0 to głównie estetyka i „osobiste preferencje”.
Po utworzeniu C ++ 11, z nowym typem nullptr, niektóre kompilatory zaczęły narzekać (z parametrami domyślnymi) na przekazywanie 0 do funkcji z argumentami wskaźnika, ponieważ 0 nie jest wskaźnikiem.
Jeśli kod został napisany przy użyciu wartości NULL, można było przeprowadzić proste wyszukiwanie i zamianę przez bazę kodu, aby zamiast tego był nullptr. Jeśli utkniesz w kodzie napisanym przy użyciu 0 jako wskaźnika, jego aktualizacja jest znacznie bardziej nużąca.
A jeśli musisz teraz napisać nowy kod do standardu C ++ 03 (i nie możesz używać nullptr), naprawdę powinieneś po prostu użyć NULL. Ułatwi ci to aktualizację w przyszłości.
źródło
Zwykle używam 0. Nie lubię makr i nie ma gwarancji, że nagłówek innej firmy, którego używasz, nie redefiniuje wartości NULL jako czegoś dziwnego.
Możesz użyć obiektu nullptr zaproponowanego przez Scotta Meyersa i innych, dopóki C ++ nie otrzyma słowa kluczowego nullptr:
Google „nullptr”, aby uzyskać więcej informacji.
źródło
(void*)0
jeśli jest kompilowana jako kod C), po prostu prosi o problemy i nie powinna być używana.NULL
ponad(type *)0
jest możliwość wyszukiwania. W przeciwnym razie wydaje się niepotrzebne zaciemnianie, jeśli nie jest to idiom C. Osobiście uważam, że idiom rozprzestrzeniania sięNULL
po całym miejscu zasługuje na śmierć.NULL
jest moim zdaniem bezużytecznym makrem. Brzytwa Ockhama ma tu trochę roboty ...Kiedyś pracowałem na maszynie, w której 0 był prawidłowym adresem, a NULL został zdefiniowany jako specjalna wartość ósemkowa. Na tym komputerze (0! = NULL), więc kod taki jak
nie działałby zgodnie z oczekiwaniami. MUSISZ napisać
Chociaż uważam, że większość kompilatorów obecnie definiuje NULL jako 0, wciąż pamiętam lekcję sprzed lat: NULL niekoniecznie jest 0.
źródło
Myślę, że standard gwarantuje, że NULL == 0, więc możesz to zrobić. Wolę NULL, ponieważ dokumentuje twoją intencję.
źródło
foo.bar_ptr = (Bar *) 0
wyraża intencję o wiele jaśniej niżfoo.bar_ptr = NULL
. Ten nawyk umożliwia także kompilatorowi wykrywanie błędów błędnych przekonań. Dla mniefoo.bar_ptr = 0
wyraża intencję, a także użycie,NULL
jeśli wiem, żefoo.bar_ptr
to wskaźnik.Użycie 0 lub NULL będzie miało taki sam efekt.
Nie oznacza to jednak, że oba są dobrymi praktykami programowania. Biorąc pod uwagę, że nie ma różnicy w wydajności, wybranie opcji niskiego poziomu świadomości zamiast agnostycznej / abstrakcyjnej alternatywy jest złą praktyką programistyczną. Pomóż czytelnikom Twojego kodu zrozumieć proces myślowy .
NULL, 0, 0,0, „\ 0”, 0x00 i whatelse tłumaczą to samo, ale są różnymi logicznymi jednostkami w twoim programie. Powinny być używane jako takie. NULL to wskaźnik, 0 to ilość, 0x0 to wartość, której bity są interesujące itp. Nie przypisalibyśmy wskaźnika „\ 0” bez względu na to, czy się kompiluje, czy nie.
Wiem, że niektóre społeczności zachęcają do wykazania się dogłębną wiedzą na temat środowiska poprzez łamanie jego umów. Odpowiedzialni programiści tworzą jednak kod, który można utrzymać i nie dopuszczają do tego.
źródło
Dziwne, nikt o tym nie wspominał, w tym Stroustroup. Choć dużo mówić o standardach i estetyka nikt nie zauważył, że jest to niebezpieczne w użyciu
0
wNULL
„s zamiast, na przykład, w liście zmiennych argumentów na architekturze gdziesizeof(int) != sizeof(void*)
. Podobnie jak Stroustroup, wolę0
ze względów estetycznych, ale trzeba uważać, aby nie używać go tam, gdzie jego typ może być niejednoznaczny.źródło
0
pod warunkiem, że określenie, które0
masz na myśli - na przykład(int *)0
,(char *)0
,(const char *)0
lub(void *)0
lub(unsigned long long) 0
lub cokolwiek. To moim zdaniem wyraża intencję o wiele jaśniej niżNULL
.NULL
oznacza.(void *)
kiedy mogę użyć dokładnego typu. Celowo podałem przykład (zwykle) 64-bitowej liczby całkowitej na liście, ponieważ jest ona analogiczna do wielkości wskaźnika. Ponadto, jeśli moje wspomnienie, że starszy C ++ zdefiniowanyNULL
jako0
dokładny (minęły lata, odkąd programowałem w C ++), to nie widzimy żadnej poprawy poprawności programu. Nowszy standard C ++ na szczęście zapewnianullptr
słowo kluczowe, dzięki czemu możemy pozbyć się tejNULL
brzydoty i całego kontrowersji pisząc nowsze C ++.(void*)
abstrakcji zostało abstrakcyjneNULL
. INULL
faktycznie nie wyrazić intencją wyraźnie większość czasu. I myślę, że twoje wspomnienia są błędne. Nie jestem pewien co do standardów, ale w praktyce uważam, że tak było(void*)0
. I tak,nullptr
to niezły prettifier, choć sprowadza się do tego samegoNULL
- określania wskaźnika zerowego bez określania typu.nullptr
ma ten sam przekazNULL
, co dotyczyło tylko wyrażenia zamiaru, o którym wspomniałeś na samym początku. (Wstępne przetwarzanieNULL
nowoczesnychgcc
zbiorów__null
, cokolwiek to jest).Staram się unikać całego pytania, w miarę możliwości korzystając z referencji C ++. Zamiast
często możesz pisać
Oczywiście nie zawsze to działa; ale wskaźniki zerowe mogą być nadużywane.
źródło
Jestem z Stroustrup na tym :-) Ponieważ NULL nie jest częścią języka, wolę używać 0.
źródło
Przede wszystkim preferencje osobiste, choć można by argumentować, że NULL czyni dość oczywistym, że obiekt jest wskaźnikiem, który obecnie nie wskazuje na nic, np.
IIRC, standard nie wymaga wartości NULL równej 0, więc użycie tego, co zdefiniowano w <stddef.h>, jest prawdopodobnie najlepsze dla twojego kompilatora.
Innym aspektem tego argumentu jest to, czy należy użyć porównań logicznych (niejawne rzutowanie na bool), czy sprawdzenia jawności względem wartości NULL, ale sprowadza się to również do czytelności.
źródło
Wolę używać NULL, ponieważ wyjaśnia, że twoją intencją jest wartość reprezentująca wskaźnik, a nie wartość arytmetyczna. Fakt, że jest to makro, jest niefortunny, ale ponieważ jest tak głęboko zakorzeniony, nie ma niebezpieczeństwa (chyba że ktoś zrobi coś naprawdę bezczelnego). Chciałbym, żeby to było słowo kluczowe od samego początku, ale co możesz zrobić?
To powiedziawszy, nie mam problemu z używaniem wskaźników jako wartości prawdy samych w sobie. Podobnie jak w przypadku NULL, jest to zakorzeniony idiom.
C ++ 09 doda konstrukcję nullptr, która moim zdaniem jest już dawno spóźniona.
źródło
Zawsze używam 0. Nie z żadnego naprawdę przemyślanego powodu, tylko dlatego, że kiedy uczyłem się C ++, przeczytałem coś, co zalecało użycie 0 i po prostu zawsze tak robiłem. Teoretycznie może wystąpić problem z czytelnością, ale w praktyce nigdy nie spotkałem się z takim problemem w tysiącach roboczogodzin i milionach wierszy kodu. Jak mówi Stroustrup, tak naprawdę jest to tylko osobisty problem estetyczny, dopóki standard nie stanie się zerowy.
źródło
Ktoś mi kiedyś powiedział ... Przedefiniuję NULL do 69. Od tego czasu nie używam: P
To sprawia, że twój kod jest dość podatny na ataki.
Edytować:
Nie wszystko w standardzie jest idealne. Makro NULL jest zdefiniowaną implementacją stałą zerowego wskaźnika C ++, który nie jest w pełni kompatybilny z makrem C NULL, co oprócz ukrywania typu przekształca go w bezużyteczne i podatne na błędy narzędzie.
NULL nie zachowuje się jak wskaźnik zerowy, ale jako literał O / OL.
Powiedz, że następny przykład nie jest mylący:
Z tego powodu w nowym standardzie pojawia się std :: nullptr_t
Jeśli nie chcesz czekać na nowy standard i chcesz użyć nullptr, użyj co najmniej przyzwoitego, takiego jak zaproponowany przez Meyersa (patrz komentarz jon.h).
źródło
NULL
jest dobrze zdefiniowaną częścią standardu C ++. Umożliwianie osobom, które lubią redefiniować standardowe makra edycji kodu w twoim projekcie, sprawia, że Twój kod jest podatny na ataki; używanieNULL
nie.Cóż, argumentuję za tym, aby w ogóle nie używać wskaźników 0 lub NULL.
Korzystanie z nich prędzej czy później doprowadzi do błędów segmentacji w kodzie. Z mojego doświadczenia wynika, że wskaźniki w gereral są jednym z największych źródeł błędów w C ++
prowadzi również do instrukcji „jeśli-nie-zerowe” w całym kodzie. Znacznie ładniej, jeśli możesz polegać na zawsze poprawnym stanie.
Prawie zawsze istnieje lepsza alternatywa.
źródło
0
) jest przydatna do debugowania. O wiele lepsze niż dereferencje losowych śmieci i zdobywanie informacji, kto wie, jaki wynik.Ustawienie wskaźnika na 0 nie jest po prostu takie jasne. Zwłaszcza jeśli korzystasz z języka innego niż C ++. Dotyczy to zarówno C, jak i Javascript.
Niedawno dodałem trochę kodu:
virtual void DrawTo(BITMAP *buffer) =0;
dla czystej funkcji wirtualnej po raz pierwszy. Przez tydzień myślałem, że to jakiś magiczny jiberjash. Kiedy zdałem sobie sprawę, że po prostu ustawiałem wskaźnik funkcji na
null
(ponieważ funkcje wirtualne są w większości przypadków wskaźnikami funkcji dla C ++), kopnąłem się.virtual void DrawTo(BITMAP *buffer) =null;
byłby mniej mylący niż to basterdation bez odpowiedniego odstępu od moich nowych oczu. Właściwie zastanawiam się, dlaczego C ++ nie używa małych liter,
null
tak jak teraz używa małych i fałszywych i prawdziwych.źródło