Yo estaba bajo la impresión de que el acceso a una union miembro que no sea el último conjunto es UB, pero me parece que no puede encontrar una referencia sólida (aparte de respuestas afirmando que la UB, pero sin ningún tipo de apoyo de la norma).

Por lo tanto, es un comportamiento indefinido?

C99 (y creo que con C++11) permitir explícitamente de tipo descriptivo con los sindicatos. Así que creo que cae bajo «definido por la implementación» de la conducta.
Yo la he utilizado en varias ocasiones para convertir individuales int a char. Así que, definitivamente, sé que no es indefinido. Lo he usado en el Sol CC compilador. Así, todavía puede ser dependiente del compilador.
Claramente, no sabes lo que significa para el comportamiento a ser indefinido. El hecho de que se apareció a trabajar para usted en alguna instancia no contradice su undefinededness.
Relacionados: Propósito de los Sindicatos en C y C++
la entrada de blog que enlace es muy específicamente en relación con la C99; esta pregunta es sólo etiquetadas para C++.

OriginalEl autor Luchian Grigore | 2012-07-07

5 Comentarios

  1. 106

    La confusión es que la C explícitamente los permisos de tipo descriptivo a través de un sindicato, mientras que C++ () no tiene ningún tipo de permiso.

    6.5.2.3 Estructura y miembros de la unión

    95) Si el miembro de que se utiliza para leer el contenido de una unión de objeto no es el mismo que el miembro de la última utilizado para
    almacenar un valor en el objeto, la parte correspondiente de la representación de objeto de valor es reinterpretado
    como un objeto de representación en el nuevo tipo descrito en 6.2.6 (un proceso a veces se denomina «tipo de
    descriptivo»). Esto podría ser una trampa de la representación.

    La situación con C++:

    9.5 Sindicatos [clase.de la unión]

    En un sindicato, en la mayoría de los no-miembros de datos estáticos puede estar activo en cualquier momento, es decir, el valor de a
    la mayoría de los no-miembros de datos estáticos pueden ser almacenados en una unión en cualquier momento.

    C++ posterior posee un lenguaje que permite el uso de uniones que contienen structs comunes de secuencias iniciales; esto no obstante, el permiso de tipo descriptivo.

    Para determinar si la unión de tipo descriptivo es permitido en C++, tenemos que buscar más. Recordar que es un referente normativo para C++11 (y C99 tiene un lenguaje similar a C11 permitiendo la unión de tipo descriptivo):

    3.9 Tipos [basic.tipos]

    4 – El objeto de la representación de un objeto de tipo T es la secuencia de N unsigned char objetos tomados por
    el objeto de tipo T, donde N es igual a sizeof(T). El valor de la representación de un objeto es el conjunto de bits que
    mantener el valor de tipo T. Para trivialmente copiable tipos, el valor de la representación es un conjunto de bits en el objeto
    la representación que determina un valor, el cual es un elemento discreto de una aplicación definida por el conjunto de
    valores. 42

    42) La intención es que el modelo de memoria de C++ es compatible con el de la ISO/IEC 9899 Lenguaje de Programación C.

    Se vuelve especialmente interesante cuando leemos

    3.8 tiempo de vida del Objeto [basic.la vida]

    La vida útil de un objeto de tipo T comienza cuando:
    — almacenamiento con la correcta alineación y tamaño para el tipo T se obtiene, y
    — si el objeto tiene una inicialización no trivial, su inicialización es completa.

    Así que para un tipo primitivo (que ipso facto ha trivial de inicialización) contenida en un sindicato, el tiempo de vida del objeto abarca al menos el tiempo de vida de la propia unión. Esto nos permite invocar

    3.9.2 Compuesto de tipos [basic.compuesto]

    Si un objeto de tipo T se encuentra en una dirección, Un puntero de tipo cv T* cuyo valor es el
    la dirección de Una se dice a punto para ese objeto, independientemente de cómo era el valor obtenido.

    Suponiendo que la operación que nos interesa es de tipo descriptivo es decir, tomando el valor de un activo no miembro de la unión, y por la de arriba, que tenemos una referencia válida para el objeto que se conoce por ese miembro, que la operación es lvalue-a-r-value de conversión:

    4.1 Lvalue-a-r-value de conversión [conv.lval]

    Un glvalue de un no-función, no de la matriz de tipo T puede ser convertido a un prvalue.
    Si T es un tipo incompleta, un programa que requiere esta conversión está mal formado. Si el objeto al que el glvalue se refiere no es un objeto de tipo T y no es un objeto de un tipo derivado de T, o si el objeto no está inicializada, un programa que requiere esta conversión tiene un comportamiento indefinido.

    La pregunta entonces es si un objeto que es un activo no miembro de la unión es inicializado por el almacenamiento del activo miembro de la unión. Por lo que puedo decir, este no es el caso y por ello, aunque si:

    • una unión se copia en char de la matriz de almacenamiento y back (3.9:2), o
    • un sindicato es bytewise copiado a otro sindicato del mismo tipo (3.9:3), o
    • una unión que se accede a través de las fronteras del idioma por parte de un elemento del programa conforme a la norma ISO/IEC 9899 (en la medida en que sea definido) (3.9:4 nota 42), luego

    el acceso a un sindicato por parte de un no-miembro activo se define y se define a seguir el objeto y el valor de la representación, el acceso sin una de las interposiciones es un comportamiento indefinido. Esto tiene implicaciones para las optimizaciones deberán ser realizados en un programa de este tipo, como la aplicación puede, por supuesto, asume que los indefinidos comportamiento no se produce.

    Que es, aunque legítimamente se puede formar un lvalue a un no-activo miembro de la unión (que es la razón por la asignación a un no-miembro activo sin construcción es aceptar) se considera para ser inicializadas.

    debe estar presente; buscar 6.5.2.3, nota de pie de página 82.
    3.8/1 dice que la vida de un objeto termina cuando su almacenamiento se reutiliza. Que me indica que un no-miembro activo de una unión de por vida se ha terminado debido a que su almacenamiento se ha reutilizado para el miembro activo. Eso significaría que usted está limitado en el uso del miembro (3.8/6).
    buen punto, pero si ha trivial de inicialización, a continuación, su vida comienza de nuevo inmediatamente o cuando la no-miembro activo es la que se accede (de almacenamiento con la correcta alineación y tamaño para el tipo T se obtiene).
    Bajo esa interpretación, a continuación, todos los bits de la memoria simultáneamente contiene objetos de todos los tipos que son trivialmente initiallizable y se cuenta con la alineación… entonces, ¿la vida de cualquier no-trivial initiallizable tipo inmediatamente final como su almacenamiento se reutiliza para todos estos otros tipos (y no reiniciar porque no son trivialmente initiallizable)?
    Usted puede encontrar algunas de las referencias que he enlace en esta respuesta de tipo descriptivo interesante. Especialmente la cita de Pascal Cuoq en mi nota de pie de página. También un lado de la cuestión, ya que la invocación de C99 ser un referente normativo para C++11 ¿tiene una posición en Podemos aplicar el contenido no se citan explícitamente a partir de las referencias normativas para el estándar de C++??

    OriginalEl autor ecatmur

  2. 22

    El estándar C++11 dice de esta manera

    9.5 Sindicatos

    En un sindicato, en la mayoría de los no-miembros de datos estáticos puede estar activo en cualquier momento, es decir, el valor de en la mayoría de los no-miembros de datos estáticos pueden ser almacenados en una unión en cualquier momento.

    Si sólo un valor se almacena, cómo se puede leer otro? Simplemente no está allí.


    El gcc documentación de las listas de este bajo Definido por la implementación de comportamiento

    • Un miembro de una unión de objetos se puede acceder usando un miembro de un tipo diferente (C90 6.3.2.3).

    La correspondiente bytes de la representación del objeto, son tratadas como un objeto del tipo de las utilizadas para el acceso. Ver de Tipo descriptivo. Esto puede ser una trampa de la representación.

    lo que indica que este no es requerido por el estándar de C.


    2016-01-05: a Través de los comentarios que yo estaba vinculado a C99 Informe De Defecto #283 que añade un texto similar como una nota a pie de página el estándar de C documento:

    78a) Si el miembro de que se utiliza para acceder a los contenidos de una unión de objeto no es el mismo como el último miembro utiliza para almacenar un valor en el objeto, la parte pertinente de la representación de objetos de valor se reinterpreta como un objeto de representación en el nuevo tipo descrito en 6.2.6 (un proceso que a veces se llama «tipo descriptivo»). Esto podría ser una trampa de la representación.

    No se si se aclara mucho, aunque, teniendo en cuenta que una nota de pie de página no es normativo para las normas.

    La UB no es lo que la norma dice que es de la UB, en cambio, es lo que la norma no describe cómo se debe trabajar. Este es exactamente el caso. ¿El estándar de describir lo que sucede? Se dice que es definido por la implementación? No y No. Así que es UB. Por otra parte, con respecto a los «miembros comparten la misma dirección de memoria» del argumento, usted tendrá que referirse a la asignación de alias a las reglas, que le llevará a la UB de nuevo.
    Está muy claro qué medio activo, «, es decir, el valor de en la mayoría de los no-miembros de datos estáticos pueden ser almacenados en una unión en cualquier momento.»
    Sí que los hay. Hay una infinita cantidad de casos que la norma no (y no puede) dirección. C++ es un Turing completo VM por lo que es incompleta.) Entonces, ¿qué? Explicar lo que «activa», se refieren a la cita anterior, después de «que».
    La omisión de la definición explícita de comportamiento también se unconsidered un comportamiento indefinido, de acuerdo a la sección de definiciones.
    Que la UB por una razón diferente – viola estricto aliasing.

    OriginalEl autor Bo Persson

  3. 16

    Creo que el más cercano a la norma viene a decir que no es un comportamiento indefinido es donde se define el comportamiento de una unión que contiene común de secuencia inicial (C99, §6.5.2.3/5):

    Una especial garantía se hace con el fin de simplificar el uso de los sindicatos: si un sindicato contiene
    varias estructuras que comparten la secuencia inicial (ver más abajo), y si la unión
    objeto contiene en la actualidad una de estas estructuras, se permite inspeccionar el común
    parte inicial de cualquiera de ellos en cualquier lugar que una declaración de tipo completo de la unión
    visible. Dos estructuras comparten la secuencia inicial si los miembros tienen
    tipos compatibles (y, para los bits de los campos, los mismos anchos) para una secuencia de uno o más
    miembros iniciales.

    C++11 da igual los requisitos/permiso en §9.2/19:

    Si una norma de diseño de la unión contiene dos o más estándar de diseño de estructuras que comparten la secuencia inicial,
    y si el estándar de diseño de la unión de objeto contiene en la actualidad una de estas estándar de diseño de estructuras, es permitido
    para inspeccionar el común de la parte inicial de cualquiera de ellos. Dos estándar de diseño de estructuras comparten un común inicial
    secuencia de si los miembros han de diseño de tipos compatibles y, o bien ni el miembro es un poco de campo o
    ambos son de los bits de los campos con el mismo ancho para una secuencia de uno o más miembros iniciales.

    Aunque ninguno de los dos estados directamente, ambos llevan una fuerte implicación de que «la inspección» (lectura) de un miembro es «permitida» sólo si 1) es (parte de) el miembro más recientemente escrito, o 2) es parte de una misma secuencia inicial.

    Que no es una declaración directa que hacer lo contrario es un comportamiento indefinido, pero es el más cercano de lo que soy consciente.

    Para hacer eso, usted necesita saber qué «diseño de tipos compatibles» son para C++, o «tipos compatibles» son para C.
    Sí y no. Usted necesita para tratar con aquellos cuando/si usted quiere estar seguro de si algo cae dentro de esta excepción; pero la verdadera pregunta aquí es si algo que claramente queda fuera de la excepción de verdad da la UB. Creo que es lo suficientemente fuerte como implícita aquí para hacer la intención clara, pero yo no creo que sea cada vez declarada directamente.
    Este «común de la secuencia inicial de» cosa podría haber salvado 2 o 3 de mis proyectos a partir de la Reescritura de Reciclaje. No lo podía creer cuando leí por primera vez acerca de la mayoría descriptivo de los usos de unions siendo indefinido, ya que me habían dado la impresión de un blog en particular que esto estaba bien, y se construyeron varias estructuras de gran tamaño y proyectos en torno a ella. Ahora yo creo yo podría estar bien después de todo, ya que mi unions contienen clases que tener los mismos tipos en la parte delantera
    Creo que estaban haciendo alusión a la misma pregunta que yo: ¿y si nuestra union contiene por ejemplo una uint8_t y un class Something { uint8_t myByte; [...] }; – supongo que esta condición también se aplica aquí, pero está redactado de una forma muy deliberada para permitir sólo para structs. Por suerte ya estoy usando los de raw primitivas :O
    El estándar de C al menos el tipo de cubiertas que se pregunta: «Un puntero a un objeto de estructura, convenientemente convertido, puntos a su primer miembro (o si el miembro es un poco de campo, luego a la unidad en la que reside), y viceversa.»

    OriginalEl autor Jerry Coffin

  4. 10

    Algo que aún no se ha mencionado por disposición de respuestas es la nota de pie de página 37 en el párrafo 21 de la sección 6.2.5:

    Tenga en cuenta que tipo agregado no incluye el tipo de unión ya que un objeto
    con tipo de unión sólo puede contener un miembro a la vez.

    Este requisito parece claramente implica que no debes escribir en un miembro y leer en el otro. En este caso podría ser un comportamiento indefinido por falta de especificación.

    Ese es un buen punto.
    Muchas de las implementaciones de documento, sus formatos de almacenamiento y el diseño de las reglas. Tal especificación en muchos casos implican lo que el efecto de la lectura de almacenamiento de un tipo y la escritura como otro sería en ausencia de reglas diciendo que los compiladores no tienen que usar sus define el formato de almacenamiento, excepto cuando las cosas se leen y se escriben mediante punteros de un tipo de carácter.

    OriginalEl autor mpu

  5. -1

    Yo también a explicar esto con un ejemplo.

    supongamos que tenemos la unión siguiente:

    union A{
       int x;
       short y[2];
    };

    Yo también asumir que sizeof(int) da 4, y que sizeof(short) da 2.


    cuando usted escribe union A a = {10} que bien crear una nueva var de tipo a en poner en ella el valor de 10.

    su memoria debe tener el siguiente aspecto: (recuerde que todos los miembros de la unión de obtener la misma ubicación)

     | x | 
    | y[0] | [1] | 
    ----------------------------------------- 
    a->|0000 0000/0000 0000/0000 0000/0000 1010| 
    ----------------------------------------- 
    

    como se puede ver, el valor de una.x es 10, el valor de una.yUno es de 10, y el valor de una.y[0] es 0.

    ahora, ¿qué sucede si hago esto?

    a.y[0] = 37;

    nuestra memoria tendrá este aspecto:

     | x | 
    | y[0] | [1] | 
    ----------------------------------------- 
    a->|0000 0000/0010 0101/0000 0000/0000 1010| 
    ----------------------------------------- 
    

    esto convertirá el valor de una.x a 2424842 (en decimal).

    ahora, si la unión tiene un float o double, su mapa de memoria así ser más de un lío, porque de la forma de almacenar los números exactos.
    más información se puede poner en aquí.

    🙂 Esto no es lo que pregunté. Sé lo que ocurre internamente. Yo sé que funciona. Me preguntó si es en el estándar.

    OriginalEl autor elyashiv

Dejar respuesta

Please enter your comment!
Please enter your name here