¿Puedo usar NULL como sustitución del valor de 0?

73

¿Se me permite usar el NULLpuntero como reemplazo del valor de 0?

¿O hay algo malo en hacer 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;
}

Salida:

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.


  • ¿Estoy cruzando con "Comportamiento indefinido" con esto?
  • ¿Está permitido utilizar NULLde esta manera?
  • ¿Hay algo de malo en usar NULLcomo valor numérico en expresiones aritméticas?
  • ¿Y cuál es el resultado y el comportamiento en C ++ para este caso?

He leído las respuestas de ¿Cuál es la diferencia entre NULL, '\ 0' y 0 sobre cuál es la diferencia entre NULL, \0y 0es, pero no obtuve la información concisa de allí, si es bastante permisible y también correcto usarlo NULLcomo valor para operar en asignaciones y otras operaciones aritméticas.

RobertS apoya a Monica Cellio
fuente
Los comentarios no son para discusión extendida; Esta conversación se ha movido al chat .
Samuel Liew
Realmente sería mejor hacer dos preguntas separadas, una para C y otra para C ++.
Konrad Rudolph

Respuestas:

82

¿Puedo usar el puntero NULL como reemplazo para el valor de 0?

No , no es seguro hacerlo. NULLes una constante de puntero nulo, que podría tener tipo int, pero que normalmente tiene 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 enteros, pero no prevén que tales conversiones se realicen implícitamente (aunque algunos compiladores lo ofrecen como una extensión). Además, aunque es común convertir un puntero nulo en un entero para obtener el valor 0, el estándar no garantiza eso. Si desea una constante con tipo inty valor 0, deletreela 0.

  • ¿Podría estar cruzando hacia un Comportamiento Indefinido con esto?

Sí, en cualquier implementación donde se NULLexpande a un valor con tipo void *o cualquier otro que no sea directamente asignable int. El estándar no define el comportamiento de su asignación en dicha implementación, por lo tanto, su comportamiento no está definido.

  • ¿Está permitido operar con el NULL de esa manera?

Es un estilo pobre, y se romperá en algunos sistemas y en algunas circunstancias. En la medida en que parezca que está utilizando GCC, se rompería en su propio ejemplo si compilara la -Werroropción.

  • ¿Hay algo 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 corto 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 también, las implementaciones pueden proporcionar extensiones. De nuevo, si te refieres a 0, entonces eso es lo que debes escribir.

John Bollinger
fuente
44
Debe señalar que "lo que más típicamente tiene tipo void *" solo es cierto para C. void *no es un tipo legal para C ++ (porque no puede asignar void*a ningún otro tipo de puntero). En C ++ 89 y C ++ 03, de hecho NULL debe ser de tipo int, pero en versiones posteriores puede ser (y generalmente lo es) nullptr_t.
Martin Bonner apoya a Monica el
También está equivocado, la conversión void*a intes un comportamiento indefinido. No lo es; Es el comportamiento de implementación especificado.
Martin Bonner apoya a Monica el
@MartinBonnersupportsMonica, en aquellos contextos en los que C especifica que un puntero se convierte en un entero, el resultado de la conversión se especifica en la implementación, pero eso no es de lo que estoy hablando. Es la asignación de un puntero a un valor de tipo entero (sin convertir explícitamente a través de una conversión) que tiene un comportamiento indefinido. El idioma no define una conversión automática allí.
John Bollinger el
@MartinBonnersupportsMonica, he editado para incluir más las consideraciones de C ++. En cualquier caso, el tema central se aplica por igual a ambos idiomas: si desea un número entero 0, escríbalo explícitamente como una constante entera de tipo apropiado.
John Bollinger
31

NULLes un puntero nulo constante. En C podría ser una expresión constante entera con valor 0o una expresión de ese tipo convertida en void*, con la última más probable. Lo que significa que no puede asumir que se usa NULLindistintamente con cero. Por ejemplo, en este ejemplo de código

char const* foo = "bar"; 
foo + 0;

No se garantiza que reemplazar 0con NULLun programa C válido, ya que no se define la suma entre dos punteros (y mucho menos de diferentes tipos de puntero). Hará que se emita un diagnóstico debido a una violación de restricción. Los operandos para sumar 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 significa que NULLse definió históricamente como 0en el código C ++. En C ++ 03, probablemente podría salirse con la suya. Pero desde C ++ 11 se puede definirnullptr legalmente como la palabra clave . Ahora nuevamente produce un error, ya std::nullptr_tque no se puede agregar a los tipos de puntero.

