Tenía la impresión de que acceder a un union
miembro que no sea el último conjunto es UB, pero parece que no puedo encontrar una referencia sólida (aparte de las respuestas que afirman que es UB pero sin ningún apoyo del estándar).
Entonces, ¿es un comportamiento indefinido?
c++
undefined-behavior
language-lawyer
unions
Luchian Grigore
fuente
fuente
Respuestas:
La confusión es que C permite explícitamente la escritura de tipos mediante una unión, mientras que C ++ (c ++ 11) no tiene dicho permiso.
La situación con C ++:
C ++ más tarde tiene lenguaje que permite el uso de uniones que contienen
struct
s con secuencias iniciales comunes; Sin embargo, esto no permite la escritura de tipos.Para determinar si la unión de tipos está permitida en C ++, tenemos que buscar más. Recordar quec99 es una referencia normativa para C ++ 11 (y C99 tiene un lenguaje similar al C11 que permite la unión de tipos):
Se pone particularmente interesante cuando leemos
Entonces, para un tipo primitivo (que ipso facto tiene una inicialización trivial) contenido en una unión, la vida útil del objeto abarca al menos la vida útil de la unión misma. Esto nos permite invocar
Suponiendo que la operación en la que estamos interesados es la escritura de tipos, es decir, tomar el valor de un miembro de la unión no activo, y dado lo anterior que tenemos una referencia válida al objeto al que hace referencia ese miembro, esa operación es lvalue-to -conversión de valor:
La pregunta entonces es si un objeto que es un miembro de la unión no activo se inicializa por almacenamiento en el miembro de la unión activa. Por lo que puedo decir, este no es el caso y, aunque si:
char
almacenamiento de matriz y viceversa (3.9: 2), oel acceso a una unión por un miembro no activo se define y se define para seguir la representación del objeto y valor, el acceso sin una de las interposiciones anteriores es un comportamiento indefinido. Esto tiene implicaciones para las optimizaciones permitidas en dicho programa, ya que la implementación puede, por supuesto, asumir que no ocurre un comportamiento indefinido.
Es decir, aunque podemos formar legítimamente un valor l para un miembro sindical no activo (razón por la cual está bien asignar a un miembro no activo sin construcción) se considera no inicializado.
fuente
memcpy
implementaciones personalizadas (acceder a objetos usandounsigned char
valores), no permitía acceder a*p
despuésint *p = 0; const int *const *pp = &p;
(aunque la conversión implícita deint**
aconst int*const*
es válida), no permitía incluso accederc
despuésstruct S s; const S &c = s;
. CWG número 616 . ¿La nueva redacción lo permite? También hay [basic.lval].&
operador unario quiere decir cuando se aplica a un miembro del sindicato. Creo que el puntero resultante debería poder usarse para acceder al miembro al menos hasta la próxima vez que se use el siguiente uso directo o indirecto de cualquier otro valor de miembro, pero en gcc el puntero no se puede usar ni tanto tiempo, lo que plantea una pregunta de qué&
se supone que el operador quiere decir.El estándar C ++ 11 lo dice de esta manera
Si solo se almacena un valor, ¿cómo puede leer otro? Simplemente no está ahí.
La documentación de gcc enumera esto en Comportamiento definido de implementación
indicando que esto no es requerido por el estándar C.
2016-01-05: A través de los comentarios, me vinculé al Informe de defectos C99 # 283 que agrega un texto similar como una nota al pie del documento estándar C:
Sin embargo, no estoy seguro si aclara mucho, considerando que una nota al pie no es normativa para el estándar.
fuente
Creo que lo más cercano al estándar es decir que su comportamiento indefinido es donde define el comportamiento para una unión que contiene una secuencia inicial común (C99, §6.5.2.3 / 5):
C ++ 11 proporciona requisitos / permisos similares en §9.2 / 19:
Aunque ninguno lo declara directamente, ambos tienen una fuerte implicación de que "inspeccionar" (leer) a un miembro está "permitido" solo si 1) es (parte de) el miembro escrito más recientemente, o 2) es parte de una inicial común secuencia.
Esa no es una declaración directa de que hacer lo contrario es un comportamiento indefinido, pero es lo más cercano a lo que sé.
fuente
union
indefinidos, ya que un blog en particular me había dado la impresión de que esto estaba bien, y construí varias estructuras y proyectos grandes a su alrededor. Ahora creo que podría estar bien después de todo, ya que misunion
s contienen clases que tienen los mismos tipos en la parte delanteraunion
contiene, por ejemplo, auint8_t
y aclass Something { uint8_t myByte; [...] };
? Supongo que esta condición también se aplicaría aquí, pero está redactada muy deliberadamente para permitir solostruct
s. Afortunadamente, ya estoy usando esos en lugar de primitivas crudas: OAlgo que aún no se menciona en las respuestas disponibles es la nota a pie de página 37 en el párrafo 21 de la sección 6.2.5:
Este requisito parece implicar claramente que no debe escribir en un miembro y leer en otro. En este caso, podría ser un comportamiento indefinido por falta de especificación.
fuente
Bien explico esto con un ejemplo.
Supongamos que tenemos la siguiente unión:
Supongo que
sizeof(int)
da 4, ysizeof(short)
da 2.cuando escribes
union A a = {10}
bien, crea una nueva var de tipo A y pon el valor 10.su memoria debería verse así: (recuerde que todos los miembros del sindicato obtienen la misma ubicación)
como puede ver, el valor de ax es 10, el valor de ay 1 es 10 y el valor de ay [0] es 0.
ahora, ¿qué pasaría si hago esto?
nuestra memoria se verá así:
esto convertirá el valor de ax a 2424842 (en decimal).
ahora, si su unión tiene un flotante, o doble, su mapa de memoria puede ser más desordenado, debido a la forma en que almacena los números exactos. Más información puede obtener aquí .
fuente