traducir significa qué ingles español c pointers language-lawyer undefined-behavior

ingles - qué significa en español



¿Cuándo es válido acceder a un puntero a un objeto "muerto"? (3)

Primero, para aclarar, ¡ no estoy hablando de desreferenciar punteros inválidos!

Considera los siguientes dos ejemplos.

Ejemplo 1

typedef struct { int *p; } T; T a = { malloc(sizeof(int) }; free(a.p); // a.p is now indeterminate? T b = a; // Access through a non-character type?

Ejemplo 2

void foo(int *p) {} int *p = malloc(sizeof(int)); free(p); // p is now indeterminate? foo(p); // Access through a non-character type?

Pregunta

¿Alguno de los ejemplos anteriores invoca un comportamiento indefinido?

Contexto

Esta pregunta se plantea en respuesta a esta discusión . La sugerencia fue que, por ejemplo, los argumentos del puntero se pueden pasar a una función a través de registros de segmento x86, lo que podría causar una excepción de hardware.

A partir del estándar C99, aprendemos lo siguiente (el énfasis es mío):

[3.17] valor indeterminado , ya sea un valor no especificado o una representación de trampa

y entonces:

[6.2.4 p2] El valor de un puntero se vuelve indeterminado cuando el objeto al que apunta llega al final de su vida útil.

y entonces:

[6.2.6.1 p5] Ciertas representaciones de objetos no necesitan representar un valor del tipo de objeto. Si el valor almacenado de un objeto tiene tal representación y es leído por una expresión lvalue que no tiene un tipo de carácter, el comportamiento no está definido . Si tal representación es producida por un efecto secundario que modifica todo o parte del objeto por una expresión lvalue que no tiene un tipo de carácter, el comportamiento no está definido. Tal representación se llama representación de trampa .

Tomando todo esto en conjunto, ¿qué restricciones tenemos para acceder a los punteros a los objetos "muertos"?

Apéndice

Aunque he citado el estándar C99 anterior, me interesaría saber si el comportamiento difiere en cualquiera de los estándares de C ++.


Discusión C ++

Respuesta corta: en C ++, no existe el acceso a "leer" una instancia de clase; solo puede "leer" un objeto que no sea de clase, y esto se hace mediante una conversión lvalue-to-rvalue.

Respuesta detallada:

typedef struct { int *p; } T;

T designa una clase sin nombre. Por el bien de la discusión, llamemos a esta clase T :

struct T { int *p; };

Como no declaró un constructor de copia, el compilador declara implícitamente uno, por lo que la definición de clase es la siguiente:

struct T { int *p; T (const T&); };

Entonces tenemos:

T a; T b = a; // Access through a non-character type?

Sí, de hecho; esto es inicialización por el constructor de copia, por lo que el compilador generará la definición del constructor de copia; la definición es equivalente con

inline T::T (const T& rhs) : p(rhs.p) { }

Entonces está accediendo al valor como un puntero , no como un grupo de bytes.

Si el valor del puntero no es válido (no se inicializa, se libera), el comportamiento no está definido.


El ejemplo 2 no es válido. El análisis en tu pregunta es correcto.

El ejemplo 1 es válido. Un tipo de estructura nunca contiene una representación de trampa, incluso si lo hace uno de sus miembros. Esto significa que la asignación de estructura, en un sistema donde las representaciones de trampa causarían problemas, debe implementarse como una copia bytewise, en lugar de una copia de miembro por miembro.

6.2.6 Representaciones de tipos

6.2.6.1 General

6 [...] El valor de una estructura o de un objeto de unión nunca está en la representación de rap, aunque el valor de un miembro de la estructura o del objeto de unión puede ser una representación de trampa.


Mi interpretación es que, aunque solo los tipos que no son de carácter pueden tener representaciones de trampa, cualquier tipo puede tener un valor indeterminado, y que el acceso a un objeto con un valor indeterminado de cualquier manera invoca un comportamiento indefinido. El ejemplo más infame podría ser el uso no válido de OpenSSL de objetos no inicializados como una semilla aleatoria.

Entonces, la respuesta a tu pregunta sería: nunca.

Por cierto, una consecuencia interesante de no solo el objeto apuntado, sino que el puntero mismo es indeterminado después de free o realloc es que este modismo invoca un comportamiento indefinido:

void *tmp = realloc(ptr, newsize); if (tmp != ptr) { /* ... */ }