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

En los primeros días de C ++ cuando estaba atornillado en la parte superior de C, no se podía usar NULL como se definió como (void*)0 . No se pudo asignar NULL a ningún puntero que no sea void* , lo que lo hizo inútil. En aquellos días, se aceptaba que usaba 0 (cero) para punteros nulos.

Hasta el día de hoy, he seguido utilizando cero como un puntero nulo, pero los que me rodean insisten en usar NULL . Personalmente, no veo ningún beneficio al 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 use NULL , lógicamente no puede usar p && !q – necesita comparar explícitamente contra NULL , a menos que suponga que NULL es cero, en cuyo caso por qué usar NULL ).

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

Editar: Debo agregar (y pretendía decir originalmente) que con RAII y excepciones, rara vez uso punteros nulos / NULL, pero a veces los necesitas todavía.

Aquí está la opinión de Stroustrup sobre esto: C ++ Style and Technique FAQ

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

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

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

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 intención

El uso de NULL permite búsquedas en su uso y también resalta que el desarrollador quería usar un puntero NULL , independientemente de si el comstackdor lo interpreta como NULL o no.

  1. La sobrecarga de puntero y ‘int’ es relativamente rara

El ejemplo que todo el mundo cita 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 estamos usando NULL para la constante de puntero nulo, sino que tenemos sobrecargas de ‘foo’ que toman diferentes tipos de argumentos. El parámetro debe ser también un int , ya que cualquier otro tipo generará una llamada ambigua y, por lo tanto, generará una advertencia de comstackción útil.

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

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

  1. C ++ 11 tendrá un nuevo tipo std::nullptr_t .

Este es el último argumento para la mesa. El problema de 0 y NULL se está abordando activamente para C ++ 0x, y puede garantizar que para cada implementación que proporcione NULL , lo primero que harán es:

 #define NULL nullptr 

Para aquellos que usen NULL lugar de 0 , el cambio será una mejora en la seguridad de tipo con poco o ningún esfuerzo; en todo caso, también puede detectar algunos errores donde han usado NULL para 0 . Para cualquiera que use 0 hoy … erm … bueno, espero que tengan un buen conocimiento de las expresiones regulares …

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

Dejé de usar NULL a favor de 0 hace mucho tiempo (al igual que la mayoría de las otras macros). Hice esto no solo porque quería evitar las macros tanto como fuera posible, sino también porque NULL parece haber sido sobreutilizado en los códigos C y C ++. Parece que se usa siempre que se necesita un valor de 0, no solo para los punteros.

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

 static const int nullptr = 0; 

Ahora, cuando lleguen los comstackdores compatibles con C ++ 0x, todo lo que tengo que hacer es eliminar esa línea. Una buena ventaja de esto es que Visual Studio ya reconoce nullptr como una palabra clave y la resalta de forma adecuada.

Yo siempre uso:

  • NULL para punteros
  • '\0' para caracteres
  • 0.0 para carrozas 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.

  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ías usar NULL cuando tratas con punteros.

