Czy mogę używać NULL
wskaźnika jako zamiennika wartości 0
?
Czy jest w tym coś złego?
Jak na przykład:
int i = NULL;
jako zamiennik dla:
int i = 0;
W ramach eksperymentu skompilowałem następujący kod:
#include <stdio.h>
int main(void)
{
int i = NULL;
printf("%d",i);
return 0;
}
Wynik:
0
Rzeczywiście daje mi to ostrzeżenie, które samo w sobie jest całkowicie poprawne:
warning: initialization makes integer from pointer without a cast [-Wint-conversion]
ale wynik jest nadal równoważny.
- Czy przechodzę przez to do „Nieokreślonego zachowania”?
- Czy można
NULL
w ten sposób wykorzystywać ? - Czy jest coś złego w używaniu
NULL
jako wartości liczbowej w wyrażeniach arytmetycznych? - A jaki jest wynik i zachowanie w C ++ w tym przypadku?
Przeczytałem odpowiedzi na pytanie Jaka jest różnica między NULL, „\ 0” i 0 na temat różnicy między NULL
, \0
i 0
jest, ale nie otrzymałem stamtąd zwięzłych informacji, jeśli jest to całkiem dopuszczalne, a także prawo do używania NULL
jako wartość do obsługi w zadaniach i innych operacjach arytmetycznych.
Odpowiedzi:
Nie , nie jest to bezpieczne.
NULL
jest stałą wskaźnika zerowego, która może mieć typint
, ale bardziej typowo ma typvoid *
(w C), lub w inny sposób nie może być bezpośrednio przypisana doint
(w C ++> = 11). Oba języki pozwalają na konwersję wskaźników na liczby całkowite, ale nie przewidują, że takie konwersje mogą być wykonywane w sposób dorozumiany (chociaż niektóre kompilatory zapewniają to jako rozszerzenie). Co więcej, chociaż konwersja wskaźnika zerowego na liczbę całkowitą w celu uzyskania wartości 0 jest powszechna, standard nie gwarantuje tego. Jeśli chcesz stałą z typemint
i wartością 0, to przeliteruj ją0
.Tak, w każdej implementacji, w której
NULL
rozwija się do wartości typuvoid *
lub innej, której nie można bezpośrednio przypisaćint
. Standard nie definiuje zachowania twojego zadania na takiej implementacji, ergo jego zachowanie jest niezdefiniowane.Jest to kiepski styl, aw niektórych przypadkach może się zepsuć. O ile wydaje się, że używasz GCC, włamałoby się to na twój własny przykład, gdybyś skompilował z
-Werror
opcją.Tak. Nie ma gwarancji, że w ogóle ma wartość liczbową. Jeśli masz na myśli 0, to napisz 0, które jest nie tylko dobrze zdefiniowane, ale także krótsze i wyraźniejsze.
Język C ++ jest bardziej restrykcyjny w stosunku do konwersji niż C i ma inne reguły
NULL
, ale tam również implementacje mogą zapewniać rozszerzenia. Znów, jeśli masz na myśli 0, to właśnie powinieneś napisać.źródło
void *
” jest prawdziwe tylko dla C.void *
nie jest legalnym typem dla C ++ (ponieważ nie można przypisaćvoid*
do żadnego innego typu wskaźnika). W C ++ 89 i C ++ 03 tak naprawdęNULL
musi być typuint
, ale w późniejszych wersjach może być (i zwykle jest)nullptr_t
.void*
naint
zachowanie jest niezdefiniowana. Nie jest; jest to zachowanie określone w implementacji.NULL
to jakaś stała wskaźnika zerowego. W C może to być ciągłe wyrażenie całkowite o wartości0
lub takie wyrażenie rzucone navoid*
, przy czym to drugie jest bardziej prawdopodobne. Co oznacza, że nie możesz zakładać używaniaNULL
zamiennie z zerem. Na przykład w tym kodzie przykładowymNie można zagwarantować, że zastąpienie
0
goNULL
jest poprawnym programem typu C, ponieważ dodawanie między dwoma wskaźnikami (nie mówiąc już o różnych typach wskaźników) nie jest zdefiniowane. Spowoduje to wydanie diagnostyki z powodu naruszenia ograniczenia. Argumenty dodawania nie będą ważne .Jeśli chodzi o C ++, rzeczy są nieco inne. Brak niejawnej konwersji
void*
na inne typy obiektów oznaczał, żeNULL
historycznie był zdefiniowany jak0
w kodzie C ++. W C ++ 03 możesz prawdopodobnie uciec. Ale od C ++ 11 można go legalnie zdefiniować jakonullptr
słowo kluczowe . Teraz znowu pojawia się błąd, ponieważstd::nullptr_t
nie można go dodać do typów wskaźników.Jeśli
NULL
zostanie zdefiniowane jako,nullptr
to nawet eksperyment stanie się nieważny. Nie ma konwersji zstd::nullptr_t
na liczbę całkowitą. Dlatego jest uważany za bezpieczniejszą stałą zerową wskaźnika.źródło
NULL
w obu językach.0
stałego wskaźnika zerowego: „najwyraźniej jako poprawka do całego istniejącego źle napisanego kodu C, który przyjmował błędne założenia”Zasady różnią się w zależności od języka i jego wersji. W niektórych przypadkach CAN oraz w innych, nie można. Niezależnie od tego nie powinieneś . Jeśli masz szczęście, kompilator ostrzeże, gdy spróbujesz, a nawet lepiej, kompilacja się nie powiedzie.
W C ++ przed C ++ 11 (cytat z C ++ 03):
Nie ma sensu używać stałej zerowej wskaźnika jako liczby całkowitej. Jednak...
Tak, technicznie by to działało, nawet jeśli byłoby bezsensowne. Z powodu tej techniki możesz napotkać źle napisane programy, które nadużywają
NULL
.Od wersji C ++ 11 (cytat z najnowszej wersji roboczej):
A
std::nullptr_t
nie jest konwertowalne na liczbę całkowitą, więc użycieNULL
jako liczby całkowitej działałoby tylko warunkowo, w zależności od wyborów dokonanych przez implementację języka.PS
nullptr
jest typem wartościstd::nullptr_t
. O ile nie potrzebujesz kompilacji programu w wersji wcześniejszej niż C ++ 11, powinieneś zawsze używaćnullptr
zamiastNULL
.C jest nieco inny (cytaty z projektu C11 N1548):
Sprawa jest więc podobna do postu C ++ 11, tzn. Warunku nadużycia
NULL
prac w zależności od wyborów dokonanych przez implementację języka.źródło
Tak , ale w zależności od implementacji może być konieczna obsada. Ale tak, jest w 100% uzasadniony, w przeciwnym razie.
Chociaż jest to naprawdę, bardzo zły styl (nie trzeba dodawać?).
NULL
jest lub faktycznie nie był C ++, to jest C. Standard ma jednak, podobnie jak w przypadku wielu wersji C, dwie klauzule ([diff.null] i [support.types.nullptr]), które technicznie tworząNULL
C ++. Jest to zdefiniowana przez implementację stała zerowego wskaźnika . Dlatego nawet jeśli jest to zły styl, technicznie jest tak C ++, jak to tylko możliwe.Jak wskazano w przypisie , możliwe implementacje mogą być
0
albo0L
, ale nie(void*)0
.NULL
mógłby oczywiście (standard nie mówi wprost, ale jest to właściwie jedyny wybór pozostały po0
lub0L
)nullptr
. Prawie nigdy tak nie jest, ale jest to prawna możliwość.Ostrzeżenie, które pokazał ci kompilator, pokazuje, że kompilator w rzeczywistości nie jest zgodny (chyba że skompilowałeś w trybie C). Ponieważ, no cóż, zgodnie z ostrzeżeniem, przekształcił wskaźnik zerowy (nie
nullptr
który z nichnullptr_t
, który byłby wyraźny), więc najwyraźniej definicjaNULL
jest rzeczywiście(void*)0
, a może nie być.Tak czy inaczej, masz dwa możliwe uzasadnione (tj. Nie uszkodzony kompilator) przypadki. Albo (realistyczny przypadek),
NULL
jest czymś w rodzaju0
lub0L
, wtedy masz konwersję „zero lub jeden” na liczbę całkowitą i możesz zacząć.Lub
NULL
rzeczywiścienullptr
. W takim przypadku masz wyraźną wartość, która gwarantuje gwarancje porównania, a także jasno określone konwersje z liczb całkowitych, ale niestety nie na liczby całkowite. Ma jednak jasno określoną konwersję nabool
(w wynikufalse
) ibool
ma jasno zdefiniowaną konwersję na liczbę całkowitą (w wyniku0
).Niestety, to dwie konwersje, więc nie mieści się w „zero lub jeden”, jak wskazano w [konw.]. Tak więc, jeśli twoja implementacja zdefiniuje
NULL
jakonullptr
, będziesz musiał dodać jawną rzutowanie, aby Twój kod był poprawny.źródło
Z C faq:
źródło
Oświadczenie: Nie znam C ++. Moja odpowiedź nie ma być stosowana w kontekście C ++
'\0'
maint
wartość zero, tylko 100% dokładnie jak0
.W kontekście wskaźników ,
0
iNULL
w 100% równoważne:są w 100% równoważne.
Uwaga na temat
ptr + NULL
Kontekst nie
ptr + NULL
obejmuje wskazówek. Nie ma definicji dodawania wskaźników w języku C; wskaźniki i liczby całkowite można dodawać (lub odejmować). W przypadku, gdy jeden lub jest wskaźnikiem, drugi musi być liczbą całkowitą, więc jest skuteczny lub w zależności od definicji i kilku zachowań można się spodziewać: to wszystko działa, ostrzeżenie o konwersji między wskaźnikiem a liczbą całkowitą, brak kompilacji, .. .ptr + NULL
ptr
NULL
ptr + NULL
(int)ptr + NULL
ptr + (int)NULL
ptr
NULL
źródło
#define NULL (void *)0
wcześniej Czy na pewno NULL i zwykły 0 są w 100% równoważne?ptr + NULL
nie jest używanyNULL
w kontekście wskaźnikównullptr
jest, ale((void*)0)
i0
(lub'\0'
) są równoważne w kontekście wskaźników ...if (ptr == '\0' /* or equivalent 0, NULL */)
Nie, już nie wolał używać
NULL
(stary sposób inicjowania wskaźnika).Od C ++ 11:
nullptr
Słowo kluczowe oznacza literał wskaźnika. Jest to wartość typu std :: nullptr_t. Istnieją niejawne konwersje znullptr
na wartość zerowego wskaźnika dowolnego typu wskaźnika i dowolnego wskaźnika na typ elementu. Podobne konwersje istnieją dla dowolnej stałej wskaźnika zerowego, która zawiera wartości typustd::nullptr_t
oraz makroNULL
.https://en.cppreference.com/w/cpp/language/nullptr
Właściwie std :: nullptr_t jest typu null pointer dosłowne
nullptr
. Jest to odrębny typ, który sam nie jest typem wskaźnika ani wskaźnikiem typu elementu.Wynik:
źródło