¿Utiliza NULL o 0 (cero) para punteros en C ++?

192

En los primeros días de C ++ cuando estaba atornillado encima de C, no podía usar NULL como se definió como (void*)0. No puede asignar NULL a ningún puntero que no sea void*, lo que lo hace un poco inútil. En aquellos días, se aceptaba que usabas 0(cero) para punteros nulos.

Hasta el día de hoy, he seguido usando cero como puntero nulo, pero los que me rodean insisten en usarlo NULL. Personalmente, no veo ningún beneficio en dar un nombre ( NULL) a un valor existente, y dado que también me gusta probar los punteros como valores de verdad:

if (p && !q)
  do_something();

entonces usar cero tiene más sentido (como en el caso de que lo use NULL, no puede usarlo lógicamente p && !q; debe compararlo explícitamente NULL, a menos que suponga que NULLes cero, en cuyo caso por qué usarlo NULL).

¿Hay alguna razón objetiva para preferir cero sobre NULL (o viceversa), o todo es preferencia personal?

Editar: debería agregar (y tenía la intención de decir originalmente) que con RAII y excepciones, rara vez uso punteros cero / NULL, pero a veces todavía los necesita.

camh
fuente
9
espera, ¿no se requiere un puntero nulo para evaluar como falso independientemente de si nulo es cero internamente o no?
Mooing Duck

Respuestas:

185

Aquí está la opinión de Stroustrup sobre esto: Preguntas frecuentes sobre estilo y técnica de C ++

En C ++, la definición de NULLes 0, por lo que solo hay una diferencia estética. Prefiero evitar las macros, así que uso 0. Otro problema NULLes que las personas a veces creen erróneamente que es diferente de 0 y / o no un número entero. En código pre-estándar,NULL was / se define a veces como algo inadecuado y, por lo tanto, se tuvo que evitar. Eso es menos común en estos días.

Si tiene que nombrar el puntero nulo, llámelo nullptr; así se llama en C ++ 11. Entonces, nullptrserá una palabra clave.

Dicho eso, no te preocupes por las cosas pequeñas.

Martin Cote
fuente
77
Bjarne escribió esto antes de que C ++ 0x comenzara a trabajar en un nuevo tipo nulo. Será el caso de que se utilizará NULL para este tipo cuando esté disponible para una plataforma, y ​​creo que verá un cambio de C en el consenso general al respecto.
Richard Corden
122

Hay algunos argumentos (uno de los cuales es relativamente reciente) que creo que contradicen la posición de Bjarne sobre esto.

  1. Documentación de intenciones

El uso NULLpermite búsquedas en su uso y también destaca que el desarrollador quería usar un NULLpuntero, independientemente de si el compilador lo está interpretando NULLo no.

  1. La sobrecarga del puntero y 'int' es relativamente rara

El ejemplo que todos citan es:

void foo(int*);
void foo (int);

void bar() {
  foo (NULL);  // Calls 'foo(int)'
}

Sin embargo, al menos en mi opinión, el problema con lo anterior no es que estemos usando NULL para la constante de puntero nulo, es que tenemos sobrecargas de 'foo' que toman muy diferentes tipos de argumentos. El parámetro también debe ser un int, ya que cualquier otro tipo dará como resultado una llamada ambigua y generará una advertencia útil del compilador.

  1. ¡Las herramientas de análisis pueden ayudar HOY!

Incluso en ausencia de C ++ 0x, hay herramientas disponibles hoy que verifican que NULLse está utilizando para punteros y que 0se está utilizando para tipos integrales.

  1. C ++ 11 tendrá un nuevo std::nullptr_ttipo.

Este es el argumento más nuevo de la tabla. El problema de 0y NULLestá siendo activamente dirigida para C ++ 0x, y se puede garantizar que por cada aplicación que proporciona NULL, lo primero que van a hacer es:

#define NULL  nullptr

Para aquellos que utilizan NULLen lugar de 0, el cambio será una mejora en la seguridad de tipos con poco o ningún esfuerzo - en todo caso también puede capturar algunos errores en que han utilizado NULLpara 0. Para cualquiera que use 0hoy ... erm ... bueno, espero que tengan un buen conocimiento de las expresiones regulares ...

