que - punteros c++ pdf
Cómo escribir código C/C++ correctamente cuando el puntero nulo no es todos los bits cero (5)
Como dice el FAQ de comp.lang.c , hay arquitecturas donde el puntero nulo no es todos bits cero. Entonces la pregunta es qué comprueba realmente la siguiente construcción:
void* p = get_some_pointer();
if (!p)
return;
¿Estoy comparando
p
con un puntero nulo dependiente de la máquina o estoy comparando
p
con cero aritmético?
Debería escribir
void* p = get_some_pointer();
if (NULL == p)
return;
en lugar de estar listo para tales arquitecturas o es solo mi paranoia?
De acuerdo con la especificación C:
Una expresión constante entera con el valor 0, o tal expresión emitida para escribir void *, se llama constante de puntero nulo. 55) Si una constante de puntero nulo se convierte en un tipo de puntero, se garantiza que el puntero resultante, llamado puntero nulo, se comparará desigual a un puntero con cualquier objeto o función.
Entonces
0
es una constante de puntero nulo.
Y si lo convertimos a un tipo de puntero, obtendremos un puntero nulo que puede ser distinto de cero para algunas arquitecturas.
A continuación, veamos qué dice la especificación sobre la comparación de punteros y una constante de puntero nulo:
Si un operando es un puntero y el otro es una constante de puntero nulo, la constante de puntero nulo se convierte al tipo de puntero.
Consideremos
(p == 0)
: primero
0
se convierte en un puntero nulo, y luego
p
se compara con una constante de puntero nulo cuyos valores de bits reales dependen de la arquitectura.
A continuación, vea lo que dice la especificación sobre el operador de negación:
¡El resultado del operador de negación lógica! es 0 si el valor de su operando se compara desigual a 0, 1 si el valor de su operando se compara igual a 0. El resultado tiene el tipo int. La expresión! E es equivalente a (0 == E).
Esto significa que
(!p)
es equivalente a
(p == 0)
que, según las especificaciones, prueba
p
contra la constante de puntero nulo definida por la máquina.
Por lo tanto, puede escribir de forma segura
if (!p)
incluso en arquitecturas donde la constante de puntero nulo no es todo cero.
En cuanto a C ++, una constante de puntero nulo se define como:
Una constante de puntero nulo es un valor de expresión de constante integral (5.19) de tipo entero que se evalúa a cero o un valor de tipo std :: nullptr_t. Una constante de puntero nulo se puede convertir a un tipo de puntero; el resultado es el valor de puntero nulo de ese tipo y es distinguible de cualquier otro valor de puntero de objeto o tipo de puntero de función.
Lo que está cerca de lo que tenemos para C, más el azúcar de sintaxis
nullptr
.
El comportamiento del operador
==
se define por:
Además, se pueden comparar punteros a miembros, o un puntero a miembro y una constante de puntero nulo. Las conversiones de puntero a miembro (4.11) y las conversiones de calificación (4.4) se realizan para llevarlas a un tipo común. Si un operando es una constante de puntero nulo, el tipo común es el tipo del otro operando. De lo contrario, el tipo común es un puntero al tipo de miembro similar (4.4) al tipo de uno de los operandos, con una firma de calificación cv (4.4) que es la unión de las firmas de calificación cv de los tipos de operando. [Nota: esto implica que cualquier puntero a miembro se puede comparar con una constante de puntero nulo. - nota final]
Eso lleva a la conversión de
0
a un tipo de puntero (como para C).
Para el operador de negación:
¡El operando del operador de negación lógica! se convierte contextualmente a bool (Cláusula 4); su valor es verdadero si el operando convertido es verdadero y falso de lo contrario. El tipo de resultado es bool.
Eso significa que el resultado de
!p
depende de cómo se realice la conversión de puntero a
bool
.
El estándar dice:
Un valor cero, un valor de puntero nulo o un valor de puntero de miembro nulo se convierte en falso;
Entonces,
if (p==NULL)
y
if (!p)
hacen lo mismo en C ++.
En el pasado, las computadoras STRATUS tenían punteros nulos como 1 en todos los idiomas.
Esto causó problemas para C, por lo que su compilador de C permitiría que la comparación de puntero de 0 y 1 devuelva verdadero
Esto permitiría:
void * ptr=some_func();
if (!ptr)
{
return;
}
Para
return
en un ptr nulo, incluso aunque pueda ver que
ptr
tenía un valor de 1 en el depurador
if ((void *)0 == (void *)1)
{
printf("Welcome to STRATUS/n");
}
De hecho, imprimiría "Bienvenido a STRATUS"
Esta respuesta se aplica a C.
No mezcle
NULL
con punteros nulos.
NULL
es solo una macro garantizada para ser una
constante de puntero nulo
.
Se garantiza que una constante de puntero nulo sea
0
o
(void*)0
.
Desde C11 6.3.2.3:
Una expresión constante entera con el valor 0, o tal expresión emitida para escribir void *, se denomina constante de puntero nulo 66). Si una constante de puntero nulo se convierte en un tipo de puntero, se garantiza que el puntero resultante, llamado puntero nulo, se comparará desigual a un puntero con cualquier objeto o función.
66) La macro NULL se define en <stddef.h> (y otros encabezados) como una constante de puntero nulo; ver 7.19.
7.19:
Las macros son
NULO
que se expande a una constante de puntero nulo definida por la implementación;
La implementación definida en el caso de
NULL
es
0
o
(void*)0
.
NULL
no puede ser otra cosa.
Sin embargo, cuando se asigna una constante de puntero nulo a un puntero, se obtiene un
puntero nulo
, que puede no tener el valor cero, aunque se compare igual a una constante de puntero nulo.
El código
if (!p)
no tiene nada que ver con la macro
NULL
, está comparando un puntero nulo con el valor aritmético cero.
Entonces, en teoría, un código como
int* p = NULL
puede dar como resultado un puntero nulo
p
que es diferente de cero.
No importa si el puntero nulo es cero en todos los bits o no en la máquina real.
Suponiendo que
p
es un puntero:
if (!p)
siempre es una forma legal de probar si
p
es un puntero nulo, y siempre es equivalente a:
if (p == NULL)
Quizás te interese otro artículo de C-FAQ: Esto es extraño. NULL se garantiza que sea 0, pero el puntero nulo no lo es.
Lo anterior es cierto tanto para C como para C ++.
Tenga en cuenta que en C ++ (11), se prefiere usar
nullptr
para un puntero nulo literal.
Si su compilador es bueno, hay dos cosas (y solo dos) a tener en cuenta.
1: los punteros inicializados por defecto estáticos (es decir, no asignados) no tendrán NULL en ellos.
2: memset () en una estructura o matriz o, por extensión, calloc () no establecerá punteros en NULL.