2012-03-27 22 views
47

Estaba entrevistando ayer a un tipo para un puesto de ingeniería de software de nivel medio, y mencionó que en C, NULL no siempre es cero y que había visto implementaciones de C donde NULL no es cero. Encuentro esto altamente sospechoso, pero quiero estar seguro. Alguien sabe si él tiene razón?¿NULL siempre es cero en C?

(Las respuestas no afectarán mi opinión sobre este candidato, ya he presentado mi decisión de mi manager.)

+0

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

+0

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

+1

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

Respuesta

44

Asumo que quiere decir el puntero nulo. Está garantizado para comparar igual a 0. Pero no tiene que ser representado con bits a cero.

Véase también comp.lang.c FAQ en punteros nulos.


  1. Ver C99, 6.3.2.3.
  2. No hay ningún reclamo explícito; pero vea la nota al pie para C99, 7.20.3 (gracias a @birryree en los comentarios).
+5

Como un apéndice a su referencia C99, como 'NULL' siempre compara igual a' 0' (constante de puntero nulo), la sección 7.20.3.2 también observa que todos los bits configurados a cero (como el caso de 'calloc') no son necesariamente lo mismo que la representación de '0.0f' o entero' 0'. – birryree

+0

Impresionante. Por lo tanto, es una práctica bastante común en estos días, pero no es un requisito. Gracias. – chi42

+0

Entonces, si tuviera que convertir un puntero NULL en un int, y luego imprimirlo, ¿no imprimiría '0'? ¿Significa eso que el compilador tiene que recordar cuándo está haciendo una comparación de puntero para que pueda comparar un puntero NULL igual a cero? – chi42

10

§ 6.3.2.3 de la norma C99 dice

Una expresión constante entera con el valor 0, o una expresión tal fundido a escribir void *, que se llama una constante puntero nulo) Si una constante de puntero nulo se convierte en un tipo de puntero , se garantiza que el puntero resultante, llamado puntero nulo, compare el desigual con un puntero a cualquier objeto o función.

§ 7.17 también dice

[...] NULL que se expande a una constante de puntero nulo definido por la implementación [...]

La dirección del puntero NULL podría ser diferente de 0, mientras que se comportará como en la mayoría de los casos.

(Esta debe ser la misma que en las normas más antiguas de C, que no tengo a mano en este momento)

10

el puntero nulo constante es siempre 0. La macro NULL puede ser definido por la implementación como 0 desnudo, o una expresión moldeada como (void *) 0, u otra expresión entera de valor cero (de ahí el lenguaje "implementación definida" en el estándar).

El puntero nulo valor puede ser algo distinto de 0. Cuando se encuentra una constante de puntero nulo, se convertirá en el valor de puntero nulo apropiado.

+1

Coloréame estúpido, pero me temo que necesito más educación ahora. Cuando tienes un puntero 'ptr', ¿no es' ptr == NULL' _exactly_ lo mismo que 'ptr == 0' o'! Ptr'? ¿Es esta implementación dependiente? –

+3

@MrLister: en el contexto de su * código fuente *, tiene toda la razón: 'ptr == 0' y' ptr == NULL' y '! Ptr' son todos equivalentes.Sin embargo, una vez que el código fuente ha sido traducido a código máquina, el valor real del puntero nulo puede ser distinto de 0 (y todas esas comparaciones estarán en contra del valor real del puntero nulo). –

+0

'El valor del puntero nulo puede ser distinto de 0' - ¿quiere decir' * ptr == algo' incluso si 'void * ptr = 0'? –

0

Tiene razón, en algunas implementaciones, el tamaño del puntero no es el mismo que el del entero. NULL en el contexto entero es 0, pero el diseño binario real no tiene que ser todo 0s.

7

En C, hay un contexto, y solo uno, donde es necesario convertir explícitamente una constante de puntero nulo en un tipo de puntero específico para que el programa funcione correctamente. Ese contexto pasa un puntero nulo a través de una lista de argumentos de la función sin tipo. En moderno C, esto solo ocurre cuando necesita pasar un puntero nulo a una función que toma una cantidad variable de argumentos. (En el legado C, sucede con cualquier función no declarada con un prototipo.) El ejemplo paradigmático es execl, donde el último argumento debe ser un puntero nulo convertir explícitamente a (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í, ese último ejemplo sin definir el comportamiento ha incluso siNULL se define como ((void *)0), porque void * y char * son no implícitamente interconvertibles cuando pasan a través de una lista de argumentos sin tipo, a pesar de que están en todas partes.

"Bajo el capó", el problema aquí es no acaba con el patrón de bits utilizado para un puntero nulo, pero que el compilador puede necesitar saber el tipo concreto exacta de cada argumento con el fin de establecer una llamada marco correctamente. (Considere el MC68000, con su dirección separada y registros de datos; algunos ABI especifican argumentos de puntero para pasar en los registros de direcciones pero en los registros enteros en los registros de datos. Considere también cualquier ABI donde int y void * no son del mismo tamaño. , pero C todavía proporciona explícitamente que void * y char * no tengan el mismo tamaño). Si hay un prototipo de función, el compilador puede usarlo, pero las funciones no diseñadas y los argumentos variados no ofrecen tal asistencia.

C++ es más complicado y no me siento capacitado para explicar cómo.

Cuestiones relacionadas