tamaño de una unión en C / C ++

¿Cuál es el tamaño de la unión en C / C ++? ¿Es el tamaño del tipo de datos más grande dentro de él? Si es así, ¿cómo calcula el comstackdor cómo mover el puntero de stack si uno de los tipos de datos más pequeños de la unión está activo?

El Estándar responde todas las preguntas en la sección 9.5 del estándar C ++, o en la sección 6.5.2.3 párrafo 5 del estándar C99 (o el párrafo 6 del estándar C11):

En una unión, como máximo uno de los miembros de los datos puede estar activo en cualquier momento, es decir, el valor de, como máximo, uno de los miembros de los datos puede almacenarse en una unión en cualquier momento. [Nota: se hace una garantía especial para simplificar el uso de uniones: si un POD-union contiene varias POD-struct que comparten una secuencia inicial común (9.2), y si un objeto de este tipo de POD-union contiene uno de las estructuras POD, se permite inspeccionar la secuencia inicial común de cualquiera de los miembros POD-struct; ver 9.2. ] El tamaño de una unión es suficiente para contener el mayor de sus miembros de datos. Cada miembro de datos se asigna como si fuera el único miembro de una estructura.

Eso significa que cada miembro comparte la misma región de memoria. Hay como máximo un miembro activo, pero no puede encontrar cuál. Tendrá que almacenar esa información sobre el miembro activo actualmente en otro lugar. El almacenamiento de dicha bandera además de la unión (por ejemplo, tener una estructura con un número entero como indicador de tipo y una unión como almacén de datos) le dará una llamada “unión discriminada”: una unión que sabe de qué tipo actualmente es el “activo”.

Un uso común es en lexers, donde puedes tener diferentes tokens, pero dependiendo del token, tienes diferentes informaciones para almacenar (poner una line en cada estructura para mostrar qué secuencia inicial es común):

 struct tokeni { int token; /* type tag */ union { struct { int line; } noVal; struct { int line; int val; } intVal; struct { int line; struct string val; } stringVal; } data; }; 

El Estándar le permite acceder a la line de cada miembro, porque esa es la secuencia inicial común de cada uno.

Existen extensiones de comstackdor que permiten acceder a todos los miembros sin tener en cuenta cuál tiene actualmente su valor almacenado. Eso permite la reinterpretación eficiente de los bits almacenados con diferentes tipos entre cada uno de los miembros. Por ejemplo, lo siguiente se puede usar para diseccionar una variable flotante en 2 cortos sin firmar:

 union float_cast { unsigned short s[2]; float f; }; 

Eso puede ser bastante útil al escribir código de bajo nivel. Si el comstackdor no admite esa extensión, pero lo hace de todos modos, escriba el código cuyos resultados no están definidos. Así que asegúrese de que su comstackdor tenga soporte si usa ese truco.

Una union siempre ocupa tanto espacio como el miembro más grande. No importa lo que está actualmente en uso.

 union { short x; int y; long long z; } 

Un ejemplo de la union anterior siempre llevará al menos un long long de almacenamiento.

Nota al pie : tal como señala Stefano , el espacio real que cualquier tipo ( union , struct , class ) tomará dependerá de otros problemas, como la alineación por parte del comstackdor. No revisé esto por simplicidad ya que solo quería decir que un sindicato tiene en cuenta el artículo más grande. Es importante saber que el tamaño real depende de la alineación .

Depende del comstackdor y de las opciones.

 int main() { union { char all[13]; int foo; } record; printf("%d\n",sizeof(record.all)); printf("%d\n",sizeof(record.foo)); printf("%d\n",sizeof(record)); } 

Esto produce:

13 4 16

Si recuerdo correctamente, depende de la alineación que el comstackdor coloca en el espacio asignado. Entonces, a menos que use alguna opción especial, el comstackdor colocará relleno en su espacio sindical.