Richard Corden
fuente
1
Esos son algunos puntos bastante buenos, debo admitir. Me alegra que C ++ 0x tenga un tipo nulo, creo que hará que muchas cosas sean más limpias.
Rob
2
@ Richard, ¿por qué no hacer lo contrario? Puede usar Meyers nullptr_t y luego, cuando 0x esté disponible, quite el #includey manténgalo en el lado seguro por completo.
fnieto - Fernando Nieto
15
#define NULL nullptrParece peligroso Para bien o para mal, muchos códigos heredados usan NULL para otras cosas que no sean 0. Por ejemplo, los identificadores a menudo se implementan como algún tipo integral, y su configuración NULLno es infrecuente. Incluso he visto abusos como usar NULLpara establecer un charterminador cero.
Adrian McCarthy
8
@AdrianMcCarthy: Solo diría que es peligroso si existe el peligro de que el código se compile silenciosamente y tenga un significado diferente. Estoy bastante seguro de que este no es el caso, por lo que, de hecho, se detectarían todos los usos incorrectos de NULL.
Richard Corden
3
@ RichardCorden: Um, eso supone que esos otros usos NULLson realmente incorrectos. Muchas API se han utilizado durante mucho tiempo NULLcon identificadores, y ese es de hecho el uso documentado con muchos de ellos. No es pragmático romperlos repentinamente y declarar que lo están haciendo mal.
Adrian McCarthy
45

Use NULL. NULL muestra tu intención. Que es 0 es un detalle de implementación que no debería importar.

Andy Lester
fuente
28
0 no es un detalle de implementación. El estándar define 0 como cualquier patrón de bits que represente un puntero nulo.
Ferruccio
55
Como si ..!! ¡Amigo, C ++ es un lenguaje de bajo nivel! Use 0, es un idioma muy conocido.
hasen
8
Entiendo que es parte del estándar. Es un detalle de implementación en cuanto a leer el código. El lector debe pensar "puntero NULL" no "0, que en este caso significa puntero NULL, no un número con el que podría estar haciendo aritmética".
Andy Lester
2
+1. De acuerdo con Andy. @Ferruccio, El detalle de implementación de la idea del programador no es lo mismo que la implementación del compilador definida
usuario
si usa NULL, en un código simple sin encabezado complejo, encontrará el error de "NULL no está definido en este ámbito" ..
ArtificiallyIntelligence
37

Yo siempre uso:

  • NULL para punteros
  • '\0' para caracteres
  • 0.0 para flotadores y dobles

donde 0 estaría bien. Es una cuestión de intención de señalización. Dicho esto, no soy anal al respecto.

Andrew Stein
fuente
24
probablemente debería usar 0.0F para flotadores, para evitar el tipo de
letra
35

Dejé de usar NULL a favor de 0 hace mucho tiempo (así como la mayoría de las otras macros). Hice esto no solo porque quería evitar las macros tanto como sea posible, sino también porque NULL parece haberse usado en exceso en el código C y C ++. Parece que se usa siempre que se necesita un valor 0, no solo para punteros.

En proyectos nuevos, pongo esto en un encabezado de proyecto:

static const int nullptr = 0;

Ahora, cuando llegan los compiladores compatibles con C ++ 0x, todo lo que tengo que hacer es eliminar esa línea. Un buen beneficio de esto es que Visual Studio ya reconoce nullptr como una palabra clave y la resalta adecuadamente.

Ferruccio
fuente
44
Usar NULL será más portátil a largo plazo. 'nullptr' estará disponible para algunas plataformas y no para otras. Su solución aquí requiere que use el preprocesador alrededor de su declaración para asegurarse de que solo esté presente cuando sea necesario. NULL lo hará automáticamente.
Richard Corden
66
Estoy en desacuerdo. Será menos portátil a corto plazo hasta que los compiladores se pongan al día. A largo plazo, será igual de portátil y quizás un poco más legible.
Ferruccio
44
Además, siempre puede #define nullptr NULL para su compilador que no sea C ++ 0x.
Anteru
20
    cerr << sizeof(0) << endl;
    cerr << sizeof(NULL) << endl;
    cerr << sizeof(void*) << endl;

    ============
    On a 64-bit gcc RHEL platform you get:
    4
    8
    8
    ================

