¿Puedo usar el puntero NULL
como reemplazo del valor de 0
?
¿O hay algo de malo en eso?
Como por ejemplo:
int i = NULL;
como reemplazo de:
int i = 0;
Como experimento compilé el siguiente código:
#include <stdio.h> int main(void) { int i = NULL; printf("%d",i); return 0; }
Producción:
0
De hecho, me da esta advertencia, que es completamente correcta por sí sola:
warning: initialization makes integer from pointer without a cast [-Wint-conversion]
pero el resultado sigue siendo equivalente.
NULL
de esta manera?NULL
como valor numérico en expresiones aritméticas? He leído las respuestas de ¿Cuál es la diferencia entre NULL, '\0' y 0 sobre cuál es la diferencia entre NULL
, \0
y 0
, pero no obtuve la información concisa de allí, si es bastante permisible y también derecho a usar NULL
como valor para operar en asignaciones y otras operaciones aritméticas.
Descargo de responsabilidad: no sé C ++. Mi respuesta no debe aplicarse en el contexto de C++
'\0'
es un int
con valor cero, solo 100% exactamente como 0
.
for (int k = 10; k > '\0'; k--) /* void */; for (int k = 10; k > 0; k--) /* void */;
En el contexto de punteros , 0
y NULL
son 100% equivalentes:
if (ptr) /* ... */; if (ptr != NULL) /* ... */; if (ptr != '\0') /* ... */; if (ptr != 0) /* ... */;
son todos 100% equivalentes.
Nota sobre ptr + NULL
El contexto de ptr + NULL
no es el de punteros. No existe una definición para la adición de punteros en el lenguaje C; se pueden sumar (o restar) punteros y números enteros. En ptr + NULL
si ptr
o NULL
es un puntero, el otro debe ser un número entero, por lo que ptr + NULL
es efectivamente (int)ptr + NULL
o ptr + (int)NULL
y dependiendo de las definiciones de ptr
y NULL
varios comportamientos se puede esperar: todo funcionando, advertencia de conversión entre puntero y entero, falla al compilar, ...
NULL
es una constante de puntero nulo. En C, podría ser una expresión constante de entero con valor 0
o una expresión de este tipo convertida en void*
, siendo lo último más probable. Lo que significa que no puede asumir que usará NULL
indistintamente con cero. Por ejemplo, en este ejemplo de código
char const* foo = "bar"; foo + 0;
No se garantiza que reemplazar 0
con NULL
sea un programa C válido, porque la suma entre dos punteros (y mucho menos de diferentes tipos de punteros) no está definida. Hará que se emita un diagnóstico debido a una violación de restricción. Los operandos para la suma no serán válidos .
En cuanto a C++, las cosas son algo diferentes. La falta de una conversión implícita de void*
a otros tipos de objetos significó que NULL
se definió históricamente como 0
en el código C++. En C ++ 03, probablemente podría salirse con la suya. Pero desde C++11 se puede definir legalmente como la palabra clave nullptr
. Ahora nuevamente se produce un error, ya que std::nullptr_t
no se puede agregar a los tipos de puntero.
Si NULL
se define como nullptr
, incluso su experimento se vuelve inválido. No hay conversión de std::nullptr_t
a un número entero. Es por eso que se considera una constante de puntero nulo más segura.
¿Puedo usar el puntero NULL como reemplazo del valor 0?
No , no es seguro hacerlo. NULL
es una constante de puntero nulo, que podría tener el tipo int
, pero que normalmente tiene el tipo void *
(en C) o, de lo contrario, no se puede asignar directamente a un int
(en C++ >= 11). Ambos lenguajes permiten que los punteros se conviertan en números enteros, pero no permiten que dichas conversiones se realicen implícitamente (aunque algunos compiladores lo proporcionan como una extensión). Además, aunque es común convertir un puntero nulo en un número entero para obtener el valor 0, el estándar no lo garantiza. Si desea una constante con tipo int
y valor 0, deletréelo 0
.
- ¿Puedo cruzarme con un Comportamiento indefinido con esto?
Sí, en cualquier implementación en la que NULL
se expanda a un valor de tipo void *
o cualquier otro que no se pueda asignar directamente a int
. El estándar no define el comportamiento de su asignación en dicha implementación, ergo, su comportamiento no está definido.
- ¿Está permitido operar con NULL de esa manera?
Tiene un estilo pobre y se romperá en algunos sistemas y bajo algunas circunstancias. Dado que parece estar usando GCC, se rompería en su propio ejemplo si compilara con la opción -Werror
.
- ¿Hay algo de malo en usar NULL como valor numérico en expresiones aritméticas?
Si. No se garantiza que tenga un valor numérico en absoluto. Si quiere decir 0, escriba 0, que no solo está bien definido, sino que es más breve y claro.
- ¿Y cómo es el resultado en C++ para ese caso?
El lenguaje C++ es más estricto con respecto a las conversiones que C y tiene reglas diferentes para NULL
, pero allí también las implementaciones pueden proporcionar extensiones. Nuevamente, si quiere decir 0, entonces eso es lo que debe escribir.
De las preguntas frecuentes de C:
P: Si NULL y 0 son equivalentes como constantes de puntero nulo, ¿cuál debo usar?
R: Solo en contextos de puntero
NULL
y0
son equivalentes.NULL
no debe usarse cuando se requiere otro tipo de 0, aunque podría funcionar, porque hacerlo envía un mensaje de estilo incorrecto. (Además, ANSI permite que la definición de NULL sea((void *)0)
, lo que no funcionará en contextos que no sean de puntero). En particular, no useNULL
cuando se desee el carácter nulo ASCII (NUL). Proporcione su propia definición
¿Puedo usar el puntero NULL como reemplazo del valor 0?
int i = NULL;
Las reglas varían entre los idiomas y sus versiones. En algunos casos se puede y en otros no. De todos modos, no deberías . Si tiene suerte, su compilador le advertirá cuando lo intente o, mejor aún, no pueda compilar.
En C++, antes de C++11 (cita de C++03):
[lib.soporte.tipos]
NULL es una constante de puntero nulo de C++ definida por la implementación en esta Norma Internacional.
Tiene poco sentido usar una constante de puntero nulo como un número entero. Sin embargo...
[conv.ptr]
Una constante de puntero nulo es una expresión constante integral (5.19) rvalue de tipo entero que se evalúa como cero.
Entonces, técnicamente funcionaría incluso si no tiene sentido. Debido a este tecnicismo, es posible que encuentre programas mal escritos que abusen de NULL
.
Desde C++ 11 (cita del último borrador):
[conv.ptr]
Una constante de puntero nulo es un literal entero ([lex.icon]) con valor cero o un prvalue de tipo std::nullptr_t .
Un std::nullptr_t
no se puede convertir a un número entero, por lo que usar NULL
como número entero solo funcionaría de forma condicional, dependiendo de las elecciones realizadas por la implementación del lenguaje.
PS nullptr
es un prvalue de tipo std::nullptr_t
. A menos que necesite que su programa se compile en una versión anterior a C++ 11, siempre debe usar nullptr
en lugar de NULL
.
C es un poco diferente (citas del borrador C11 N1548):
6.3.2.3 Idioma / Conversiones / Otros operandos / Punteros
3 Una expresión constante de entero con el valor 0, o una expresión de este tipo convertida al tipo
void *
, se denomina constante de puntero nulo. ...
Entonces, el caso es similar a la publicación C++ 11, es decir, el abuso de NULL
funciona condicionalmente según las elecciones realizadas por la implementación del lenguaje.
Sí , aunque dependiendo de la implementación, es posible que necesite un molde. Pero sí, es 100% legítimo, por lo demás.
Aunque es muy, muy, muy mal estilo (¿no hace falta decirlo?).
NULL
no es, o era, en realidad C++, es C. Sin embargo, el estándar, como para muchos legados de C, tiene dos cláusulas ([diff.null] y [support.types.nullptr]) que técnicamente hacen NULL
C++. Es una constante de puntero nulo definida por la implementación . Por lo tanto, incluso si tiene un mal estilo, técnicamente es tan C++ como puede ser.
Como se señaló en la nota al pie, las posibles implementaciones podrían ser 0
o 0L
, pero no (void*)0
.
NULL
podría, por supuesto (el estándar no lo dice explícitamente, pero es prácticamente la única opción que queda después de 0
o 0L
) ser nullptr
. Ese casi nunca es el caso, pero es una posibilidad legal.
La advertencia que le mostró el compilador demuestra que, de hecho, el compilador no cumple (a menos que haya compilado en modo C). Porque, bueno, según la advertencia, convirtió un puntero nulo (no nullptr
que sería de nullptr_t
, que sería distinto), por lo que aparentemente la definición de NULL
es (void*)0
, que puede no serlo.
De cualquier manera, tiene dos posibles casos legítimos (es decir, el compilador no está roto). Cualquiera de los dos (el caso realista), NULL
es algo así como 0
o 0L
, entonces tiene conversiones de "cero o uno" a enteros, y está listo para comenzar.
O NULL
es de hecho nullptr
. En ese caso, tiene un valor distinto que tiene garantías sobre la comparación, así como conversiones claramente definidas de números enteros, pero desafortunadamente no a números enteros. Sin embargo, tiene una conversión claramente definida a bool
(que da como resultado false
), y bool
tiene una conversión claramente definida a entero (que da como resultado 0
).
Desafortunadamente, son dos conversiones, por lo que no está dentro de "cero o uno" como se indica en [conv]. Por lo tanto, si su implementación define NULL
como nullptr
, deberá agregar una conversión explícita para que su código sea correcto.
No, ya no se prefiere usar NULL
(antigua forma de inicialización del puntero).
Desde C++11:
La palabra clave nullptr
denota el puntero literal. Es un prvalue de tipo std::nullptr_t. Existen conversiones implícitas de nullptr
a valor de puntero nulo de cualquier tipo de puntero y cualquier puntero a tipo de miembro. Existen conversiones similares para cualquier constante de puntero nulo, que incluye valores de tipo std::nullptr_t
, así como la macro NULL
.
https://en.cppreference.com/w/cpp/language/nullptr
En realidad, std::nullptr_t es el tipo del literal de puntero nulo, nullptr
. Es un tipo diferenciado que no es en sí mismo un tipo de puntero o un puntero a un tipo de miembro.
#include <cstddef> #include <iostream> void f(int* pi) { std::cout << "Pointer to integer overload\n"; } void f(double* pd) { std::cout << "Pointer to double overload\n"; } void f(std::nullptr_t nullp) { std::cout << "null pointer overload\n"; } int main() { int* pi; double* pd; f(pi); f(pd); f(nullptr); // would be ambiguous without void f(nullptr_t) // f(0); // ambiguous call: all three functions are candidates // f(NULL); // ambiguous if NULL is an integral null pointer constant // (as is the case in most implementations) }
Producción:
Pointer to integer overload Pointer to double overload null pointer overload