editar: con gcc necesitas usar una directiva pragma

 int main() { #pragma pack(push, 1) union { char all[13]; int foo; } record; #pragma pack(pop) printf("%d\n",sizeof(record.all)); printf("%d\n",sizeof(record.foo)); printf("%d\n",sizeof(record)); } 

esta salida

13 4 13

También puede verlo desde el desassembly (eliminado algunos printf, para mayor claridad)

  0x00001fd2 
: push %ebp | 0x00001fd2
: push %ebp 0x00001fd3
: mov %esp,%ebp | 0x00001fd3
: mov %esp,%ebp 0x00001fd5
: push %ebx | 0x00001fd5
: push %ebx 0x00001fd6
: sub $0x24,%esp | 0x00001fd6
: sub $0x24,%esp 0x00001fd9
: call 0x1fde
| 0x00001fd9
: call 0x1fde
0x00001fde
: pop %ebx | 0x00001fde
: pop %ebx 0x00001fdf
: movl $0xd,0x4(%esp) | 0x00001fdf
: movl $0x10,0x4(%esp) 0x00001fe7
: lea 0x1d(%ebx),%eax | 0x00001fe7
: lea 0x1d(%ebx),%eax 0x00001fed
: mov %eax,(%esp) | 0x00001fed
: mov %eax,(%esp) 0x00001ff0
: call 0x3005 | 0x00001ff0
: call 0x3005 0x00001ff5
: add $0x24,%esp | 0x00001ff5
: add $0x24,%esp 0x00001ff8
: pop %ebx | 0x00001ff8
: pop %ebx 0x00001ff9
: leave | 0x00001ff9
: leave 0x00001ffa
: ret | 0x00001ffa
: ret

Donde la única diferencia está en main + 13, donde el comstackdor asigna en la stack 0xd en lugar de 0x10

No hay ninguna noción de tipo de datos activo para una unión. Usted es libre de leer y escribir cualquier ‘miembro’ de la unión: esto depende de usted para interpretar lo que obtiene.

Por lo tanto, el tamaño de una unión siempre es el tamaño de su tipo de datos más grande.

El tamaño será al menos el del tipo de composición más grande. No hay concepto de tipo “activo”.

Realmente debería ver una unión como un contenedor para el tipo de datos más grande dentro de ella combinado con un acceso directo para un elenco. Cuando usa uno de los miembros más pequeños, el espacio no utilizado aún está allí, pero simplemente no se utiliza.

A menudo se ve que esto se usa en combinación con las llamadas a ioctl () en Unix, todas las llamadas ioctl () pasarán la misma estructura, que contiene una unión de todas las respuestas posibles. Por ejemplo, este ejemplo proviene de /usr/include/linux/if.h y esta estructura se usa en ioctl () para configurar / consultar el estado de una interfaz ethernet, los parámetros de solicitud definen qué parte de la unión está realmente en uso. :

 struct ifreq { #define IFHWADDRLEN 6 union { char ifrn_name[IFNAMSIZ]; /* if name, eg "en0" */ } ifr_ifrn; union { struct sockaddr ifru_addr; struct sockaddr ifru_dstaddr; struct sockaddr ifru_broadaddr; struct sockaddr ifru_netmask; struct sockaddr ifru_hwaddr; short ifru_flags; int ifru_ivalue; int ifru_mtu; struct ifmap ifru_map; char ifru_slave[IFNAMSIZ]; /* Just fits the size */ char ifru_newname[IFNAMSIZ]; void * ifru_data; struct if_settings ifru_settings; } ifr_ifru; }; 
  1. El tamaño del miembro más grande.

  2. Esta es la razón por la cual las uniones generalmente tienen sentido dentro de una estructura que tiene una bandera que indica cuál es el miembro “activo”.

Ejemplo:

 struct ONE_OF_MANY { enum FLAG { FLAG_SHORT, FLAG_INT, FLAG_LONG_LONG } flag; union { short x; int y; long long z; }; }; 

¿Cuál es el tamaño de la unión en C / C ++? ¿Es el tamaño del tipo de datos más grande dentro de él?

, el tamaño de la unión es del tamaño de su miembro más grande.

Por ejemplo :

 #include union un { char c; int i; float f; double d; }; int main() { union un u1; printf("sizeof union u1 : %ld\n",sizeof(u1)); return 0; } 

Salida:

 sizeof union u1 : 8 sizeof double d : 8 

Aquí el miembro más grande es double . Ambos tienen talla 8 . Entonces, como te dijo correctamente sizeof , el tamaño de la unión es de hecho 8 .

¿Cómo calcula el comstackdor cómo mover el puntero de stack si uno de los tipos de datos más pequeños de la unión está activo?

Se maneja internamente por el comstackdor. Supongamos que estamos accediendo a uno de los miembros de datos de la unión, entonces no podemos acceder a otros miembros de datos ya que podemos acceder a un único miembro de datos de unión porque cada miembro de datos comparte la misma memoria. Al usar Union podemos ahorrar mucho espacio valioso.