La moraleja de la historia. Debería usar NULL cuando se trata de punteros.

1) Declara tu intención (no me hagas buscar en todo tu código tratando de averiguar si una variable es un puntero o algún tipo numérico).

2) En ciertas llamadas API que esperan argumentos variables, usarán un puntero NULL para indicar el final de la lista de argumentos. En este caso, usar un '0' en lugar de NULL puede causar problemas. En una plataforma de 64 bits, la llamada va_arg quiere un puntero de 64 bits, pero solo pasará un entero de 32 bits. ¿Me parece que estás confiando en que los otros 32 bits se pondrán a cero para ti? He visto ciertos compiladores (por ejemplo, el icpc de Intel) que no son tan amables, y esto ha resultado en errores de tiempo de ejecución.

abonet
fuente
NULLquizás no es portátil y no es seguro. Puede haber plataformas que aún #define NULL 0(de acuerdo con las preguntas frecuentes de Stroustrup: ¿Debería usar NULL o 0? Citado por la pregunta principal y se encuentra entre los primeros resultados de búsqueda). Al menos en C ++ anterior, 0tiene un significado conceptual especial en el contexto del puntero. No debes pensar concretamente en los bits. Tenga en cuenta también que en diferentes contextos enteros ( short, int, long long) " sizeof(0)" será diferente. Creo que esta respuesta está un poco equivocada.
FooF
(En lo personal como programador C en la vida diaria, llegué a visitar esta pregunta para entender por qué la gente quiere usar NULLen lugar de (char *)0, (const char *)0o (struct Boo *)0o (void *)0o lo que sea que se establece la voluntad más claramente -. Sin ser (en mi opinión) demasiado engorroso)
Foof
Vota arriba. está sucediendo en el compilador msvc2013 C. en 64 bits, 0 cuando se convierte a un puntero no garantiza ser puntero NULO.
pengMiao
16

Si recuerdo correctamente, NULL se define de manera diferente en los encabezados que he usado. Para C se define como (void *) 0, y para C ++ se define como solo 0. El código se parecía a:

#ifndef __cplusplus
#define NULL (void*)0
#else
#define NULL 0
#endif

Personalmente, todavía uso el valor NULL para representar punteros nulos, lo que hace explícito que estás usando un puntero en lugar de algún tipo integral. Sí internamente, el valor NULL sigue siendo 0 pero no está representado como tal.

Además, no confío en la conversión automática de enteros a valores booleanos, sino que los comparo explícitamente.

Por ejemplo, prefiero usar:

if (pointer_value != NULL || integer_value == 0)

más bien que:

if (pointer_value || !integer_value)

Baste decir que todo esto se soluciona en C ++ 11, donde uno simplemente puede usar en nullptrlugar de NULL, y también nullptr_tese es el tipo de a nullptr.

Daemin
fuente
15

Diría que la historia ha hablado y aquellos que argumentaron a favor del uso de 0 (cero) estaban equivocados (incluido Bjarne Stroustrup). Los argumentos a favor de 0 fueron principalmente estética y "preferencia personal".

Después de la creación de C ++ 11, con su nuevo tipo nullptr, algunos compiladores han comenzado a quejarse (con parámetros predeterminados) acerca de pasar 0 a funciones con argumentos de puntero, porque 0 no es un puntero.

Si el código se hubiera escrito usando NULL, una simple búsqueda y reemplazo podría haberse realizado a través de la base de código para hacerlo nullptr. Si está atascado con el código escrito usando la opción de 0 como puntero, es mucho más tedioso actualizarlo.

Y si tiene que escribir un código nuevo ahora en el estándar C ++ 03 (y no puede usar nullptr), realmente debería usar NULL. Le facilitará mucho la actualización en el futuro.

Gaute Lindkvist
fuente
11

