Diferencia entre una estructura y una unión

412

¿Hay 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 de miembros más grande. ¿Hay alguna otra diferencia de nivel de sistema operativo?

gagneet
fuente

Respuestas:

678

Con una unión, se supone que solo 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 uno de varios tipos. Una estructura, por otro lado, tiene una ubicación de memoria separada para cada uno de sus elementos y todos pueden usarse a la vez.

Para dar un ejemplo concreto de su uso, estaba trabajando en un intérprete de Scheme hace un tiempo y esencialmente superponía los tipos de datos de Scheme en 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 qué configuración de xb a 'c' cambia el valor de xa, técnicamente hablando no está definido. En la mayoría de las máquinas modernas, un carácter es 1 byte y un int es 4 bytes, por lo que dar 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 ponemos un número mayor para xa, verá 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ó al 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 donde sobrescribiría el 0xDE porque el orden de los bytes que componen el int se invirtió. Por lo tanto, al escribir un programa, nunca debe 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 endianness .

Kyle Cronin
fuente
1
usando este ejemplo, en unión, si xb = 'c', ¿qué se almacena en xa? ¿Es el número de referencia del personaje?
kylex
1
con suerte eso explica con más detalle lo que se almacena en xa cuando configura xb
Kyle Cronin
1
@KyleCronin Creo que lo entiendo. En su caso, tiene un grupo de tipos, sabiendo que solo necesitará usar uno, pero no sabe cuál hasta el tiempo de ejecución, por lo que la unión le permite hacerlo. Gracias
user12345613
2
@ user12345613 las uniones se pueden usar como una especie de clase base para estructuras. Puede emular una jerarquía OO utilizando uniones de estructuras
Morten Jensen el
1
El orden de bytes de @Lazar en tipos de varios bytes depende de la endianidad. Sugiero leer el artículo de Wikipedia al respecto.
Kyle Cronin
83

Aquí está la respuesta corta: una estructura es una estructura de registro: cada elemento en la estructura asigna 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 forzar al compilador a rellenar la estructura).

Por otra parte,

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