Si NULLse define como nullptrentonces, incluso su experimento se vuelve inválido. No hay conversión de std::nullptr_ta entero. Es por eso que se considera una constante de puntero nulo más segura.

StoryTeller - Unslander Monica
fuente
Para completar, 0L también es una constante de puntero nulo y podría usarse como NULLen ambos idiomas.
eerorika
1
@jamesqf El estándar dice que la constante entera con valor 0 es una constante de puntero nulo. Por lo tanto, 0L es una constante de puntero nulo.
eerorika
1
@eerorika: Justo lo que el mundo necesita, estándares que ignoran la realidad :-) Porque si recuerdo mi ensamblaje 80286 correctamente, ni siquiera puedes asignar un puntero lejano como una sola operación, por lo que los escritores del compilador tendrían que especial -en caso
jamesqf
2
@jamesqf Según las preguntas frecuentes de C , re: haciendo 0un puntero nulo constante: "evidentemente como un soplo a todo el código C mal escrito existente que hizo suposiciones incorrectas"
Andrew Henle
3
@jamesqf, que cualquier constante entera con valor 0 es una constante de puntero nulo (en C) no tiene nada que ver con las implementaciones de puntero de hardware. Tenga en cuenta también que el estándar C no reconoce una distinción entre punteros cercanos y lejanos en cualquier caso, pero admite asignaciones de puntero a puntero. También admite (algunas) comparaciones de punteros, que presentan problemas interesantes para formatos de direccionamiento segmentados como los 286.
John Bollinger el
21

¿Se me permite usar el puntero NULL como reemplazo del valor de 0?

int i = NULL;

Las reglas varían entre idiomas y sus versiones. En algunos casos pueden los y en otros, no se puede. De todos modos, no deberías . Si tienes suerte, tu compilador te avisará cuando lo intentes o incluso mejor, no compiles.

En C ++, antes de C ++ 11 (cita de C ++ 03):

[lib.support.types]

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 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 a cero.

Entonces, técnicamente funcionaría incluso si no tiene sentido. Debido a este tecnicismo, puede encontrar programas mal escritos que abusanNULL .

Desde C ++ 11 (cita del último borrador):

[conv.ptr]

Una constante de puntero nulo es un literal entero ([lex.icon]) con el valor cero o un prvalue de tipo std :: nullptr_t .

A std​::​nullptr_­tno es convertible a un entero, por lo que usarlo NULLcomo un entero funcionaría solo condicionalmente, dependiendo de las elecciones realizadas por la implementación del lenguaje.

PS nullptres un valor de tipo std​::​nullptr_­t. A menos que necesite que su programa se compile en versiones anteriores a C ++ 11, siempre debe usarlo en nullptrlugar 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 entera con el valor 0, o una expresión de este tipo convertida a tipovoid * , se denomina constante de puntero nulo. ...

Entonces, el caso es similar a publicar C ++ 11, es decir, el abuso de las NULLobras condicionalmente dependiendo de las elecciones realizadas por la implementación del lenguaje.

eerorika
fuente
10

, aunque dependiendo de la implementación, es posible que necesite un reparto. Pero sí, es 100% legítimo, de lo contrario.

Aunque es muy, muy, muy mal estilo (¿no hace falta decirlo?).

NULLes, o era, en realidad no C ++, es C. Sin embargo, el estándar tiene , como para muchos legados en C, dos cláusulas ([diff.null] y [support.types.nullptr]) que técnicamente hacen NULLC ++. Es una constante de puntero nulo definida por la implementación . Por lo tanto, incluso si es un mal estilo, técnicamente es tan C ++ como puede ser.
Como se señala en la nota al pie , las posibles implementaciones podrían ser 0o 0L, pero no (void*)0 .

