Diferencia entre una estructura y una unión

Resuelto gagneet asked hace 15 años • 17 respuestas

¿Existe algún buen ejemplo para dar la diferencia entre a structy a union? Básicamente, sé que structusa toda la memoria de su miembro y unionusa el espacio de memoria más grande de los miembros. ¿Existe alguna otra diferencia en el nivel del sistema operativo?

gagneet avatar Dec 07 '08 00:12 gagneet
Aceptado

Con una unión, se supone que sólo debes usar uno de los elementos, porque todos están almacenados en el mismo lugar. Esto lo hace útil cuando desea almacenar algo que podría ser de varios tipos. Una estructura, por otro lado, tiene una ubicación de memoria separada para cada uno de sus elementos y todos se pueden usar a la vez.

Para dar un ejemplo concreto de su uso, hace un tiempo estuve trabajando en un intérprete de Scheme y básicamente estaba superponiendo los tipos de datos de Scheme sobre los tipos de datos de C. Esto implicó almacenar en una estructura una enumeración que indica el tipo de valor y una unión para almacenar ese valor.

union foo {
  int a;   // can't use both a and b at once
  char b;
} foo;

struct bar {
  int a;   // can use both a and b simultaneously
  char b;
} bar;

union foo x;
x.a = 3; // OK
x.b = 'c'; // NO! this affects the value of x.a!

struct bar y;
y.a = 3; // OK
y.b = 'c'; // OK

editar: Si se pregunta a qué configuración xb a 'c' cambia el valor de xa, técnicamente hablando no está definido. En la mayoría de las máquinas modernas, un char tiene 1 byte y un int tiene 4 bytes, por lo que darle a xb el valor 'c' también le da al primer byte de xa ese mismo valor:

union foo x;
x.a = 3;
x.b = 'c';
printf("%i, %i\n", x.a, x.b);

huellas dactilares

99, 99

¿Por qué los dos valores son iguales? Debido a que los últimos 3 bytes de int 3 son todos cero, también se lee como 99. Si ingresamos un número mayor para xa, verás que este no es siempre el caso:

union foo x;
x.a = 387439;
x.b = 'c';
printf("%i, %i\n", x.a, x.b);

huellas dactilares

387427, 99

Para ver más de cerca los valores de memoria reales, configuremos e imprimamos los valores en hexadecimal:

union foo x;
x.a = 0xDEADBEEF;
x.b = 0x22;
printf("%x, %x\n", x.a, x.b);

huellas dactilares

deadbe22, 22

Puede ver claramente dónde el 0x22 sobrescribió el 0xEF.

PERO

En C, el orden de los bytes en un int no está definido. Este programa sobrescribió el 0xEF con 0x22 en mi Mac, pero hay otras plataformas en las que sobrescribiría el 0xDE porque el orden de los bytes que componen el int se invirtió. Por lo tanto, al escribir un programa, nunca debes confiar en el comportamiento de sobrescribir datos específicos en una unión porque no es portátil.

Para obtener más información sobre el orden de los bytes, consulte endianidad .

Kyle Cronin avatar Dec 06 '2008 17:12 Kyle Cronin

Aquí está la respuesta corta: una estructura es una estructura de registro: cada elemento de la estructura asigna un nuevo espacio. Entonces, una estructura como

struct foobarbazquux_t {
    int foo;
    long bar;
    double baz; 
    long double quux;
}

Asigna al menos (sizeof(int)+sizeof(long)+sizeof(double)+sizeof(long double))bytes en memoria para cada instancia. ("Al menos" porque las restricciones de alineación de la arquitectura pueden obligar al compilador a rellenar la estructura).

Por otro lado,

union foobarbazquux_u {
    int foo;
    long bar;
    double baz; 
    long double quux;
}