1) Declara tu intención (no me hagas buscar todo tu código tratando de descubrir 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, sin embargo, solo pasará un entero de 32 bits. Me parece que confía en los otros 32 bits que se le han puesto a cero? He visto ciertos comstackdores (por ejemplo, el icpc de Intel) que no son tan amables, y esto ha resultado en errores de tiempo de ejecución.

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

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

Personalmente sigo usando el valor NULL para representar apuntadores nulos, esto hace explícito que estás usando un puntero en lugar de un tipo integral. Sí internamente, el valor NULL sigue siendo 0, pero no se representa 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 remedia en C ++ 11 donde uno puede simplemente usar nullptr lugar de NULL , y también nullptr_t que es el tipo de nullptr .

Yo diría que la historia ha hablado y los que argumentaron a favor de usar 0 (cero) estaban equivocados (incluido Bjarne Stroustrup). Los argumentos a favor de 0 fueron en su mayoría estética y “preferencia personal”.

Después de la creación de C ++ 11, con su nuevo tipo de nullptr, algunos comstackdores comenzaron a quejarse (con parámetros por defecto) sobre pasar 0 a funciones con argumentos de puntero, porque 0 no es un puntero.

Si el código se escribió usando NULL, se podría haber realizado una simple búsqueda y reemplazo a través de la base del código para convertirlo en nullptr. Si tiene un código escrito usando la opción 0 como puntero, es mucho más tedioso actualizarlo.

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

Usualmente uso 0. No me gustan las macros, y no hay garantía de que un encabezado de un tercero que esté utilizando no redefina NULL para que sea algo extraño.

Podría usar un objeto nullptr como propuso Scott Meyers y otros hasta que C ++ obtenga una palabra clave nullptr:

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

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), código tal como

 char *p; ... if (p) { ... } 

no funcionaría como esperabas TENÍAS que escribir

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

Aunque creo que la mayoría de los comstackdores definen NULL como 0 en estos días, aún recuerdo la lección de aquellos años atrás: NULL no necesariamente es 0.

Creo que el estándar garantiza que NULL == 0, por lo que puede hacer cualquiera de los dos. Prefiero NULL porque documenta tu intención.

Usar 0 o NULL tendrá el mismo efecto.

Sin embargo, eso no significa que ambas sean buenas prácticas de progtwigción. Dado que no hay diferencia en el rendimiento, elegir una opción de bajo nivel de conocimiento sobre una alternativa agnóstica / abstracta es una mala práctica de progtwigció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 progtwig. Deben ser usados ​​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 comstack o no.

Sé que algunas comunidades fomentan la demostración de un conocimiento profundo de un entorno al romper los contratos del medio ambiente. Los progtwigdores responsables, sin embargo, hacen que el código sea fácil de mantener y mantienen estas prácticas fuera de su código.

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

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

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

a menudo puedes escribir

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

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

Estoy con Stroustrup en este caso 🙂 Como NULL no es parte del idioma, prefiero usar 0.

Principalmente preferencia personal, aunque uno podría argumentar que NULL hace 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 se define en es probablemente lo mejor para su comstackdor.

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

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 se trata de un macro es desafortunado, pero dado que está tan ampliamente arraigado, hay poco peligro (a menos que alguien haga algo realmente desquiciado). 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 modismo arraigado.

C ++ 09 agregará el constructo nullptr que creo que hace mucho tiempo.

Siempre uso 0. No por una razón realmente pensada, solo porque cuando estaba aprendiendo C ++ por primera vez leí algo que recomendaba usar 0 y siempre lo he hecho 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 nullptr.

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 no totalmente compatible con la macro C NULL, que además del tipo oculto lo convierte implícitamente 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 desea esperar el nuevo estándar y quiere usar un nullptr, use al menos uno decente como el propuesto por Meyers (vea el comentario jon.h).

Bueno, yo argumento por no usar punteros 0 o NULL en lo posible siempre que sea posible.

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

también, conduce a declaraciones “if-not-null” en todo su código. Mucho mejor si puedes confiar siempre en un estado válido.

Casi siempre hay una mejor alternativa.

Establecer un puntero a 0 simplemente no es tan claro. Especialmente si vienes un idioma que no sea C ++. Esto incluye C así como Javascript.

Recientemente delt con un código como ese:

virtual void DrawTo(BITMAP *buffer) =0;

para la función virtual pura por primera vez. Pensé que sería una magia jiberjash por una semana. Cuando me di cuenta de que básicamente estaba configurando el puntero de la función como null (como las funciones virtuales son solo indicadores 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 ese bastiondation sin el espacio adecuado para mis nuevos ojos. En realidad, me pregunto por qué C ++ no emplea el null minúscula, al igual que emplea minúsculas falsas y verdaderas ahora.