resueltos punteros puntero lenguaje funciones ejercicios declarar con como apuntadores c++ pointers language-lawyer dangling-pointer

c++ - lenguaje - ¿Es legal comparar punteros colgantes?



punteros en c++ ejercicios resueltos pdf (3)

¿Es legal comparar punteros colgantes?

int *p, *q; { int a; p = &a; } { int b; q = &b; } std::cout << (p == q) << ''/n'';

Observe cómo p y q apuntan a objetos que ya han desaparecido. ¿Es esto legal?


Históricamente, ha habido algunos sistemas en los que el uso de un puntero como un valor r podría hacer que el sistema obtenga alguna información identificada por algunos bits en ese puntero. Por ejemplo, si un puntero podría contener la dirección del encabezado de un objeto junto con un desplazamiento en el objeto, recuperar un puntero podría hacer que el sistema también recupere información de ese encabezado. Si el objeto ha dejado de existir, el intento de obtener información de su encabezado podría fallar con consecuencias arbitrarias.

Dicho esto, en la gran mayoría de las implementaciones de C, todos los punteros que estuvieron vivos en algún momento en el tiempo mantendrán para siempre las mismas relaciones con respecto a los operadores relacionales y de resta que tenían en ese momento en particular. De hecho, en la mayoría de las implementaciones si uno tiene char *p , uno puede determinar si identifica parte de un objeto identificado por char *base; size_t size; char *base; size_t size; comprobando si (size_t)(p-base) < size ; dicha comparación funcionará incluso de forma retrospectiva si hay una superposición en la vida útil de los objetos.

Desafortunadamente, el Estándar no define ningún medio por el cual el código pueda indicar que requiere ninguna de las últimas garantías, ni existe un medio estándar por el cual el código pueda preguntar si una implementación particular puede prometer alguno de estos últimos comportamientos y rechazar la compilación si no lo hace. . Además, algunas implementaciones hipermodernas considerarán cualquier uso de operadores relacionales o de resta en dos punteros como una promesa del programador de que los punteros en cuestión siempre identificarán el mismo objeto vivo y omitirán cualquier código que solo sería relevante si ese supuesto no aguantó En consecuencia, a pesar de que muchas plataformas de hardware podrían ofrecer garantías que serían útiles para muchos algoritmos, no hay una forma segura por la cual el código pueda explotar tales garantías, incluso si el código nunca necesita ejecutarse en hardware que no los proporciona naturalmente.


Los punteros contienen las direcciones de las variables a las que hacen referencia. Las direcciones son válidas incluso cuando las variables que solían almacenarse allí se liberan / destruyen / no están disponibles. Mientras no intente usar los valores en esas direcciones, estará seguro, lo que significa que * py * q no estarán definidos.

Obviamente, el resultado es la implementación definida, por lo tanto, este ejemplo de código puede usarse para estudiar las características de su compilador si uno no quiere profundizar en el código de ensamblaje.

Si esta es una práctica significativa es una discusión totalmente diferente.


Introducción: La primera cuestión es si es legal usar el valor de p en absoluto.

Después de que a ha sido destruido, p adquiere lo que se conoce como un valor de puntero no válido . Cita de N4430 (para una discusión sobre el estado de N4430, consulte la "Nota" a continuación):

Cuando se alcanza el final de la duración de una región de almacenamiento, los valores de todos los punteros que representan la dirección de cualquier parte del almacenamiento desasignado se convierten en valores de puntero no válidos .

El comportamiento cuando se usa un valor de puntero no válido también se cubre en la misma sección de N4430 (y aparece texto casi idéntico en C ++ 14 [basic.stc.dynamic.deallocation] / 4):

La indirección a través de un valor de puntero no válido y el paso de un valor de puntero no válido a una función de desasignación tienen un comportamiento indefinido. Cualquier otro uso de un valor de puntero no válido tiene un comportamiento definido por la implementación .

[ Nota al pie: Algunas implementaciones pueden definir que copiar un valor de puntero no válido causa un error de tiempo de ejecución generado por el sistema. - nota al pie de página]

Por lo tanto, deberá consultar la documentación de su implementación para averiguar qué debería suceder aquí (desde C ++ 14).

El uso del término en las citas anteriores means necesario convertir lvalue-to-rvalue, como en C ++ 14 [conv.lval / 2]:

Cuando se aplica una conversión lvalue-to-rvalue a una expresión e, y el [...] objeto al que se refiere el glvalue contiene un valor de puntero no válido, el comportamiento se define por la implementación.

Historia: en C ++ 11 esto decía indefinido en lugar de definido por la implementación ; fue cambiado por DR1438 . Consulte el historial de edición de esta publicación para ver las cotizaciones completas.

Aplicación a p == q : suponiendo que hayamos aceptado en C ++ 14 + N4430 que el resultado de evaluar q está definido por la implementación, y que la implementación no define que ocurra una trampa de hardware; [expr.eq] / 2 dice:

Dos punteros se comparan igual si ambos son nulos, ambos apuntan a la misma función o ambos representan la misma dirección (3.9.2), de lo contrario se comparan desiguales.

Dado que está definido por la implementación qué valores se obtienen cuando se evalúan q , no podemos decir con certeza qué sucederá aquí. Pero debe estar definido por la implementación o no especificado.

g ++ parece exhibir un comportamiento no especificado en este caso; dependiendo del interruptor -O pude hacer que dijera 1 o 0 , lo que corresponde a si la misma dirección de memoria se reutilizó o no para b después de que a hubiera sido destruida.

Nota sobre N4430: Esta es una resolución de defecto propuesta para C ++ 14, que aún no ha sido aceptada. Limpia muchas palabras que rodean la vida útil de los objetos, punteros inválidos, subobjetos, uniones y acceso a límites de matriz.

En el texto de C ++ 14, se define en [basic.stc.dynamic.deallocation] / 4 y los párrafos subsiguientes que surge un valor de puntero no válido cuando se usa delete . Sin embargo, no se establece claramente si el mismo principio se aplica o no al almacenamiento estático o automático.

Hay una definición de "puntero válido" en [basic.compound] / 3, pero es demasiado vago para usarla con sensatez. [Basic.life] / 5 (nota al pie) se refiere al mismo texto para definir el comportamiento de los punteros a objetos de duración del almacenamiento estático, lo que sugiere que estaba destinado a aplicarse a todos los tipos de almacenamiento.

En N4430, el texto se mueve desde esa sección hacia arriba un nivel para que se aplique claramente a todas las duraciones de almacenamiento. Hay una nota adjunta:

Nota de redacción: esto debería aplicarse a todas las duraciones de almacenamiento que pueden finalizar, no solo a la duración del almacenamiento dinámico. En una implementación que admite subprocesos o pilas segmentadas, el subproceso y el almacenamiento automático pueden comportarse de la misma manera que el almacenamiento dinámico.

Mi opinión: no veo ninguna forma coherente de interpretar el estándar (anterior a N4430) que no sea decir que p adquiere un valor de puntero no válido. El comportamiento no parece estar cubierto por ninguna otra sección además de lo que ya hemos visto. Por lo tanto, me complace tratar la redacción del N4430 como la intención de la norma en este caso.