punteros - Devolver una estructura local parcialmente inicializada desde una función y un comportamiento indefinido
typedef struct en c (2)
(Al iniciarse parcialmente, me refiero a definido como no inicializado y uno de sus miembros tiene un valor válido, pero no a todos. Y al decir local, me refiero a la duración del almacenamiento automático. Esta pregunta solo habla de eso).
El uso de una variable no inicializada automática que podría definirse con el registro, ya que un valor de r es un comportamiento indefinido. Las estructuras se pueden definir con el especificador de clase de almacenamiento de registro.
6.3.2.1
- Si el lvalue designa un objeto de duración de almacenamiento automático que podría haberse declarado con la clase de almacenamiento de registro (nunca se tomó su dirección), y ese objeto no está inicializado (no se declaró con un inicializador y no se realizó ninguna asignación antes de su uso) ), el comportamiento es indefinido.
Tenga en cuenta que específicamente dice eso y no se le han realizado asignaciones .
Además, sabemos que una estructura no puede ser un valor de captura:
6.2.6.1.
- El valor de una estructura o un objeto de unión nunca es una representación de captura, aunque el valor de un miembro de la estructura o el objeto de unión puede ser una representación de captura
Por lo tanto, devolver una estructura sin inicializar es claramente un comportamiento indefinido.
Declaración: se define la devolución de una estructura sin inicializar que tenía uno de sus miembros asignado con un valor válido.
Ejemplo para una comprensión más fácil:
struct test
{
int a;
int b;
};
struct test Get( void )
{
struct test g;
g.a = 123;
return g;
}
{
struct test t = Get();
}
Simplemente me centré en regresar, pero creo que esto debería aplicarse también a una tarea simple, sin ninguna diferencia.
¿Mi declaración es correcta?
Aparte del detalle de devolver el valor de una función, este es precisamente el tema del Informe de Defectos 222 , presentado en 2000 por Clive Feather, y la resolución de ese DR parece responder claramente a la pregunta: devolver una struct
parcialmente no inicializada es bien definido (aunque los valores de los miembros no inicializados no se pueden usar)
La resolución al DR aclaró que union
objetos de struct
y union
no tienen representaciones de trampas (que se agregaron explícitamente a §6.2.6.1 / 6). Por consiguiente, la copia miembro por miembro no se puede utilizar en una arquitectura en la que los miembros individuales puedan atrapar. Aunque, presumiblemente para parsimonia, no se agregó una declaración explícita a este efecto a la norma, la nota de pie de página 42 (ahora nota de pie de página 51) que mencionó anteriormente la posibilidad de copia miembro por miembro se reemplazó por una declaración mucho más débil que indica que los bits de relleno no necesitan ser copiado
El acta de la reunión del WG14 (Toronto, octubre de 2000) es clara (énfasis añadido):
DR222 - Estructuras parcialmente inicializadas
Este DR pregunta si la asignación de
struct
está bien definida o no cuando la fuente de la asignación es unastruct
, algunos de cuyos miembros no han recibido un valor. Hubo consenso en que esto debería estar bien definido debido al uso común, incluida la estructura destruct tm
especificada por el estándar. También hubo consenso en que si la asignación con algunos miembros sin inicializar (y, por lo tanto, posiblemente con un valor de trampa) se estaba definiendo bien, había poco valor en requerir que al menos a un miembro se le hubiera dado un valor correctamente.
Por lo tanto, la noción de que el valor de unastruct
ounion
en su totalidad puede tener un valor de captura se está eliminando.
Es interesante notar que en los minutos anteriores, el comité sostuvo que ni siquiera era necesario que se le diera un valor a un solo miembro de la struct
. Sin embargo, ese requisito se restableció posteriormente en algunos casos, con la resolución a DR338 (ver más abajo).
En resumen:
Si un objeto agregado automático se ha inicializado, al menos parcialmente, o si se ha tomado su dirección (por lo tanto, no es adecuado para una declaración de
register
según §6.3.2.1 / 2), entonces la conversión de ese valor a ese valor es correcta. -definidoDicho objeto se puede asignar a otro objeto agregado del mismo tipo, posiblemente después de haber sido devuelto desde una función, sin invocar un comportamiento indefinido.
La lectura de los miembros sin inicializar en la copia es indefinida o indeterminada, dependiendo de si las representaciones de trampa son posibles. (Una lectura a través de un puntero a un tipo de carácter estrecho no firmado no puede interceptarse, por ejemplo). Pero si escribe el miembro antes de leerlo, está bien.
No creo que haya ninguna diferencia teórica entre la asignación de objetos de union
y struct
. Obviamente, las union
no pueden copiarse miembro por miembro (lo que eso significaría incluso), y el hecho de que algún miembro inactivo tenga una representación de trampa es irrelevante, incluso si ese miembro no tiene alias por ningún otro elemento . No hay una razón obvia por la que una struct
deba ser diferente.
Finalmente, con respecto a la excepción en §6.3.2.1 / 2: esto se agregó como resultado de la resolución al DR 338 . La esencia de ese DR es que algún hardware (IA64) puede interceptar el uso de un valor no inicializado en un registro . C99 no permite representaciones de trampas para caracteres sin firmar. Por lo tanto, en este tipo de hardware, podría no ser posible mantener una variable automática en un registro sin "innecesariamente" inicializar el registro.
La resolución del DR 338 marca específicamente como comportamiento indefinido el uso de valores no inicializados en variables automáticas que posiblemente podrían almacenarse en registros (es decir, aquellos cuya dirección nunca se ha tomado, como si fuera un register
declarado), lo que permite al compilador mantener un register
automático. unsigned char
en un registro sin preocuparse por el contenido anterior de ese registro.
Como efecto secundario del DR 338, parece que las struct
automáticas completamente sin inicializar cuya dirección nunca se ha tomado no pueden sufrir una conversión de valor a valor de r. No sé si ese efecto secundario fue contemplado por completo en la resolución a DR 338, pero no se aplica en el caso de una struct
parcialmente inicializada, como en esta pregunta.
Su declaración sobre 6.3.2.1 es correcta, si el objeto asignado a lvalue no está inicializado, entonces el comportamiento no está definido.
Entonces, la pregunta es si su estructura debe considerarse como no inicializada o no. Usted asigna un valor a uno de los miembros, por lo que ha habido una asignación al objeto. De acuerdo con el 6.3.2.1 citado, eso significaría que no se puede considerar la estructura como un todo sin inicializar. Ese miembro en particular está claramente inicializado, aunque los otros miembros no lo estén.
Sin embargo, existe otro caso de comportamiento indefinido, y es cuando se almacena una representación de captura en el valor l:
6.2.6.1/5
Ciertas representaciones de objetos no necesitan representar un valor del tipo de objeto. Si el valor almacenado de un objeto tiene una representación de este tipo y es leído por una expresión lvalue que no tiene tipo de carácter, el comportamiento no está definido. Si una representación de este tipo es producida por un efecto secundario que modifica todo o parte del objeto por una expresión lvalue que no tiene tipo de carácter, el comportamiento es indefinido.50) Dicha representación se llama una representación de trampa.
El texto que citó en 6.2.6.1/6 dice que la estructura en sí misma no puede ser una representación de trampa, aunque sus miembros individuales pueden ser representaciones de trampa. Si lo son, entonces la asignación sería un comportamiento indefinido según lo anterior.
Pero tenga en cuenta la "puede ser trampa". No es seguro que sean representaciones de trampas, porque tienen valores indeterminados . Echa un vistazo a lo básico:
6.7.9 / 10
Si un objeto que tiene una duración de almacenamiento automático no se inicializa explícitamente, su valor es indeterminado.
y
3.19.2 / 1
valor indeterminado
ya sea un valor no especificado o una representación de trampa
Usar una variable con valor indeterminado es solo un comportamiento indefinido en caso de que el valor sea una representación de captura.
Si las variables de miembro no inicializadas de su estructura contendrán valores no especificados o representaciones de trampas es un comportamiento definido por la implementación.
Si la variable con valor indeterminado simplemente tiene un valor no especificado, entonces 6.2.6.1/5 no se aplica y no hay un comportamiento indefinido.
Conclusión: si la implementación establece que cualquier valor indeterminado para cualquiera de los miembros de la estructura es una representación de trampa, el comportamiento no está definido. De lo contrario, el comportamiento es meramente definido por la implementación / no especificado, los miembros no inicializados mantendrán valores no especificados.