que - ¿Por qué los punteros NULL se definen de manera diferente en C y C++?
punteros a cadenas (3)
De vuelta en C ++ 03, un nulo puntero fue definido por la especificación ISO (§4.10 / 1) como
Una constante de puntero nulo es una expresión de constante integral (5.19) rvalor de tipo entero que se evalúa como cero.
Es por eso que en C ++ puedes escribir
int* ptr = 0;
En C, esta regla es similar, pero es un poco diferente (§6.3.2.3 / 3):
Una expresión constante entera con el valor 0, o una expresión de este tipo para escribir
void *
, se denomina constante de puntero nulo.55) Si una constante de puntero nulo se convierte en un tipo de puntero, el puntero resultante, llamado puntero nulo, se garantizado para comparar desigual a un puntero a cualquier objeto o función.
En consecuencia, ambos
int* ptr = 0;
y
int* ptr = (void *)0
son legales Sin embargo, mi suposición es que el molde de void*
está aquí para que las declaraciones como
int x = NULL;
producir una advertencia de compilador en la mayoría de los sistemas. En C ++, esto no sería legal porque no se puede convertir implícitamente un void*
a otro tipo de puntero implícitamente sin un molde. Por ejemplo, esto es ilegal:
int* ptr = (void*)0; // Legal C, illegal C++
Sin embargo, esto genera problemas porque el código
int x = NULL;
es legal C ++. Debido a esto y la consiguiente confusión (y otro caso, que se muestra más adelante), desde C ++ 11, hay una palabra clave nullptr
representa un puntero nulo:
int* ptr = nullptr;
Esto no tiene ninguno de los problemas anteriores.
La otra ventaja de nullptr
sobre 0 es que funciona mejor con el sistema de tipo C ++. Por ejemplo, supongamos que tengo estas dos funciones:
void DoSomething(int x);
void DoSomething(char* x);
Si llamo
DoSomething(NULL);
Es equivalente a
DoSomething(0);
que llama DoSomething(int)
lugar del DoSomething(char*)
esperado DoSomething(char*)
. Sin embargo, con nullptr
, podría escribir
DoSomething(nullptr);
Y llamará a la función DoSomething(char*)
como se espera.
De forma similar, supongamos que tengo un vector<Object*>
y quiero configurar cada elemento para que sea un puntero nulo. Usando el algoritmo std::fill
, podría intentar escribir
std::fill(v.begin(), v.end(), NULL);
Sin embargo, esto no se compila, porque el sistema de plantillas trata a NULL
como un int
y no como un puntero. Para arreglar esto, tendría que escribir
std::fill(v.begin(), v.end(), (Object*)NULL);
Esto es feo y en cierto modo frustra el propósito del sistema de plantillas. Para solucionar esto, puedo usar nullptr
:
std::fill(v.begin(), v.end(), nullptr);
Y dado que se sabe que nullptr
tiene un tipo correspondiente a un puntero nulo (específicamente, std::nullptr_t
), se compilará correctamente.
¡Espero que esto ayude!
En C, NULL
se define como (void *)0
mientras que en C ++ es 0
. ¿Por que es esto entonces? En CI podemos entender que si NULL
no está encasillado a (void *)
entonces los compiladores pueden / no generar advertencia. Aparte de esto, ¿hay alguna razón?
El lenguaje C se creó para facilitar la programación de microprocesadores. El puntero de CA se usa para almacenar la dirección de los datos en la memoria. Se necesitaba una forma de representar que un puntero no tenía un valor válido. Se eligió la dirección cero ya que todos los microprocesadores usaron esa dirección para arrancar. Como no podía usarse para nada más, cero era una buena opción para representar un puntero sin valor válido. C ++ es compatible con C, por lo que es heredado de esa convención.
El requisito de emitir cero cuando se usa como un puntero es solo un agregado reciente. Las generaciones posteriores de C querían tener más rigor (y con suerte menos errores) para que comenzaran a ser más pedantes sobre la sintaxis.
En C, NULL
expande a una "constante de puntero nulo" definida por la implementación. Una constante de puntero nulo es una expresión constante entera con el valor 0, o una expresión de este tipo como void*
. Por lo tanto, una implementación de C puede definir NULL
como 0
o como ((void*)0)
.
En C ++, las reglas para las constantes de puntero nulo son diferentes. En particular, ((void*)0)
no es una constante de puntero nulo de C ++, por lo que una implementación de C ++ no puede definir NULL
esa manera.