Usualmente uso 0. No me gustan las macros, y no hay garantía de que algún encabezado de terceros que estés usando no redefina NULL para que sea algo extraño.

Puede usar un objeto nullptr según lo propuesto por Scott Meyers y otros hasta que C ++ obtenga una palabra clave nullptr:

const // It is a const object...
class nullptr_t 
{
public:
    template<class T>
    operator T*() const // convertible to any type of null non-member pointer...
    { return 0; }

    template<class C, class T>
    operator T C::*() const   // or any type of null member pointer...
    { return 0; }

private:
    void operator&() const;  // Can't take address of nullptr

} nullptr = {};

Google "nullptr" para más información.

jon-hanson
fuente
9
Cualquier biblioteca de terceros que defina NULL para cualquier cosa que no sea 0 (o (void*)0si se está compilando como código C) solo está pidiendo problemas y no debe usarse.
Adam Rosenfield el
2
¿Alguna vez has visto una biblioteca que redefine NULL? ¿Nunca? Si alguna vez existiera dicha biblioteca, tendría problemas mayores que el NULL redefinido, como que está utilizando una biblioteca que es lo suficientemente tonta como para redefinir NULL.
Andy Lester
1
Hace más de una década, recuerdo vagamente haber tenido que lidiar con algunos encabezados de terceros, posiblemente Orbix u ObjectStore, que definieron NULL. Creo que tengo un odio patológico hacia las macros después de desperdiciar varios días y noches tratando de hacer que varios encabezados de terceros funcionen con windows.h.
jon-hanson
2
"no me gustan las macros" es una crítica extraña de un #define parecido a un objeto. ¿Quizás quiere decir que no le gusta el preprocesador C?
Andrew Prock
@ Andrew - El único beneficio de NULLover (type *)0es la capacidad de búsqueda, me parece a mí. De lo contrario, parece una ofuscación innecesaria si no fuera un idioma C. Personalmente, creo que el idioma de propagarse NULLpor todo el lugar merece morir. NULLEs una macro inútil en mi opinión. La navaja de Occam tiene trabajo que hacer aquí ...
FooF
11

Una vez trabajé en una máquina donde 0 era una dirección válida y NULL se definió como un valor octal especial. En esa máquina (0! = NULL), entonces código como

char *p;

...

if (p) { ... }

No funcionaría como esperabas. Tuviste que escribir

if (p != NULL) { ... }

Aunque creo que la mayoría de los compiladores definen NULL como 0 en estos días, todavía recuerdo la lección de hace años: NULL no es necesariamente 0.

mxg
fuente
26
No estabas utilizando un compilador compatible. El estándar dice que NULL es 0 y que el compilador debe convertir 0 en un contexto de puntero en un verdadero valor NULL verdadero para el arco.
Evan Teran
17
Sí, tiene usted razón. Esto fue a mediados de los 80 antes de que ANSI produjera un estándar C. No existía el cumplimiento y los escritores de compiladores eran libres de interpretar el lenguaje como mejor les pareciera. Por eso era necesario un estándar.
mxg
9

Creo que el estándar garantiza que NULL == 0, por lo que puede hacerlo. Prefiero NULL porque documenta tu intención.

Mark Ransom
fuente
Si tienes estructuras anidadas, creo que decir foo.bar_ptr = (Bar *) 0expresa la intención mucho más claramente que foo.bar_ptr = NULL. Este hábito también permite al compilador detectar errores de concepción errónea por usted. Para mí, foo.bar_ptr = 0expresa la intención además de usar NULLsi sé que foo.bar_ptres un puntero.
FooF
9

Usar 0 o NULL tendrá el mismo efecto.

Sin embargo, eso no significa que ambas sean buenas prácticas de programación. Dado que no hay diferencia en el rendimiento, elegir una opción de bajo nivel en lugar de una alternativa agnóstica / abstracta es una mala práctica de programación. Ayude a los lectores de su código a comprender su proceso de pensamiento .

NULL, 0, 0.0, '\ 0', 0x00 y whatelse se traducen en lo mismo, pero son entidades lógicas diferentes en su programa. Deben usarse como tales. NULL es un puntero, 0 es cantidad, 0x0 es un valor cuyos bits son interesantes, etc. No asignaría '\ 0' a un puntero si se compila o no.