NULLpodría, por supuesto (el estándar no lo dice explícitamente, pero es prácticamente la única opción que queda después 0o 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, no es compatible (a menos que haya compilado en modo C). Porque, bueno, de acuerdo con la advertencia, convirtió un puntero nulo (no nullptrcuál sería de nullptr_t, lo que sería distinto), por lo que aparentemente la definición de sí NULLes (void*)0, lo que puede no ser.

De cualquier manera, tiene dos posibles casos legítimos (es decir, el compilador no está roto). O (el caso realista), NULLes algo como 0o 0L, entonces tienes "cero o uno" conversiones al número entero, y que son buenos para ir.

O NULLes de hecho nullptr. En ese caso, tiene un valor distinto que tiene garantías sobre la comparación, así como conversiones claramente definidas de enteros, pero desafortunadamente no a enteros. Sin embargo, tiene una conversión claramente definida a bool(que resulta en false), y booltiene una conversión claramente definida a entero (que resulta en0 ).

Desafortunadamente, son dos conversiones, por lo que no está dentro de "cero o uno" como se señala en [conv]. Por lo tanto, si su implementación se define NULLcomo nullptr, entonces tendrá que agregar una conversión explícita para que su código sea correcto.

Damon
fuente
6

De las preguntas frecuentes de C:

P: Si NULL y 0 son equivalentes como constantes de puntero nulo, ¿qué debo usar?

R: Es solo en contextos de puntero que NULLy 0son equivalentes. NULLdebe no ser utilizado cuando se requiere otro tipo de 0, a pesar de que podría funcionar, ya que al hacerlo envía el mensaje equivocado estilística. (Además, ANSI permite que la definición de NULL sea ((void *)0), que no funcionará en absoluto en contextos sin puntero). En particular, no utiliceNULL cuando se desea el carácter nulo ASCII (NUL). Proporcione su propia definición

http://c-faq.com/null/nullor0.html

phuclv
fuente
5

Descargo de responsabilidad: no sé C ++. Mi respuesta no está destinada a aplicarse en el contexto de C ++

'\0'es un intvalor cero, solo 100% exactamente igual 0.

for (int k = 10; k > '\0'; k--) /* void */;
for (int k = 10; k > 0; k--) /* void */;

En el contexto de los punteros , 0y NULLson 100% equivalentes:

if (ptr) /* ... */;
if (ptr != NULL) /* ... */;
if (ptr != '\0') /* ... */;
if (ptr != 0) /* ... */;

son todos 100% equivalentes.


Nota sobre ptr + NULL

El contexto de noptr + NULL es el de los punteros. No existe una definición para la adición de punteros en el lenguaje C; Se pueden sumar (o restar) punteros y enteros. En caso de que o sea ​​un puntero, el otro debe ser un número entero, por lo que es efectivo o dependiendo de las definiciones y se pueden esperar varios comportamientos: todo funciona, advertencia de conversión entre el puntero y el número entero, falla al compilar, .. .ptr + NULLptrNULLptr + NULL(int)ptr + NULLptr + (int)NULLptrNULL

pmg
fuente
Lo he visto #define NULL (void *)0antes. ¿Estás seguro de que NULL y 0 simple son 100% equivalentes?
machine_1
2
En el contexto del puntero, sí ... condición enfatizada en mi respuesta, gracias
pmg
@phuclv: No tengo absolutamente ninguna idea sobre C ++. Mi respuesta (excepto el bit entre paréntesis) es sobre C
pmg
@phuclv ptr + NULLno se utiliza NULLen el contexto de los punteros
pmg
3
@JesperJuhl: en el contexto de los punteros son 100% equivalentes. No tengo idea de lo que nullptres, pero ((void*)0)y 0(o '\0') son equivalentes en el contexto de los punteros ...if (ptr == '\0' /* or equivalent 0, NULL */)
pmg
5

No, ya no se prefiere usar NULL(antigua forma de inicialización del puntero).

Desde C ++ 11:

La palabra clave nullptrdenota el puntero literal. Es un valor de tipo std :: nullptr_t. Existen conversiones implícitas de un nullptrvalor de puntero nulo de cualquier tipo de puntero y cualquier tipo de puntero a miembro. Existen conversiones similares para cualquier constante de puntero nulo, que incluye valores de tipo std::nullptr_t, así como la macroNULL .

https://en.cppreference.com/w/cpp/language/nullptr

En realidad, std :: nullptr_t es el tipo de lo literal puntero nulo, nullptr. Es un tipo distinto que no es en sí mismo un tipo de puntero o un puntero al 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)
}

Salida:

Pointer to integer overload
Pointer to double overload
null pointer overload
Mannoj
fuente
La pregunta es sobre asignar NULL a 0 para enteros. En este sentido, nada cambia con nullptr en lugar de NULL.
ivan.ukr
Usó palabras como "puntero NULL".
Mannoj
Por cierto, C ++ no tiene el concepto NULL después de C ++ 11. El autor podría estar confundido acerca del uso de constexpr o definir la inicialización a la antigua usanza. en.cppreference.com/w/cpp/language/default_initialization
Mannoj