asigna un trozo de memoria y le da cuatro alias. Entonces sizeof(union foobarbazquux_u) ≥ max((sizeof(int),sizeof(long),sizeof(double),sizeof(long double)), de nuevo con la posibilidad de alguna adición para las alineaciones.

Charlie Martin
fuente
53

¿Hay 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 en cualquier momento.

Los sindicatos son en gran medida un detalle de bajo nivel basado en la herencia de C como lenguaje de programación del sistema, donde las ubicaciones de almacenamiento "superpuestas" a veces se usan de esta manera. A veces puede usar uniones para guardar memoria donde tiene una estructura de datos donde solo se guardará uno de varios tipos a la vez.

En general, al sistema operativo no le importa ni conoce las estructuras y las 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 en cualquier momento.

cygil
fuente
1
Sí. ¡Esto explica bien un caso de uso!
Gedeón
1
supongamos que tiene un packetheader ph;¿cómo accede al número de solicitud? ph.request.requestnumber?
justin
¡La mejor explicación! Gracias.
84RR1573R
39

Como ya dijo en su pregunta, la principal diferencia entre uniony structes que los unionmiembros se superponen entre sí para que el tamaño de una unión sea el mismo, mientras que los structmiembros se disponen uno tras otro (con un relleno opcional en el medio). Además, una unión es lo suficientemente grande como para contener a todos sus miembros y tiene una alineación que se adapta a todos sus miembros. Entonces, digamos intque solo se puede almacenar en direcciones de 2 bytes y tiene 2 bytes de ancho, y el largo solo se puede almacenar en direcciones de 4 bytes y tiene 4 bytes de longitud. La siguiente unión

union test {
    int a;
    long b;
}; 

podría tener un sizeof 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 solo cambia el valor del miembro escrito. 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 como 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 que importar si un programa de usuario escribe en una unión o en una estructura. Esto en realidad es solo un problema del compilador.

Otra propiedad importante de union y struct es que permiten que un puntero hacia 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*, apuntará a su primer miembro a, en realidad. Lo mismo es cierto para una unión también. 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á señalar un int y 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 el estándar C99:

Un objeto tendrá acceso a su valor almacenado solo mediante una expresión lvalue que tenga uno de los siguientes tipos:

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

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

Johannes Schaub - litb
fuente
18

A uniones útil en un par de escenarios. unionpuede ser una herramienta para la manipulación de muy bajo nivel, como escribir controladores de dispositivo para un núcleo.

Un ejemplo de eso es diseccionar un floatnúmero usando uniona structcon campos de bits y a float. Guardo un número en el float, y luego puedo acceder a partes particulares del a floattravés de eso struct. El ejemplo muestra cómo unionse usa para tener diferentes ángulos para ver los datos.

#include <stdio.h>                                                                                                                                       

union foo {
    struct float_guts {
        unsigned int fraction : 23;
        unsigned int exponent : 8;
        unsigned int sign     : 1;
    } fg;
    float f;
};

void print_float(float f) {
    union foo ff;
    ff.f = f;
    printf("%f: %d 0x%X 0x%X\n", f, ff.fg.sign, ff.fg.exponent, ff.fg.fraction);

}

int main(){
    print_float(0.15625);
    return 0;
}

Eche un vistazo a la descripción de precisión única en wikipedia. Usé el ejemplo y el número mágico 0.15625 a partir de ahí.


unionTambién se puede utilizar para implementar un tipo de datos algebraicos que tiene múltiples alternativas. Encontré un ejemplo de eso en el libro "Real World Haskell" de O'Sullivan, Stewart y Goerzen. Compruébelo en la sección La unión discriminada .

¡Salud!

Krzysztof Voss
fuente
11

" union " y " struct " son construcciones del lenguaje C. Hablar de una diferencia de "nivel de sistema operativo" entre ellos es inapropiado, ya que es el compilador el que produce un código diferente si usa una u otra palabra clave.

Gabriele D'Antona
fuente
11

No técnicamente hablando significa:

Asunción: silla = bloque de memoria, personas = variable

Estructura : si hay 3 personas, pueden sentarse en una silla de su tamaño correspondiente.

Unión : si hay 3 personas, solo una silla estará allí para sentarse, todos deben usar la misma silla cuando quieran sentarse.

Técnicamente hablando significa:

El programa mencionado a continuación profundiza en la estructura y la unión.

struct MAIN_STRUCT
{
UINT64 bufferaddr;   
union {
    UINT32 data;
    struct INNER_STRUCT{
        UINT16 length;  
        UINT8 cso;  
        UINT8 cmd;  
           } flags;
     } data1;
};

MAIN_STRUCT total size = sizeof (UINT64) para bufferaddr + sizeof (UNIT32) para union + 32 bit para relleno (depende de la arquitectura del procesador) = 128 bits. Para estructurar todos los miembros obtienen el bloque de memoria contiguamente.

Union obtiene un bloque de memoria del miembro de tamaño máximo (Aquí es de 32 bits). Dentro de la unión se encuentra una estructura más (INNER_STRUCT), sus miembros obtienen un bloque de memoria de tamaño total de 32 bits (16 + 8 + 8). En unión, se puede acceder al miembro INNER_STRUCT (32 bits) o a los datos (32 bits).

skanda93
fuente
Gran explicación ¡Salud!
Prem
11

Sí, la principal diferencia entre struct y union es la misma que usted indicó. Struct usa toda la memoria de su miembro y union usa el espacio de memoria de miembros más grande.

Pero toda la diferencia radica en la necesidad de uso de la memoria. El mejor uso de la unión se puede ver en los procesos de Unix donde utilizamos señales. como un proceso puede actuar sobre una sola señal a la vez. Entonces la declaración general será:

union SIGSELECT
{
  SIGNAL_1 signal1;
  SIGNAL_2 signal2;
  .....
};

En este caso, el proceso utiliza solo la memoria más alta de todas las señales. pero si usa struct en este caso, el uso de memoria será la suma de todas las señales. Hace mucha diferencia.

Para resumir, se debe seleccionar Unión si sabe que accede a cualquiera de los miembros a la vez.

Ravi Kanth
fuente
10

Lo tienes, eso es todo. Pero entonces, básicamente, ¿qué sentido tienen los sindicatos?

Puede poner en la misma ubicación contenido de diferentes tipos. Debe saber el tipo de lo que ha almacenado en la unión (tan a menudo lo coloca en una structetiqueta de tipo ...).

¿Porque es esto importante? Realmente no para ganar espacio. Sí, puedes ganar algunos bits o rellenar, pero ese ya no es el punto principal.

Es por seguridad de tipos, le permite hacer algún tipo de 'tipeo dinámico': el compilador sabe que su contenido puede tener diferentes significados y el significado preciso de cómo depende su interpretación en tiempo de ejecución. Si tiene un puntero que puede apuntar a diferentes tipos, DEBE usar una unión, de lo contrario, el código puede ser incorrecto debido a problemas de alias (el compilador se dice a sí mismo "oh, solo este puntero puede apuntar a este tipo, por lo que puedo optimizar fuera de esos accesos ... ", y pueden pasar cosas malas).

Piotr Lesnicki
fuente
9

Una estructura asigna el tamaño total de todos los elementos que contiene.

Una unión solo asigna tanta memoria como lo requiera su miembro más grande.

CMS
fuente
2
Es posible que también desee agregar que los miembros del sindicato se "superponen" entre sí, ya que todos comienzan en la dirección inicial de la "estructura" del sindicato asignada.
Jim Buck
4

¿Cuál es la diferencia entre estructura y unión?

La respuesta abreviada es: la deferencia está en la asignación de memoria. Explicación: En la estructura, se creará espacio de memoria para todos los miembros dentro de la estructura. En la memoria de la unión, el espacio se creará solo para un miembro que necesite el mayor espacio de memoria. Considere el siguiente código:

struct s_tag
{
   int a; 
   long int b;
} x;

union u_tag
{
   int a; 
   long int b;
} y;

Aquí hay dos miembros dentro de struct y union: int y long int. El espacio de memoria para int es: 4 byte y el espacio de memoria para int largo es: 8 en el sistema operativo de 32 bits.

Entonces, para la estructura 4 + 8 = se crearán 12 bytes, mientras que se crearán 8 bytes para la unión

Ejemplo de código:

#include<stdio.h>
struct s_tag
{
  int a;
  long int b;
} x;
union u_tag
{
     int a;
     long int b;
} y;
int main()
{
    printf("Memory allocation for structure = %d", sizeof(x));
    printf("\nMemory allocation for union = %d", sizeof(y));
    return 0;
}

Ref: http://www.codingpractise.com/home/c-programming/structure-and-union/

Abdus Sattar Bhuiyan
fuente
3

Los usos de las Uniones sindicales se usan con frecuencia cuando se necesitan conversaciones de tipo especializado. Para tener una idea de la utilidad de la unión. La biblioteca estándar de c / c no define ninguna función específicamente diseñada para escribir enteros cortos en un archivo. El uso de fwrite () incurre en una sobrecarga excesiva para una operación simple. Sin embargo, utilizando una unión, puede crear fácilmente una función que escribe binario de un entero corto en un archivo de un byte a la vez. Supongo que los enteros cortos son de 2 bytes de largo

EL EJEMPLO:

#include<stdio.h>
union pw {
short int i;
char ch[2];
};
int putw(short int num, FILE *fp);
int main (void)
{
FILE *fp;
fp fopen("test.tmp", "wb ");
putw(1000, fp); /* write the value 1000 as an integer*/
fclose(fp);
return 0;
}
int putw(short int num, FILE *fp)
{
pw word;
word.i = num;
putc(word.c[0] , fp);
return putc(word.c[1] , fp);
}    

aunque putw () llamé con un entero corto, era posible usar putc () y fwrite (). Pero quería mostrar un ejemplo para dominar cómo se puede usar una unión

Ahmed
fuente
3

la estructura es una colección de diferentes tipos de datos donde pueden residir diferentes tipos de datos y cada uno obtiene su propio bloque de memoria

Por lo general, usamos union cuando nos aseguramos de que solo se usará una de las variables a la vez y desea utilizar completamente la memoria actual porque solo obtiene un bloque de memoria que es igual al tipo más grande.

struct emp
{
    char x;//1 byte
    float y; //4 byte
} e;

memoria total se obtiene => 5 bytes

union emp
{
    char x;//1 byte
    float y; //4 byte
} e;

memoria total se obtiene = 4 bytes

Anurag Bhakuni
fuente
2

Los sindicatos son útiles al escribir una función de orden de bytes que se proporciona a continuación. No es posible con estructuras.

int main(int argc, char **argv) {
    union {
        short   s;
        char    c[sizeof(short)];
    } un;

    un.s = 0x0102;

    if (sizeof(short) == 2) {
        if (un.c[0] == 1 && un.c[1] == 2)
            printf("big-endian\n");
        else if (un.c[0] == 2 && un.c[1] == 1)
            printf("little-endian\n");
        else
            printf("unknown\n");
    } else
        printf("sizeof(short) = %d\n", sizeof(short));

    exit(0);
}
// Program from Unix Network Programming Vol. 1 by Stevens.
Aniket Suryavanshi
fuente
1

Una Unión es diferente de una estructura ya que una Unión se repite sobre las demás: redefine la misma memoria mientras que la estructura se define una tras otra sin superposiciones o redefiniciones.

Dennis Ng
fuente