W C (lub C ++ w tym przypadku) wskaźniki są szczególne, jeśli mają wartość zero: radzę ustawić wskaźniki na zero po zwolnieniu ich pamięci, ponieważ oznacza to, że ponowne zwolnienie wskaźnika nie jest niebezpieczne; kiedy wywołuję malloc, zwraca wskaźnik o wartości zero, jeśli nie może uzyskać pamięci; if (p != 0)
Cały czas używam, aby upewnić się, że przekazane wskaźniki są prawidłowe itp.
Ale skoro adresowanie pamięci zaczyna się od 0, czy 0 nie jest tak samo poprawnym adresem jak każdy inny? W jaki sposób można użyć 0 do obsługi wskaźników zerowych, jeśli tak jest? Dlaczego zamiast tego liczba ujemna nie jest równa null?
Edytować:
Kilka dobrych odpowiedzi. Podsumuję to, co zostało powiedziane w odpowiedziach wyrażonych w moim własnym umyśle i mam nadzieję, że społeczność mnie poprawi, jeśli źle zrozumiem.
Jak wszystko inne w programowaniu, jest to abstrakcja. Tylko stała, niezupełnie związana z adresem 0. C ++ 0x podkreśla to poprzez dodanie słowa kluczowego
nullptr
.To nawet nie jest abstrakcja adresu, jest to stała określona przez standard C i kompilator może przetłumaczyć ją na inną liczbę, o ile upewni się, że nigdy nie jest równa "prawdziwemu" adresowi i równa się innym zerowym wskaźnikom, jeśli 0 nie jest najlepsza wartość do wykorzystania dla platformy.
W przypadku, gdy nie jest to abstrakcja, co miało miejsce we wczesnych dniach, adres 0 jest używany przez system i nie jest ograniczony dla programisty.
Przyznaję, że moja sugestia dotycząca liczby ujemnej była trochę szalona. Używanie liczby całkowitej ze znakiem dla adresów jest trochę marnotrawne, jeśli oznacza to, że oprócz wskaźnika zerowego (-1 lub cokolwiek innego) przestrzeń wartości jest dzielona równo między dodatnimi liczbami całkowitymi, które tworzą prawidłowe adresy, i liczbami ujemnymi, które są po prostu marnowane.
Jeśli jakakolwiek liczba jest zawsze reprezentowana przez typ danych, to jest to 0. (Prawdopodobnie 1 to także. Myślę o jednobitowej liczbie całkowitej, która będzie równa 0 lub 1, jeśli nie ma znaku, lub tylko o bicie ze znakiem, jeśli byłoby [-2, 1]. Ale wtedy możesz po prostu ustawić 0 jako null, a 1 to jedyny dostępny bajt w pamięci).
Wciąż mam coś nierozwiązanego. Wskaźnik przepełnienia stosu do określonego stałego adresu mówi mi, że nawet jeśli 0 dla wskaźnika pustego jest abstrakcją, inne wartości wskaźnika niekoniecznie. To prowadzi mnie do opublikowania kolejnego pytania o przepełnienie stosu. Czy kiedykolwiek będę chciał uzyskać dostęp do adresu zero? .
if (p != 0)
naif (p)
który jest powszechnym idiomem w C i C ++, chociaż będziesz musiał pozbyć się tego nawyku, jeśli zdecydujesz się na Javę.0xDEADBEEF
.Odpowiedzi:
2 punkty:
tylko stała wartość 0 w kodzie źródłowym jest wskaźnikiem zerowym - implementacja kompilatora może użyć dowolnej wartości, której chce lub potrzebuje w działającym kodzie. Niektóre platformy mają specjalną wartość wskaźnika, która jest „nieprawidłowa”, której implementacja może użyć jako wskaźnika pustego. W C FAQ pojawia się pytanie: „Poważnie, czy jakieś rzeczywiste maszyny rzeczywiście używały niezerowych wskaźników zerowych lub różnych reprezentacji wskaźników do różnych typów?” , co wskazuje na kilka platform, które używały tej właściwości 0 będącej pustym wskaźnikiem w źródle C, ale reprezentowanej inaczej w czasie wykonywania. Standard C ++ ma uwagę, która wyjaśnia, że konwersja „integralnego wyrażenia stałego o wartości zero zawsze daje wskaźnik zerowy,
wartość ujemna mogłaby być tak samo użyteczna dla platformy jak adres - standard C musiał po prostu wybrać coś, co będzie wskazywało na pusty wskaźnik, i wybrano zero. Naprawdę nie jestem pewien, czy wzięto pod uwagę inne wartości wartownicze.
Jedyne wymagania dotyczące wskaźnika pustego to:
źródło
Historycznie przestrzeń adresowa zaczynająca się od 0 była zawsze ROM, używana w niektórych systemach operacyjnych lub procedurach obsługi przerwań niskiego poziomu, obecnie, ponieważ wszystko jest wirtualne (w tym przestrzeń adresowa), system operacyjny może mapować dowolną alokację na dowolny adres, więc specjalnie NIE przydzielaj niczego pod adresem 0.
źródło
IIRC, wartość „wskaźnika zerowego” nie jest gwarantowana jako zero. Kompilator tłumaczy 0 na dowolną wartość „zerową” odpowiednią dla systemu (która w praktyce jest prawdopodobnie zawsze równa zero, ale niekoniecznie). To samo tłumaczenie jest stosowane zawsze, gdy porównujesz wskaźnik z zerem. Ponieważ możesz porównywać wskaźniki tylko ze sobą i z tą specjalną wartością-0, to izoluje to programistę od wiedzy o reprezentacji pamięci systemu. Co do tego, dlaczego wybrali 0 zamiast 42 lub trochę więcej, zgadnę, że to dlatego, że większość programistów zaczyna liczyć od 0 :) (Ponadto w większości systemów 0 to pierwszy adres pamięci i chcieli, aby był wygodny, ponieważ w ćwicz tłumaczenia, które opisuję, rzadko mają miejsce; język po prostu na to pozwala).
źródło
int* p = 0
) może spowodować utworzenie wskaźnika zawierającego wartość0xdeadbeef
lub dowolną inną preferowaną wartość. 0 jest wskaźnikiem zerowym, ale wskaźnik zerowy niekoniecznie jest wskaźnikiem do adresu zero. :)Musisz źle rozumieć znaczenie stałego zera w kontekście wskaźnika.
Ani w C, ani w C ++ wskaźniki nie mogą mieć „wartości zero”. Wskaźniki nie są obiektami arytmetycznymi. Nie mogą mieć wartości liczbowych, takich jak „zero”, „ujemny” ani niczego podobnego. Więc twoje stwierdzenie o „wskaźnikach… mają wartość zero” po prostu nie ma sensu.
W C i C ++ wskaźniki mogą mieć zarezerwowaną wartość wskaźnika zerowego . Rzeczywista reprezentacja wartości wskaźnika null nie ma nic wspólnego z żadnymi „zerami”. Może to być absolutnie wszystko, co jest odpowiednie dla danej platformy. Prawdą jest, że na większości plaform wartość wskaźnika null jest fizycznie reprezentowana przez rzeczywistą zerową wartość adresu. Jeśli jednak na niektórych platformach adres 0 jest rzeczywiście używany do jakiegoś celu (np. Może być konieczne utworzenie obiektów pod adresem 0), wartość wskaźnika zerowego na takiej platformie najprawdopodobniej będzie inna. Może być na przykład fizycznie reprezentowany jako
0xFFFFFFFF
wartość adresu lub jako0xBAADBAAD
wartość adresu.Niemniej jednak, niezależnie od tego, jak wartość wskaźnika null jest reprezentowana na danej platformie, w swoim kodzie nadal będziesz oznaczać wskaźniki null za pomocą stałej
0
. Aby przypisać wartość pustego wskaźnika do danego wskaźnika, będziesz nadal używać wyrażeń takich jakp = 0
. Obowiązkiem kompilatora jest uświadomienie sobie, czego chcesz i przetłumaczenie tego na odpowiednią reprezentację wartości wskaźnika zerowego, tj. Przetłumaczenie tego na kod, który umieści wartość adresu0xFFFFFFFF
we wskaźnikup
, na przykład.Krótko mówiąc, fakt, że używasz
0
w swoim kodzie sorce do generowania wartości wskaźnika null nie oznacza, że wartość wskaźnika null jest w jakiś sposób powiązana z adresem0
. To0
, czego używasz w swoim kodzie źródłowym, jest po prostu „cukrem składniowym”, który nie ma absolutnie żadnego związku z rzeczywistym adresem fizycznym, na który wskazuje wartość wskaźnika zerowego.źródło
(p1 - nullptr) - (p2 - nullptr) == (p1 - p2)
.NULL
wyraźnie nie musi być reprezentowany przez 0.W niektórych / wielu / wszystkich systemach operacyjnych adres pamięci 0 jest w pewien sposób szczególny. Na przykład często jest mapowany na nieprawidłową / nieistniejącą pamięć, co powoduje wyjątek, jeśli próbujesz uzyskać do niej dostęp.
Myślę, że wartości wskaźnika są zwykle traktowane jako liczby bez znaku: w przeciwnym razie na przykład 32-bitowy wskaźnik byłby w stanie zaadresować tylko 2 GB pamięci zamiast 4 GB.
źródło
Domyślam się, że magiczna wartość 0 została wybrana do zdefiniowania nieprawidłowego wskaźnika, ponieważ można go przetestować przy użyciu mniejszej liczby instrukcji. Niektóre języki maszynowe automatycznie ustawiają flagi zera i znaku zgodnie z danymi podczas ładowania rejestrów, dzięki czemu można przetestować wskaźnik zerowy z prostym ładowaniem i rozgałęzieniem instrukcji bez wykonywania oddzielnych instrukcji porównania.
(Większość ISA ustawia flagi tylko dla instrukcji ALU, a nie ładuje. I zazwyczaj nie tworzysz wskaźników przez obliczenia, z wyjątkiem kompilatora podczas analizowania źródła C. Ale przynajmniej nie potrzebujesz dowolnej stałej szerokości wskaźnika do porównać z.)
Na Commodore Pet, Vic20 i C64, które były pierwszymi maszynami, nad którymi pracowałem, pamięć RAM zaczynała się w miejscu 0, więc można było czytać i pisać przy użyciu wskaźnika zerowego, jeśli naprawdę chcesz.
źródło
Myślę, że to tylko konwencja. Musi istnieć jakaś wartość, aby oznaczyć nieprawidłowy wskaźnik.
Po prostu tracisz jeden bajt przestrzeni adresowej, co rzadko powinno stanowić problem.
Nie ma negatywnych wskaźników. Wskaźniki są zawsze bez znaku. Również jeśli mogłyby być ujemne, twoja konwencja oznaczałaby utratę połowy przestrzeni adresowej.
źródło
char *p = (char *)1; --p;
. Ponieważ zachowanie wskaźnika zerowego jest nieokreślone przez standard, ten system może wp
rzeczywistości odczytywać i zapisywać adres 0, inkrementować w celu podania adresu1
itp.char x = ((char*)0);
aby odczytać adres zero i zapisać tę wartość w x. Taki kod dałby Undefined Behavior każdej implementacji, która nie definiuje jej zachowania, ale fakt, że standard mówi, że coś jest niezdefiniowanym zachowaniem, w żaden sposób nie zabrania implementacjom oferowania własnych specyfikacji tego, co zrobi.*(char *)0
. To prawda, ale moim zdaniem implementacja nie musi definiować zachowania*(char *)0
ani żadnych innych operacji wskaźnika zerowego.char *p = (char*)1; --p;
byłoby zdefiniowane przez standard tylko wtedy, gdyby ta sekwencja została wykonana po tym, jak wskaźnik na coś innego niż pierwszy bajt obiektu zostałby rzutowany na obiekt anintptr_t
, a wynik tego rzutowania miałby wartość 1 iw tym konkretnym przypadku wynik of--p
dałby wskaźnik do bajtu poprzedzającego bajt, którego wartość wskaźnika po rzutowaniu naintptr_t
niego dała1
.Chociaż C używa 0 do reprezentowania wskaźnika zerowego, należy pamiętać, że wartość samego wskaźnika może nie być zerowa. Jednak większość programistów będzie używać tylko systemów, w których wskaźnik zerowy wynosi w rzeczywistości 0.
Ale dlaczego zero? Cóż, to jeden adres wspólny dla każdego systemu. Często niskie adresy są zarezerwowane dla celów systemu operacyjnego, dlatego wartość działa również jako niedostępna dla programów użytkowych. Przypadkowe przypisanie wartości całkowitej do wskaźnika może skończyć się zerem tak samo, jak cokolwiek innego.
źródło
W przeszłości mała ilość pamięci aplikacji była zajęta przez zasoby systemowe. W tamtych czasach zero stało się domyślną wartością zerową.
Chociaż niekoniecznie jest to prawdą w przypadku nowoczesnych systemów, nadal złym pomysłem jest ustawianie wartości wskaźnika na cokolwiek innego niż to, co zapewniła alokacja pamięci.
źródło
Odnośnie argumentu dotyczącego nie ustawiania wskaźnika na wartość null po jego usunięciu, aby w przyszłości usuwać „ujawniać błędy” ...
Jeśli naprawdę się tym martwisz, lepszym podejściem, które na pewno zadziała, jest wykorzystanie assert ():
Wymaga to dodatkowego wpisania i jednego dodatkowego sprawdzenia podczas kompilacji debugowania, ale z pewnością da ci to, czego chcesz: zauważ, że ptr jest usuwany „dwukrotnie”. Alternatywa podana w dyskusji o komentarzach, a nie ustawianie wskaźnika na wartość null, aby uzyskać awarię, po prostu nie gwarantuje sukcesu. Co gorsza, w przeciwieństwie do powyższego, może spowodować awarię (lub znacznie gorzej!) Użytkownika, jeśli jeden z tych „błędów” dostanie się na półkę. Wreszcie, ta wersja umożliwia dalsze uruchamianie programu, aby zobaczyć, co się naprawdę dzieje.
Zdaję sobie sprawę, że to nie odpowiada na zadane pytanie, ale martwiłem się, że ktoś czytający komentarze może dojść do wniosku, że za `` dobrą praktykę '' NIE należy ustawiać wskaźników na 0, jeśli jest możliwe, że zostaną wysłane na bezpłatne () lub usuń dwukrotnie. W tych kilku przypadkach, gdy jest to możliwe, NIGDY nie jest dobrą praktyką używanie niezdefiniowanego zachowania jako narzędzia do debugowania. Nikt, kto nigdy nie musiał szukać błędu, który został ostatecznie spowodowany usunięciem nieprawidłowego wskaźnika, nie zaproponowałby tego. Wytropienie tego rodzaju błędów zajmuje wiele godzin i prawie zawsze wpływa na program w całkowicie nieoczekiwany sposób, który jest trudny lub niemożliwy do wyśledzenia pierwotnego problemu.
źródło
Ważnym powodem, dla którego wiele systemów operacyjnych wykorzystuje wszystkie bity-zero do reprezentacji wskaźnika zerowego, jest to, że oznacza to
memset(struct_with_pointers, 0, sizeof struct_with_pointers)
i podobne, ustawi wszystkie wskaźniki wewnątrzstruct_with_pointers
na wskaźniki zerowe. Standard C nie gwarantuje tego, ale wiele, wiele programów tak zakłada.źródło
Na jednej ze starych maszyn DEC (myślę, że PDP-8), środowisko wykonawcze C chroniłoby pamięć pierwszej strony pamięci, tak aby każda próba uzyskania dostępu do pamięci w tym bloku spowodowałaby zgłoszenie wyjątku.
źródło
Wybór wartości wartowniczej jest arbitralny i faktycznie jest on adresowany przez następną wersję C ++ (nieformalnie znaną jako „C ++ 0x”, najprawdopodobniej w przyszłości znaną jako ISO C ++ 2011) wraz z wprowadzeniem słowo kluczowe
nullptr
reprezentujące wskaźnik o wartości null. W C ++ wartość 0 może być użyta jako wyrażenie inicjalizujące dla dowolnego POD i dowolnego obiektu z domyślnym konstruktorem i ma specjalne znaczenie przypisywania wartości wartowniczej w przypadku inicjalizacji wskaźnika. Jeśli chodzi o to, dlaczego nie wybrano wartości ujemnej, adresy zwykle mieszczą się w zakresie od 0 do 2 N.-1 dla pewnej wartości N. Innymi słowy, adresy są zwykle traktowane jako wartości bez znaku. Gdyby wartość maksymalna została użyta jako wartość wartownicza, musiałaby zmieniać się w zależności od systemu w zależności od rozmiaru pamięci, podczas gdy 0 jest zawsze reprezentowalnym adresem. Jest również używany ze względów historycznych, ponieważ adres pamięci 0 był zwykle bezużyteczny w programach, a obecnie większość systemów operacyjnych ma części jądra załadowane na dolne strony pamięci, a takie strony są zwykle chronione w taki sposób, że jeśli dotknięty (usunięty) przez program (zapisz jądro) spowoduje błąd.źródło
Musi mieć jakąś wartość. Oczywiście nie chcesz wkraczać na wartości, których użytkownik mógłby zasadnie chcieć użyć. Spekuluję, że skoro środowisko wykonawcze C zapewnia segment BSS dla danych zainicjowanych zerem, w pewnym stopniu sensowne jest interpretowanie zera jako niezainicjowanej wartości wskaźnika.
źródło
Rzadko gdy system operacyjny pozwala na pisanie pod adresem 0. Często zdarza się, że pliki specyficzne dla systemu operacyjnego są zapisywane w małej ilości pamięci; mianowicie IDT, tabele stron itp. (Tabele muszą być w pamięci RAM i łatwiej jest je umieścić na dole, niż próbować określić, gdzie jest górna część pamięci RAM). I żaden system operacyjny przy zdrowych zmysłach nie pozwoli ci edytuj tabele systemowe chcąc nie chcąc.
Być może K & R nie myślał o tym, kiedy tworzyli C, ale (wraz z faktem, że 0 == null jest dość łatwe do zapamiętania) sprawia, że 0 jest popularnym wyborem.
źródło
Wartość
0
jest specjalną wartością, która w określonych wyrażeniach przybiera różne znaczenia. W przypadku wskaźników, jak wielokrotnie wskazywano, używa się go zapewne dlatego, że w tamtych czasach był to najwygodniejszy sposób powiedzenia „wstaw tutaj domyślną wartość wartowniczą”. Jako wyrażenie stałe nie ma tego samego znaczenia co bitowe zero (tj. Wszystkie bity ustawione na zero) w kontekście wyrażenia wskaźnikowego. W C ++ istnieje kilka typów, które nie mają bitowej reprezentacji zerowej,NULL
takich jak element członkowski wskaźnika i wskaźnik do funkcji elementu członkowskiego.Na szczęście, C ++ 0x ma nowego słowa kluczowego dla „wyrażenia, które oznacza znaną nieprawidłowy wskaźnik, który nie jest również mapować do bitowym zera do wyrażenia integralnych”:
nullptr
. Chociaż istnieje kilka systemów, na które można kierować za pomocą C ++, które pozwalają na wyłuskiwanie adresu 0 bez barfingu, należy więc uważać.źródło
W tym wątku jest już wiele dobrych odpowiedzi; prawdopodobnie istnieje wiele różnych powodów preferowania wartości
0
wskaźników zerowych, ale dodam jeszcze dwa:źródło
Zależy to od implementacji wskaźników w C / C ++. Nie ma konkretnego powodu, dla którego NULL jest równoważne w przypisaniach do wskaźnika.
źródło
Istnieją historyczne powody, ale są też powody optymalizacji.
System operacyjny często udostępnia proces ze stronami pamięci zainicjowanymi na 0. Jeśli program chce zinterpretować część tej strony pamięci jako wskaźnik, to jest to 0, więc program może łatwo określić, że ten wskaźnik jest nie zainicjowano. (nie działa to tak dobrze, gdy jest stosowane do niezainicjowanych stron Flash)
Innym powodem jest to, że na wielu, wielu procesorach bardzo łatwo jest przetestować równoważność wartości do 0. Czasami jest to bezpłatne porównanie wykonane bez dodatkowych instrukcji i zwykle można to zrobić bez konieczności podawania wartości zerowej w innym rejestrze lub jako literał w strumieniu instrukcji do porównania.
Tanie porównania dla większości procesorów są ze znakiem mniejszym niż 0 i równym 0 (ze znakiem większym niż 0 i różnym od 0 są implikowane przez oba te sposoby)
Ponieważ 1 wartość ze wszystkich możliwych wartości musi być zarezerwowana jako zła lub niezainicjowana, równie dobrze możesz uczynić ją tą, która ma najtańszy test równoważności ze złą wartością. Dotyczy to również ciągów znaków zakończonych '\ 0'.
Gdybyś spróbował użyć do tego celu więcej lub mniej niż 0, skończyłoby się na przecięciu zakresu adresów o połowę.
źródło
Stała
0
jest używany zamiastNULL
ponieważ C została wykonana przez niektórych jaskiniowców bilionów lat temuNULL
,NIL
,ZIP
, lubNADDA
miałby wszystko miało znacznie więcej sensu niż0
.W rzeczy samej. Chociaż wiele systemów operacyjnych nie zezwala na mapowanie czegokolwiek pod adresem zero, nawet w wirtualnej przestrzeni adresowej (ludzie zdali sobie sprawę, że C jest niezabezpieczonym językiem i odzwierciedlając to, że błędy wyłuskiwania zerowego wskaźnika są bardzo częste, zdecydowano się je „naprawić”, odrzucając kod przestrzeni użytkownika do mapowania na stronę 0; Tak więc, jeśli wywołasz wywołanie zwrotne, ale wskaźnik wywołania zwrotnego ma wartość NULL, nie wykonasz dowolnego kodu).
Ponieważ
0
używany w porównaniu ze wskaźnikiem zostanie zastąpiony pewną wartością specyficzną dla implementacji , która jest wartością zwracaną przez malloc w przypadku awarii malloc.Byłoby to jeszcze bardziej zagmatwane.
źródło
int
był nie tylko tego samego rozmiaru co wskaźnik - w wielu kontekstach wskaźnikiint
i wskaźniki mogły być używane zamiennie. Jeśli procedura oczekiwałaby wskaźnika i została przekazana jako liczba całkowita 57, procedura użyłaby adresu z tym samym wzorem bitowym co liczba 57. Na tych konkretnych maszynach wzorzec bitowy oznaczający wskaźnik zerowy wynosił 0, więc przekazywanie wartości int 0 przekazałby pusty wskaźnik.( Przeczytaj ten akapit przed przeczytaniem postu.Wszystkich zainteresowanych przeczytaniem tego posta proszę o uważne przeczytanie i oczywiście nie neguj go, dopóki nie zrozumiesz go całkowicie, dzięki. )
Jest to teraz wiki społeczności, jako taka, jeśli ktoś nie zgadza się z którymkolwiek z pojęć, należy go zmodyfikować, podając jasne i szczegółowe wyjaśnienie, co jest nie tak i dlaczego, a jeśli to możliwe, zacytować źródła lub przedstawić dowody, które można odtworzyć.
Odpowiedź
Oto kilka innych powodów, które mogą być podstawowymi czynnikami dla NULL == 0
if(!my_ptr)
zamiastif(my_ptr==NULL)
.Tutaj chciałbym powiedzieć słowo na temat innych odpowiedzi
Nie z powodu cukru syntaktycznego
Mówienie, że NULL jest równe zero z powodu cukru składniowego, nie ma zbytniego sensu, jeśli tak, dlaczego nie użyć indeksu 0 tablicy do przechowywania jej długości?
W rzeczywistości C jest językiem, który najbardziej przypomina wewnętrzną implementację, czy ma sens powiedzieć, że C wybrał zero tylko z powodu cukru syntaktycznego? Woleliby raczej podać słowo kluczowe null (jak wiele innych języków) niż mapować zero na NULL!
Jako taki, chociaż na dzień dzisiejszy może to być po prostu cukier syntaktyczny, jasne jest, że pierwotnym zamiarem twórców języka C nie był cukier syntaktyczny, co pokażę dalej.
1) Specyfikacja
Chociaż prawdą jest, że specyfikacja C mówi o stałej 0 jako zerowym wskaźniku (sekcja 6.3.2.3), a także definiuje NULL jako definicję implementacji (sekcja 7.19 w specyfikacji C11 i 7.17 w specyfikacji C99), Faktem jest, że w książce „The C Programming Language” napisanej przez wynalazców C w sekcji 5.4 znajduje się, co następuje:
Jak widać (ze słów „adres zerowy”) co najmniej pierwotnym zamysłem autorów C był adres zero, a nie stałe zero, ponadto z tego fragmentu wynika, że powód, dla którego specyfikacja przemawia z stałe zero prawdopodobnie nie wyklucza wyrażenia, którego wynikiem jest zero, ale zamiast tego dołącza stałą zero będącą liczbą całkowitą, która będzie jedyną stałą całkowitą dozwoloną do użycia w kontekście wskaźnika bez rzutowania.
2) Podsumowanie
Chociaż specyfikacja nie mówi wprost, że adres zerowy może być traktowany inaczej niż stała zerowa, nie mówi, że nie, a fakt, że mając do czynienia ze stałą wskaźnika zerowego , nie twierdzi, że jest to implementacja zdefiniowana jako robi przez stałą zdefiniowaną przez NULL , zamiast twierdzić, że jest równe zero, pokazuje, że może istnieć różnica między stałą zerową a adresem zerowym.
(Jeśli jednak tak jest, zastanawiam się tylko, dlaczego NULL jest zdefiniowana w implementacji, skoro w takim przypadku NULL może być również stałym zerem, ponieważ kompilator i tak musi przekonwertować wszystkie stałe zerowe na rzeczywistą implementację zdefiniowaną NULL?)
Jednak nie widzę tego w prawdziwym działaniu, a na ogólnych platformach adres zero i stałe zero są traktowane tak samo i generują ten sam komunikat o błędzie.
Ponadto faktem jest, że dzisiejsze systemy operacyjne faktycznie rezerwują całą pierwszą stronę (zakres od 0x0000 do 0xFFFF), aby uniemożliwić dostęp do adresu zerowego z powodu wskaźnika C NULL (patrz http://en.wikipedia.org/wiki/ Zero_page , a także „Windows Via C / C ++ autorstwa Jeffrey'a Richtera i Christophe'a Nasarre'a (opublikowane przez Microsoft Press)”).
Dlatego prosiłbym każdego, kto twierdzi, że rzeczywiście widział to w akcji, o określenie platformy i kompilatora oraz dokładnego kodu, który faktycznie zrobił (chociaż z powodu niejasnej definicji w specyfikacji [jak pokazałem] każdy kompilator a platforma może robić, co chce).
Wygląda jednak na to, że autorzy C nie mieli tego na myśli i mówili o „adresie zerowym”, a „C gwarantuje, że nigdy nie jest to prawidłowy adres”, a także „NULL to tylko mnemonic ”, wyraźnie pokazując, że jego pierwotna intencja nie dotyczyła„ cukru syntaktycznego ”.
Nie z powodu systemu operacyjnego
Twierdząc również, że system operacyjny odmawia dostępu do adresu zerowego z kilku powodów:
1) Kiedy napisano C, nie było takiego ograniczenia, co można zobaczyć na tej stronie http://en.wikipedia.org/wiki/Zero_page .
2) Faktem jest, że kompilatory C miały dostęp do adresu pamięci zero.
Wydaje się, że jest to fakt z następującego artykułu BellLabs ( http://www.cs.bell-labs.com/who/dmr/primevalC.html )
(W rzeczywistości na dzień dzisiejszy (jak cytowałem powyżej odniesienia z Wikipedii i microsoft press), powodem ograniczenia dostępu do adresu zerowego są wskaźniki C's NULL! Więc na końcu okazuje się, że jest odwrotnie!)
3) Pamiętaj, że C jest również używany do pisania systemów operacyjnych, a nawet kompilatorów C!
W rzeczywistości C został opracowany w celu napisania z nim systemu operacyjnego UNIX i jako taki nie wydaje się być powodem, dla którego mieliby ograniczać się do adresu zero.
(Sprzęt) Wyjaśnienie, w jaki sposób komputery (fizycznie) mogą uzyskać dostęp do adresu zerowego
Jest jeszcze jedna kwestia, którą chcę tutaj wyjaśnić, jak w ogóle można odwołać się do adresu zero?
Pomyśl o tym przez chwilę, adresy są pobierane przez procesor, a następnie wysyłane jako napięcia na magistrali pamięci, która jest następnie używana przez system pamięci, aby dostać się do właściwego adresu, a jednak adres zerowy będzie oznaczał brak napięcia , więc w jaki sposób fizyczny sprzęt systemu pamięci uzyskuje dostęp do adresu zero?
Wydaje się, że odpowiedź brzmi, że adres zerowy jest adresem domyślnym, a innymi słowy adres zero jest zawsze dostępny dla systemu pamięci, gdy magistrala pamięci jest całkowicie wyłączona, i jako takie każde żądanie odczytu lub zapisu bez określenia rzeczywistego adresu (który tak jest w przypadku adresu zero) automatycznie uzyskuje dostęp do adresu zero.
źródło