Asigna un fragmento de memoria y le asigna cuatro alias. Así sizeof(union foobarbazquux_u) ≥ max((sizeof(int),sizeof(long),sizeof(double),sizeof(long double)), nuevamente con la posibilidad de alguna incorporación para alineaciones.

Charlie Martin avatar Dec 06 '2008 18:12 Charlie Martin

¿Existe algún buen ejemplo para dar la diferencia entre una 'estructura' y una 'unión'?

Un protocolo de comunicaciones imaginario

struct packetheader {
   int sourceaddress;
   int destaddress;
   int messagetype;
   union request {
       char fourcc[4];
       int requestnumber;
   };
};

En este protocolo imaginario, se ha especificado que, según el "tipo de mensaje", la siguiente ubicación en el encabezado será un número de solicitud o un código de cuatro caracteres, pero no ambos. En resumen, las uniones permiten que la misma ubicación de almacenamiento represente más de un tipo de datos, donde se garantiza que solo querrá almacenar uno de los tipos de datos a la vez.

Las uniones son en gran medida un detalle de bajo nivel basado en la herencia de C como lenguaje de programación de sistemas, donde a veces se utilizan de esta manera ubicaciones de almacenamiento "superpuestas". A veces puedes usar uniones para ahorrar memoria cuando tienes una estructura de datos donde solo se guardará uno de varios tipos a la vez.

En general, al sistema operativo no le importan ni conocen las estructuras y uniones; ambas son simplemente bloques de memoria para él. Una estructura es un bloque de memoria que almacena varios objetos de datos, donde esos objetos no se superponen. Una unión es un bloque de memoria que almacena varios objetos de datos, pero solo tiene almacenamiento para el mayor de ellos y, por lo tanto, solo puede almacenar uno de los objetos de datos a la vez.

cygil avatar Dec 08 '2008 01:12 cygil

Como ya indicó en su pregunta, la principal diferencia entre uniony structes que unionlos miembros se superponen en la memoria de cada uno de modo que el tamaño de una unión es el único, mientras que structlos miembros se colocan uno después del otro (con relleno opcional en el medio). Además, un sindicato es lo suficientemente grande como para contener a todos sus miembros y tener una alineación que se adapte a todos sus miembros. Entonces, digamos que intsolo se puede almacenar en direcciones de 2 bytes y tiene 2 bytes de ancho, y long solo se puede almacenar en direcciones de 4 bytes y tiene 4 bytes de largo. La siguiente unión

union test {
    int a;
    long b;
}; 

podría tener un valor sizeofde 4 y un requisito de alineación de 4. Tanto una unión como una estructura pueden tener relleno al final, pero no al principio. Escribir en una estructura cambia solo el valor del miembro en el que se escribe. Escribir a un miembro de un sindicato invalidará el valor de todos los demás miembros. No puede acceder a ellos si no les ha escrito antes; de lo contrario, el comportamiento no está definido. GCC proporciona una extensión que realmente puede leer de los miembros de un sindicato, aunque no les haya escrito más recientemente. Para un sistema operativo, no tiene por qué importar si un programa de usuario escribe en una unión o en una estructura. En realidad, esto es sólo una cuestión del compilador.

Otra propiedad importante de union y struct es que permiten que un puntero a ellos pueda apuntar a tipos de cualquiera de sus miembros . Entonces lo siguiente es válido:

struct test {
    int a;
    double b;
} * some_test_pointer;

some_test_pointer puede apuntar a int*o double*. Si envía una dirección de tipo testa int*, en realidad apuntará a su primer miembro, a. Lo mismo se aplica también a un sindicato. Por lo tanto, debido a que una unión siempre tendrá la alineación correcta, puede usar una unión para hacer que apuntar a algún tipo sea válido:

union a {
    int a;
    double b;
};

Esa unión en realidad podrá apuntar a un int y a un doble:

union a * v = (union a*)some_int_pointer;
*some_int_pointer = 5;
v->a = 10;
return *some_int_pointer;    

es realmente válido, como lo establece la norma C99:

A un objeto se le podrá acceder a su valor almacenado únicamente mediante una expresión lvalue que tenga uno de los siguientes tipos:

  • un tipo compatible con el tipo efectivo del objeto
  • ...
  • un tipo de agregado o unión que incluye uno de los tipos antes mencionados entre sus miembros

El compilador no optimizará ya v->a = 10;que podría afectar el valor de *some_int_pointer(y la función regresará 10en lugar de 5).

Johannes Schaub - litb avatar Dec 06 '2008 18:12 Johannes Schaub - litb