W C, pojawiają się różnice pomiędzy różnymi wartościami Zero - NULL
, NUL
i 0
.
Wiem, że znak ASCII '0'
ocenia na 48
lub 0x30
.
NULL
Wskaźnik jest zazwyczaj definiowane jako:
#define NULL 0
Lub
#define NULL (void *)0
Ponadto istnieje NUL
postać, '\0'
która wydaje się również oceniać 0
.
Czy zdarza się, że te trzy wartości nie mogą być równe?
Czy dotyczy to również systemów 64-bitowych?
NUL
nie istnieje w standardowym języku lub bibliotece C (lub, o ile wiem, w C ++). Znak null jest czasami nazywany NUL, ale jest to C lub C ++, które zwykle określa się mianem'\0'
.Odpowiedzi:
Uwaga: Ta odpowiedź dotyczy języka C, a nie C ++.
Wskaźniki zerowe
Stała literałowa liczby całkowitej
0
ma różne znaczenia w zależności od kontekstu, w którym jest używana. We wszystkich przypadkach jest to wciąż liczba całkowita z wartością0
, jest ona opisana na różne sposoby.Jeśli wskaźnik jest porównywany ze stałym literałem
0
, jest to sprawdzenie, czy wskaźnik jest wskaźnikiem zerowym. Jest0
to następnie określane jako stała zerowego wskaźnika. Standard C definiuje, że0
rzut na ten typvoid *
jest zarówno wskaźnikiem zerowym, jak i stałą wskaźnika zerowego.Dodatkowo, aby poprawić czytelność, makro
NULL
znajduje się w pliku nagłówkowymstddef.h
. W zależności od kompilatora może być możliwe#undef NULL
i przedefiniowanie go do czegoś zwariowanego.Dlatego oto kilka prawidłowych sposobów sprawdzania pustego wskaźnika:
NULL
jest zdefiniowany do porównania równego zerowemu wskaźnikowi. Jest to implementacja zdefiniowana jako rzeczywista definicjaNULL
, pod warunkiem, że jest to poprawna stała wskaźnika zerowego.0
jest kolejną reprezentacją stałej wskaźnika zerowego.To
if
stwierdzenie domyślnie sprawdza, że „nie jest 0”, więc odwracamy to, aby znaczyło „wynosi 0”.Poniżej przedstawiono NIEPRAWIDŁOWE sposoby sprawdzania pustego wskaźnika:
Dla kompilatora nie jest to sprawdzenie wskaźnika zerowego, ale sprawdzenie równości dwóch zmiennych. Może to działać, jeśli mynull nigdy się nie zmienia w kodzie, a stała optymalizacji kompilatora składa wartość 0 w instrukcji if, ale nie jest to gwarantowane, a kompilator musi wygenerować co najmniej jeden komunikat diagnostyczny (ostrzeżenie lub błąd) zgodnie ze standardem C.
Zauważ, że czym jest wskaźnik zerowy w języku C. Nie ma to znaczenia dla podstawowej architektury. Jeśli architektura bazowa ma zerową wartość wskaźnika zdefiniowaną jako adres 0xDEADBEEF, to kompilator musi rozwiązać ten problem.
Jako takie, nawet w tej zabawnej architekturze, następujące sposoby są nadal poprawnymi sposobami sprawdzania pustego wskaźnika:
Poniżej przedstawiono NIEPRAWIDŁOWE sposoby sprawdzania pustego wskaźnika:
ponieważ są one postrzegane przez kompilator jako zwykłe porównania.
Brak znaków
'\0'
jest zdefiniowany jako znak zerowy - jest to znak ze wszystkimi bitami ustawionymi na zero. To nie ma nic wspólnego ze wskaźnikami. Jednak możesz zobaczyć coś podobnego do tego kodu:sprawdza, czy wskaźnik łańcucha wskazuje na znak zerowy
sprawdza, czy wskaźnik łańcucha wskazuje na znak inny niż null
Nie myl ich ze wskaźnikami zerowymi. Tylko dlatego, że reprezentacja bitów jest taka sama, a to pozwala na pewne wygodne przypadki krzyżowania, nie są to tak naprawdę same.
Dodatkowo
'\0'
jest (podobnie jak wszystkie literały znakowe) stałą całkowitą, w tym przypadku o wartości zero. Więc'\0'
jest całkowicie równoważna ozdób0
stałej liczby całkowitej - jedyną różnicą jest intencją , że przenosi się do ludzkiego czytelnika ( „Używam tego jako znak NULL.”).Bibliografia
Więcej informacji znajduje się w pytaniu 5.3 w comp.lang.c FAQ . Zobacz ten pdf dla standardu C. Sprawdź sekcje 6.3.2.3 Wskaźniki, akapit 3.
źródło
ptr
do zera wszystkich bitów . To nie jestmemcmp
, ale jest to porównanie przy użyciu wbudowanego operatora. Jedna strona to wskaźnik zerowy'\0'
, a druga to wskaźnik. Podobnie jak w przypadku pozostałych dwóch wersji zNULL
i0
. Ci trzej robią to samo.0xDEADBEEF
jest jeszcze wskaźnik null, bez względu na jej łańcuch bitów wygląda i będzie nadal porównać równeNULL
,0
,\0
a wszystkie inne formy stałe wskaźnik NULL.ptr == '\0'
.Wygląda na to, że wiele osób nie rozumie, jakie są różnice między NULL, „\ 0” i 0. Tak więc, aby wyjaśnić i starając się uniknąć powtarzania rzeczy, powiedziano wcześniej:
Stałe wyrażenie typu
int
o wartości 0 lub wyrażenie tego typu rzutowane na typvoid *
jest stałą wskaźnika zerowego , która po przekształceniu w wskaźnik staje się wskaźnikiem zerowym . Standard gwarantuje, że porównanie będzie nierówne z dowolnym wskaźnikiem do dowolnego obiektu lub funkcji .NULL
to makro zdefiniowane jako stała zerowego wskaźnika .\0
to konstrukcja używana do reprezentowania znaku zerowego , używana do zakończenia łańcucha.Znak null to bajt, którego wszystkie bity są ustawione na 0.
źródło
Wszystkie trzy definiują znaczenie zera w innym kontekście.
Te trzy są zawsze różne, gdy spojrzysz na pamięć:
Mam nadzieję, że to wyjaśnia.
źródło
sizeof('\0')
i zaskocz się.Jeśli NULL i 0 są równoważne jako stałe wskaźnika zerowego, co powinienem zastosować? na liście C FAQ dotyczy również tego problemu:
źródło
„znak zerowy (NUL)” najłatwiej jest wykluczyć.
'\0'
to dosłowny charakter. W C jest zaimplementowany jakoint
, więc jest taki sam jak 0, czyli zINT_TYPE_SIZE
. W C ++ literał znakowy jest zaimplementowany jakochar
, który ma 1 bajt. Zwykle różni się to odNULL
lub0
.Następnie
NULL
jest wartością wskaźnika, która określa, że zmienna nie wskazuje żadnej przestrzeni adresowej. Pomijając fakt, że zwykle jest implementowany jako zera, musi być w stanie wyrazić pełną przestrzeń adresową architektury. Zatem w architekturze 32-bitowej NULL (prawdopodobnie) ma 4 bajty, a w architekturze 64-bitowej 8 bajtów. To zależy od wdrożenia C.Wreszcie, literał
0
jest typuint
, który ma rozmiarINT_TYPE_SIZE
. Domyślna wartośćINT_TYPE_SIZE
może być różna w zależności od architektury.Apple napisał:
Wikipedia 64-bit :
Edycja : Dodano więcej literału postaci.
Powyższy kod zwraca 4 w przypadku gcc i 1 w przypadku g ++.
źródło
'\0'
to nie jest wartość 1-bajtowy. Jest to dosłowny znak, który jest ciągłym wyrażeniem liczb całkowitych - więc jeśli można powiedzieć, że ma rozmiar, to jest to rozmiarint
(który musi wynosić co najmniej 2 bajty). Jeśli mi nie wierzysz, oceńsizeof('\0')
i przekonaj się sam.'\0'
,0
I0x0
są w pełni równoważne.sizeof('\0')
kompilator C ++.Jeden-L NUL, kończy ciąg.
Dwie L NULL nie wskazuje na nic.
I postawię złotego byka
Że nie ma trzy-L NULLL.
Jak radzisz sobie z NUL?
źródło
Jeden dobry kawałek, który pomaga mi, gdy zaczynam od C (zaczerpnięte z Expert C Programming by Linden)
One 'l' nul i Two 'l' null
Zapamiętaj ten mały wierszyk, aby przywołać poprawną terminologię wskaźników i ASCII zero:
Znak ASCII ze wzorem bitowym równym zero jest nazywany „NUL”. Specjalna wartość wskaźnika, która oznacza, że wskaźnik nigdzie nie wskazuje, to „NULL”. Te dwa terminy nie mają znaczenia zamiennego.
źródło
NUL
jest to kod, takich jak kontrolaBEL
,VT
,HT
,SOT
itd., A zatem ma max. 3 znaki.„NUL” nie jest 0, ale odnosi się do znaku NUL ASCII. Przynajmniej tak to widziałem. Wskaźnik zerowy jest często definiowany jako 0, ale zależy to od środowiska, w którym działasz, oraz specyfikacji używanego systemu operacyjnego lub języka.
W ANSI C wskaźnik zerowy jest określony jako liczba całkowita 0. Zatem każdy świat, w którym to nieprawda, nie jest zgodny z ANSI C.
źródło
Bajt o wartości
0x00
to w tablicy ASCII znak specjalny o nazwieNUL
lubNULL
. W C, ponieważ nie należy osadzać znaków kontrolnych w kodzie źródłowym, jest to reprezentowane w ciągach C ze znakiem ucieczki 0, tj\0
.Ale prawdziwa wartość NULL nie jest jest wartością. To brak wartości. W przypadku wskaźnika oznacza to, że wskaźnik nie ma na co wskazywać. W bazie danych oznacza to, że pole nie ma wartości (co nie jest tym samym, co stwierdzenie, że pole jest puste, 0 lub wypełnione spacjami).
Rzeczywista wartość danego formatu pliku bazy danych systemu lub używa do reprezentuję
NULL
nie jest koniecznie0x00
.źródło
NULL
nie ma gwarancji, że wynosi 0 - jego dokładna wartość zależy od architektury. Większość głównych architektur to definiuje(void*)0
.'\0'
będzie zawsze równa 0, ponieważ w ten sposób bajt 0 jest kodowany w dosłownym znaku.Nie pamiętam, czy kompilatory C są wymagane do używania ASCII - jeśli nie,
'0'
może nie zawsze być równe 48. Niezależnie od tego, jest mało prawdopodobne, że kiedykolwiek spotkasz system, który używa alternatywnego zestawu znaków, takiego jak EBCDIC, chyba że pracujesz nad bardzo niejasne systemy.Rozmiary różnych typów będą się różnić w systemach 64-bitowych, ale wartości całkowite będą takie same.
Niektórzy komentatorzy wyrazili wątpliwości, czy wartość NULL będzie równa 0, ale nie będzie zerowa. Oto przykładowy program wraz z oczekiwanymi danymi wyjściowymi w takim systemie:
Ten program może wydrukować:
źródło
(void *) 0 ma wartość NULL, a „\ 0” oznacza koniec ciągu.
źródło