2013-04-20 9 views
5

Ten problem przez pewien czas mi przeszkadzał. Nigdy nie widziałem inną definicję NULL, to zawszeDlaczego NULL nie jest wstępnie zdefiniowany przez kompilator

#define NULL ((void *) 0) 

jest jakaś architektura gdzie NULL jest zdefiniowana diferently, a jeśli tak, to dlaczego kompilator nie deklaruje to dla nas?

+0

Możesz użyć '0' zamiast' NULL'. – pmg

+0

NULL jest zdefiniowane w 'stddef.h', nie musisz sam go definiować. – Mat

+0

@pmg 0 nie jest przenośny, jeśli odpowiedź na moje pierwsze pytanie jest prawdziwa – stdcall

Odpowiedz

3

C 2011 Standard, online draft

6.3.2.3 Wskaźniki
...
3 Liczba całkowita stałym wyrażeniem o wartości 0 lub takim wyrazem oddanych wpisać void *, nazywa się wskaźnik zerowy stała. 66) Jeśli stała wskaźnika pustego jest konwertowana na typ wskaźnika , wynikowy wskaźnik, o nazwie zerowy wskaźnik, gwarantuje porównywanie nierówności ze wskaźnikiem dla dowolnego obiektu lub funkcji.
66) Makro NULL jest zdefiniowane w <stddef.h> (i innych nagłówkach) jako zerowa stała wskaźnika; patrz 7.19.

makroNULL jest zawsze zdefiniowany jako zerowej wartości stałej ekspresji; może to być nagi 0 lub 0 oddanych do void *, lub jakiś inny integralną wyrażenie, które ma wartość 0. Jeśli chodzi o kodzie źródłowym obawia się, NULL zawsze oceniać na 0.

Gdy kod został przetłumaczone, każde wystąpienie stałej wskaźnika zerowego (0, NULL itd.) zostanie zastąpione przez dowolną architekturę bazową dla wskaźnika zerowego, który może, ale nie musi być wartością 0.

0

Nie znam odpowiedzi na to, ale zgaduję. W C zwykle wykonuje się wiele malloców, a w konsekwencji wiele testów dla zwróconych wskaźników. Ponieważ malloc zwraca void *, a szczególnie (void *) 0 po niepowodzeniu, NULL jest podstawową rzeczą do zdefiniowania w celu przetestowania sukcesu malloc. Ponieważ jest to tak istotne, inne funkcje biblioteki używają NULL (lub (void *) 0) również, jak fopen. Właściwie wszystko, co zwraca wskaźnik.

Dlatego nie ma powodu, aby definiować to na poziomie języka - jest to po prostu specjalna wartość wskaźnika, która może być zwrócona przez wiele funkcji.

1

W epoce ciemnej przed ANSI-C stary K & R C miał wiele różnych implementacji na sprzęcie, który dziś można uznać za dziwaczny. To było przed dniami VM, kiedy maszyny były bardzo "prawdziwe". Adresy zerowe były nie tylko w porządku na tych maszynach, adres zerowy mógłby być popularny ... Myślę, że to CDC, które czasami zapisywało stałą systemową równą zeru (i dziwne rzeczy się zdarzały, gdy ustawiono ją niezerową).

 if (NULL != ptr)  /* like this */ 
if (ptr)    /* never like this */

Sztuką było znalezienie adresu można bezpiecznie używać, aby wskazać „nic” jak przechowywanie rzeczy na końcu pamięci był również popularny, co wykluczało 0xFFFF na niektórych architekturach. A te architektury używały adresów słów zamiast adresów bajtowych.

4

WhozCraig napisał te komentarze do usuniętego już answer, ale może być promowany do pełnej odpowiedzi (i to właśnie tutaj zrobiłem). Zauważa:

Ciekawa uwaga: AS/400 jest bardzo wyjątkowa platforma, gdzie każdy nie- ważny wskaźnik jest uważane za równoważne NULL. Mechanika, którą stosują, aby to zrobić, jest po prostu niesamowita. "Ważny" w tym sensie jest dowolny 128-bitowy wskaźnik (platforma wykorzystuje 128-bitową liniową przestrzeń adresową dla wszystkiego) zawierającą "wartość" uzyskaną przez znany zaufany zbiór instrukcji. Trudno uwierzyć, int *p = (int *)1; if (p) { printf("foo"); } będzie nie wydrukuj "foo" na tej platformie. Wartość przypisana do p nie jest zaufana źródłowo, a zatem jest uznawana za "nieważną" i tym samym równoważną NULL.

To zadziwiające, jak to działa. Każdy 16-bajtowy akapit w zmapowanej wirtualnej przestrzeni adresowej procesu ma odpowiadający "bit" w bitmapie obejmującym cały proces. Wszystkie wskaźniki muszą znajdować się na jednej z tych granic akapitu. Jeśli bit jest "zapalony", odpowiedni wskaźnik został zapisany z zaufanego źródła, w przeciwnym razie jest nieważny i odpowiada NULL. Połączenia do malloc, wskaźnik matematyki, itp., Są badane w celu ustalenia, czy ten kawałek się świeci, czy nie. I jak można sobie wyobrazić, umieszczanie wskaźników w strukturach przynosi zupełnie nowy świat bolesnej idei pakowania konstrukcji.


ta jest oznaczona społeczności Wiki (to nie moja odpowiedź - Nie powinien dostać kredyt), ale można je usunąć, jeśli WhozCraig pisze swoją własną odpowiedź.

To pokazuje, że istnieją prawdziwe platformy o ciekawych właściwościach wskaźnika.

Istnieją platformy, gdzie #define NULL ((void *)0) nie jest zwyczajową definicją; na niektórych platformach może to być tylko 0, na innych, 0L lub 0ULL lub inne odpowiednie wartości, o ile kompilator je rozumie. C++ nie podoba się jako ((void *)0) jako definicja; systemy, w których nagłówki współdziałające z C++ mogą nie używać wersji void indicator.

Nauczyłem się C na komputerze, gdzie reprezentacja dla adresu char * dla danej lokalizacji pamięci była inna niż adres int * dla tej samej lokalizacji pamięci. Było to w czasach poprzedzających void *, ale oznaczało to, że trzeba było poprawnie zadeklarować malloc() (char *malloc(); - również bez prototypów), a musieliście jawnie rzucić zwracaną wartość do poprawnego typu lub otrzymaliście zrzut główny. Bądź wdzięczny za standard C (choć przedmiotowa maszyna, ICL Perq - badged hardware z Three Rivers - została w dużej mierze zastąpiona przez czas zdefiniowania standardu).

+2

Nie mam problemu z tym, że zostaje. Minęło dziewięć lat, odkąd napisałem kod dla systemu OS/400, i od tego czasu wiem, że zmieniło się od tamtego czasu, ale było to niesamowicie wyjątkowe spojrzenie na to, jak to się stało, mam nadzieję, że poświęciłem go wystarczająco dużo, aby zachować dokładność. wystarczy na prime-time. Tylko wywołanie funkcji "DLL" było prawie tak samo zabawne, ponieważ jest dosłownie zaplanowane jako zadanie systemowe. Ta platforma jest po prostu * niesamowita *. – WhozCraig

Powiązane problemy