2012-03-27 7 views
47

Stavo intervistando un ragazzo per una posizione di ingegneria del software di livello medio ieri, e ha detto che in C, NULL non è sempre zero e che ha visto implementazioni di C in cui NULL non è zero. Trovo questo altamente sospetto, ma voglio essere sicuro. Qualcuno sa se ha ragione?NULL è sempre zero in C?

(Le risposte non influenzeranno il mio giudizio su questo candidato, ho già presentato la mia decisione di mio manager.)

+0

http://bytes.com/topic/c/answers/213647-null-c – soandos

+0

correlati http://stackoverflow.com/questions/459743/is-null-always-false – mcabral

+1

http: // c- faq.com/null/index.html –

risposta

44

Sto assumendo si intende il puntatore nullo. È garantito un confronto uguale a 0. Ma non deve essere rappresentato con bit tutto-zero.

Vedere anche lo comp.lang.c FAQ sui puntatori nulli.


  1. See C99, 6.3.2.3.
  2. Nessun reclamo esplicito; ma vedi la nota in calce per C99, 7.20.3 (grazie a @birryree nei commenti).
+5

Come addendum al riferimento C99, dato che 'NULL' è sempre uguale a' 0' (costante puntatore nullo), la sezione 7.20.3.2 rileva anche che tutti i bit impostati su zero (come nel caso di 'calloc') non è necessariamente uguale alla rappresentazione di '0.0f' o intero' 0'. – birryree

+0

Fantastico. Quindi è pratica abbastanza comune in questi giorni, ma non è un requisito. Grazie. – chi42

+0

Quindi se dovessi lanciare un puntatore NULL su un int e quindi stamparlo, non stamperei '0'? Significa che il compilatore deve ricordare quando sta facendo un confronto tra puntatori in modo che possa rendere un puntatore NULL confronta uguale a zero? – chi42

10

§ 6.3.2.3 dello standard C99 dice

Un costante espressione intera con il valore 0, o una tale espressione cast digitare void *, viene chiamato un puntatore nullo costante) Se una costante puntatore nullo viene convertita in un tipo di puntatore , il puntatore risultante, chiamato puntatore nullo, è garantito per confrontare il diverso con un puntatore a qualsiasi oggetto o funzione.

§ 7,17 dice anche

[...] NULL che si espande ad un puntatore nullo costante implementazione definita [...]

L'indirizzo del puntatore NULL potrebbe essere diverso da 0, mentre si comporterà come nella maggior parte dei casi.

(Questo dovrebbe essere lo stesso come in standard precedenti C, che non ho a portata di mano al momento)

10

puntatore nullo costante è sempre 0. La NULL macro può essere definita dall'implementazione come 0 nuda, o un'espressione cast come (void *) 0, o qualche altra espressione intera con valore zero (da cui la lingua "implementazione definita" nello standard).

Il puntatore nullo valore può essere diverso da 0. Quando viene rilevata una costante di puntatore nullo, verrà convertita nel valore del puntatore nullo appropriato.

+1

Colorami stupido, ma temo di aver bisogno di più educazione ora. Quando si ha un puntatore 'ptr', non' ptr == NULL' _exactly_ uguale a 'ptr == 0' o'! Ptr'? Questa implementazione dipende? –

+3

@MrLister: Nel contesto del tuo * codice sorgente *, sei assolutamente corretto - 'ptr == 0' e' ptr == NULL' e '! Ptr' sono tutti equivalenti.Una volta che il codice sorgente è stato tradotto in codice macchina, tuttavia, il valore attuale del puntatore nullo potrebbe essere diverso da 0 (e tutti i confronti saranno contro il valore effettivo del puntatore nullo). –

+0

'Il valore del puntatore nullo può essere diverso da 0' - intendi' * ptr == qualcosa' anche se 'void * ptr = 0'? –

0

Ha ragione, su alcune implementazioni, la dimensione del puntatore non è la stessa della dimensione del numero intero. NULL nel contesto intero è 0, ma il layout binario effettivo non deve essere tutti 0.

7

In C, c'è un solo contesto, in cui è necessario eseguire il cast esplicito di un puntatore nullo a un tipo di puntatore specifico affinché il programma funzioni correttamente. Quel contesto sta passando un puntatore nullo attraverso una lista di argomenti della funzione non tipizzata. In moderno C, questo accade solo quando è necessario passare un puntatore nullo a una funzione che accetta un numero variabile di argomenti. (In legacy C, accade con qualsiasi funzione non dichiarata con un prototipo.) L'esempio paradigmatico è execl, dove l'ultimo argomento deve essere un puntatore nullo in modo esplicito cast (char *):

execl("/bin/ls", "ls", "-l", (char *)0); // correct 
execl("/bin/ls", "ls", "-l", (char *)NULL); // correct, but unnecessarily verbose 

execl("/bin/ls", "ls", "-l", 0);   // undefined behavior 
execl("/bin/ls", "ls", "-l", NULL);   // ALSO undefined behavior 

Sì, questo ultimo esempio ha undefined comportamento anche seNULL è definito come ((void *)0), perché void * e char * sono non implicitamente interconvertibili quando attraversarono una lista di argomenti non tipizzato, anche se sono ovunque.

"Sotto il cofano", il problema qui è non solo con lo schema di bit utilizzato per un puntatore nullo, ma che il compilatore potrebbe aver bisogno di conoscere il tipo concreto esatto di ciascun argomento per impostare una chiamata inquadrare correttamente. (Si consideri l'MC68000, con il suo indirizzo separato e registri dati, alcuni argomenti puntatori ABI da passare nei registri degli indirizzi ma argomenti interi nei registri dati. Considera anche qualsiasi ABI dove int e void * non hanno le stesse dimensioni. Ed è orribilmente raro oggigiorno , ma C prevede comunque esplicitamente che void * e char * non siano della stessa dimensione). Se esiste un prototipo di funzione, il compilatore può utilizzarlo, ma le funzioni non convertite e gli argomenti variadici non offrono tale assistenza.

C++ è più complicato e non mi sento qualificato per spiegare come.