Sé que algunas comunidades alientan a demostrar un conocimiento profundo de un entorno al romper los contratos del entorno. Los programadores responsables, sin embargo, crean códigos mantenibles y mantienen tales prácticas fuera de su código.

Chris
fuente
5

Extraño, nadie, incluido Stroustroup, lo mencionó. Al hablar mucho sobre estándares y estética, nadie notó que es peligroso usarlo 0en NULLsu lugar, por ejemplo, en una lista de argumentos variables sobre la arquitectura donde sizeof(int) != sizeof(void*). Al igual que Stroustroup, prefiero 0por razones estéticas, pero hay que tener cuidado de no usarlo donde su tipo pueda ser ambiguo.

Michael Krelin - hacker
fuente
Y en esos lugares peligrosos aún puede utilizar 0siempre y cuando se especifique lo que 0quiere decir - por ejemplo (int *)0, (char *)0, (const char *)0o (void *)0, o (unsigned long long) 0, o lo que sea. Esto en mi opinión expresa la intención mucho más clara que NULL.
FooF
1
Claro, si no sabes lo que NULLsignifica.
Michael Krelin - hacker
Personalmente, me resulta un poco desagradable emitir algo innecesariamente(void *) cuando podría usar el tipo exacto. A propósito di ejemplo de (típicamente) un entero de 64 bits en la lista porque es análogo al caso del puntero. Además, si recuerdo que C ++ anterior se definió NULLcomo 0correcto (hace años que programé en C ++), no se observa ninguna mejora en la corrección del programa. Afortunadamente, el estándar más nuevo de C ++ proporciona nullptrpalabras clave, por lo que podemos deshacernos de esta NULLfealdad y toda la controversia al escribir C ++ más nuevo.
FooF
Bueno, por eso (void*)se ha abstraído el casting NULL. Y en NULLrealidad expresa la intención con bastante claridad la mayor parte del tiempo. Y creo que tu recuerdo está mal. No estoy seguro acerca de los estándares, pero en la práctica creo que lo ha sido (void*)0. Y sí, nullptres un buen prettifier, aunque equivale a lo mismo NULL: especificar un puntero nulo sin especificar el tipo.
Michael Krelin - hacker
1
@FooF, en algunas plataformas, tal vez. En mi realidad funcionó y, por lo tanto, sospecho que se ha definido como un puntero. En cuanto a la robustez, sí, lo que estaba tratando de decir es que usar nullptrosos tiene el mismo mensaje que NULL, solo se trataba de expresar la intención que mencionaste al principio. (Preprocesamiento NULLde gccrendimientos modernos __null, sea lo que sea).
Michael Krelin - hacker
4

Intento evitar toda la pregunta utilizando referencias de C ++ siempre que sea posible. Más bien que

void foo(const Bar* pBar) { ... }

a menudo podrías escribir

void foo(const Bar& bar) { ... }

Por supuesto, esto no siempre funciona; pero los punteros nulos pueden ser utilizados en exceso.

Ðаn
fuente
3

Estoy con Stroustrup en este caso :-) Como NULL no es parte del lenguaje, prefiero usar 0.

Robar
fuente
3

Principalmente preferencia personal, aunque uno podría argumentar que NULL hace que sea bastante obvio que el objeto es un puntero que actualmente no apunta a nada, por ejemplo

void *ptr = &something;
/* lots o' code */
ptr = NULL; // more obvious that it's a pointer and not being used

IIRC, el estándar no requiere que NULL sea 0, por lo que usar lo que esté definido en <stddef.h> es probablemente el mejor para su compilador.

Otra faceta del argumento es si debe usar comparaciones lógicas (conversión implícita a bool) o verificación de explicidad contra NULL, pero eso también se reduce a la legibilidad.

Palanqueta
fuente
3

Prefiero usar NULL, ya que deja en claro que su intención es que el valor represente un puntero, no un valor aritmético. El hecho de que sea una macro es desafortunado, pero como está muy arraigado, hay poco peligro (a menos que alguien haga algo realmente descabellado). Desearía que fuera una palabra clave desde el principio, pero ¿qué puedes hacer?

Dicho esto, no tengo ningún problema con el uso de punteros como valores de verdad en sí mismos. Al igual que con NULL, es un idioma arraigado.

C ++ 09 agregará la construcción nullptr que creo que está muy atrasada.

Michael Burr
fuente
1

Siempre uso 0. No por ninguna razón real, solo porque cuando estaba aprendiendo C ++ por primera vez, leí algo que me recomendó usar 0 y siempre lo hice de esa manera. En teoría, podría haber un problema de confusión en la legibilidad, pero en la práctica nunca me he encontrado con un problema de este tipo en miles de horas hombre y millones de líneas de código. Como dice Stroustrup, en realidad es solo un problema estético personal hasta que el estándar se convierte en nulo.

Gerald
fuente
1

Alguien me dijo una vez ... Voy a redefinir NULL a 69. Desde entonces no lo uso: P

Hace que tu código sea bastante vulnerable.

Editar:

No todo en el estándar es perfecto. La macro NULL es una constante de puntero nulo C ++ definida por la implementación que no es totalmente compatible con la macro C NULL, lo que además del tipo de ocultación implícita la convierte en una herramienta inútil y propensa a errores.

NULL no se comporta como un puntero nulo sino como un literal O / OL.

Dime el siguiente ejemplo no es confuso:

void foo(char *); 
void foo(int); 
foo(NULL); // calls int version instead of pointer version! 

Es por todo eso, en el nuevo estándar aparece std :: nullptr_t

Si no quiere esperar al nuevo estándar y quiere usar un nullptr, use al menos uno decente como el propuesto por Meyers (vea el comentario de jon.h).

fnieto - Fernando Nieto
fuente
55
NULLes una parte bien definida del estándar C ++. Permitir que las personas a las que les gusta redefinir el código de edición de macros estándar en su proyecto haga que su código sea 'vulnerable'; usando NULLno lo hace.
CB Bailey
1

Bueno, defiendo no utilizar punteros 0 o NULL siempre que sea posible.

Usarlos tarde o temprano conducirá a fallas de segmentación en su código. En mi experiencia esto, y los punteros en gereral es una de las mayores fuentes de errores en C ++

Además, conduce a declaraciones "if-not-null" en todo el código. Mucho mejor si puede confiar siempre en un estado válido.

Casi siempre hay una mejor alternativa.

Jan P
fuente
2
Un fallo de segmentación garantizada (y que se garantiza el funcionamiento en los sistemas modernos cuando dereference 0) es útil para la depuración. Mucho mejor que desreferenciar basura aleatoria y saber quién sabe qué resultado.
Carreras de ligereza en órbita
-4

Establecer un puntero a 0 simplemente no está tan claro. Especialmente si vienes un lenguaje que no sea C ++. Esto incluye C y Javascript.

Recientemente delté con un código como este:

virtual void DrawTo(BITMAP *buffer) =0;

para la función virtual pura por primera vez. Pensé que sería un jiberjash mágico durante una semana. Cuando me di cuenta de que básicamente estaba configurando el puntero de función en a null(ya que las funciones virtuales son solo punteros de función en la mayoría de los casos para C ++) me di una patada.

virtual void DrawTo(BITMAP *buffer) =null;

Hubiera sido menos confuso que esa basterdación sin un espacio adecuado para mis nuevos ojos. En realidad, me pregunto por qué C ++ no emplea minúsculas de la nullmisma manera que emplea minúsculas falso y verdadero ahora.

Pizzach
fuente
En general, prefiero NULl a 0 para punteros. Sin embargo '= 0;' es la forma idiomática de declarar una función virtual pura en C ++. Le recomiendo encarecidamente que no use '= NULL;' para este caso particular
danio
Este es el comentario más divertido en StackOverflow. Probablemente ya sepa que el ejemplo que dio es una sintaxis para la función virtual pura y no un puntero. Y sí, @danio tiene razón, no debe usar NULL para la función virtual pura.
sgowd