Uniones (Unions) en C++: Reutilización de Memoria¶
Índice¶
- Introducción
- Parte 1: Concepto de Unión
- Parte 2: Unión vs Estructura (Memoria)
- Parte 3: Ejemplo Básico con Unión
- Parte 4: Análisis en Ensamblador
- Parte 5: Caso Avanzado - Estructura dentro de Unión
- Parte 6: Uniones Anónimas (Struct inline + Array)
- Parte 7: Inicialización de Uniones (primer miembro)
- Parte 8: Discriminadores (uniones etiquetadas)
- Parte 9: Reconocimiento en Reversing
- Resumen y Casos de Uso
Introducción¶
Las uniones (unions) son un tipo especial de estructura en C++ que permite almacenar diferentes tipos de datos en la misma posición de memoria, aunque evidentemente no simultáneamente.
Diferencia clave con estructuras:
- Estructura: Cada campo tiene su propio espacio de memoria.
- Unión: Todos los campos comparten el mismo espacio de memoria.
Aplicaciones en reversing:
- Detectar reutilización de variables para diferentes tipos.
- Entender acceso a datos de múltiples formas (p.ej., bytes individuales vs entero completo).
- Reconocer almacenamiento temporal de valores.
Parte 1: Concepto de Unión¶
Definición¶
Una unión reserva espacio para el campo más grande, y todos los campos se superponen en la misma dirección de memoria.
Sintaxis:
Ejemplo Conceptual¶
Tamaño de la unión: sizeof(Ejemplo) = 8 (el mayor campo es double).
Representación en memoria:
Offset: 0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
a: | int (4 bytes) | (sin usar) |
b: |char| (sin usar) |
c: | double (8 bytes) |
+---+---+---+---+---+---+---+---+
Todos los campos comparten la misma dirección inicial.
Comportamiento¶
union Ejemplo {
int a;
char b;
double c;
} ejemplo;
ejemplo.a = 100; // Escribe 100 en los primeros 4 bytes
ejemplo.b = 'A'; // Escribe 0x41 en el PRIMER byte (sobreescribe parte de a)
ejemplo.c = 10.32; // Escribe 10.32 en los 8 bytes (sobreescribe todo)
Importante: Escribir en un campo modifica los otros campos, porque comparten memoria.
Parte 2: Unión vs Estructura (Memoria)¶
Comparación de Tamaño¶
Estructura (campos separados)¶
struct EstructuraEjemplo {
int a; // Offset 0-3 (4 bytes)
char b; // Offset 4 (1 byte)
double c; // Offset 8-15 (8 bytes, alineado)
};
sizeof(EstructuraEjemplo) = 16 bytes (con padding)
Memoria:
Offset: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| int (a) |chr| padding | double (c) |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Unión (campos superpuestos)¶
union UnionEjemplo {
int a; // Offset 0-3 (4 bytes)
char b; // Offset 0 (1 byte)
double c; // Offset 0-7 (8 bytes)
};
sizeof(UnionEjemplo) = 8 bytes (solo el mayor)
Memoria:
Offset: 0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
a: | int | (sin usar) |
b: |chr| (sin usar) |
c: | double |
+---+---+---+---+---+---+---+---+
Tabla Comparativa¶
| Aspecto | Estructura | Unión |
|---|---|---|
| Tamaño | Suma de todos los campos (+ padding) | Tamaño del campo más grande |
| Posición de campos | Cada campo en offset diferente | Todos en offset 0 |
| Independencia | Modificar un campo NO afecta otros | Modificar un campo AFECTA otros |
| Uso | Datos permanentes, diferentes campos coexisten | Datos temporales, un solo campo activo a la vez |
Parte 3: Ejemplo Básico con Unión¶
Código Completo¶
#include <iostream>
using namespace std;
union Ejemplo {
int a;
char b;
double c;
} ejemplo;
int main() {
// Asignar 100 a 'a'
ejemplo.a = 100;
cout << "a = " << ejemplo.a << endl;
// Asignar 'A' a 'b' (modifica 'a')
ejemplo.b = 'A';
cout << "b = " << ejemplo.b << endl;
// Asignar 10.32 a 'c' (modifica todo)
ejemplo.c = 10.32;
cout << "c = " << ejemplo.c << endl;
// Imprimir direcciones (todas iguales)
cout << "Dirección de a: " << (void*)&ejemplo.a << endl;
cout << "Dirección de b: " << (void*)&ejemplo.b << endl;
cout << "Dirección de c: " << (void*)&ejemplo.c << endl;
// Imprimir tamaños
cout << "sizeof(ejemplo) = " << sizeof(ejemplo) << endl;
cout << "sizeof(a) = " << sizeof(ejemplo.a) << endl;
cout << "sizeof(b) = " << sizeof(ejemplo.b) << endl;
cout << "sizeof(c) = " << sizeof(ejemplo.c) << endl;
return 0;
}
Salida Esperada¶
a = 100
b = A
c = 10.32
Dirección de a: 0x00007FF705050
Dirección de b: 0x00007FF705050
Dirección de c: 0x00007FF705050
sizeof(ejemplo) = 8
sizeof(a) = 4
sizeof(b) = 1
sizeof(c) = 8
Observaciones:
- Todas las direcciones son iguales (misma posición de memoria).
- El tamaño de la unión es 8 (el campo
double c). - Los tamaños individuales de los campos son sus tamaños normales.
Análisis Paso a Paso en Memoria¶
Memoria:
Offset: 0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
|64 |00 |00 |00 |?? |?? |?? |?? |
+---+---+---+---+---+---+---+---+
Memoria:
Offset: 0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
|41 |00 |00 |00 |?? |?? |?? |?? |
+---+---+---+---+---+---+---+---+
Nota: ejemplo.a ahora vale 0x00000041 (65), no 100. El byte bajo fue sobreescrito.
Memoria:
Offset: 0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
|A4 |70 |A4 |71 |3D |40 |24 |40 | (representación IEEE 754)
+---+---+---+---+---+---+---+---+
Todos los bytes fueron sobreescritos.
Parte 4: Análisis en Ensamblador¶
Código Original¶
union Ejemplo {
int a;
char b;
double c;
} ejemplo;
int main() {
ejemplo.a = 100;
ejemplo.b = 'A';
ejemplo.c = 10.32;
return 0;
}
Ensamblador (x64)¶
; ejemplo está en sección .data (variable global)
.data
ejemplo dq ? ; 8 bytes reservados
.text
main proc
; ejemplo.a = 100
mov dword ptr [ejemplo], 100 ; Escribir 0x64 en los primeros 4 bytes
; ejemplo.b = 'A'
mov byte ptr [ejemplo], 41h ; Escribir 0x41 en el PRIMER byte
; ejemplo.c = 10.32
movsd xmm0, qword ptr [_real_10_32] ; Cargar 10.32 en registro XMM0
movsd qword ptr [ejemplo], xmm0 ; Escribir 10.32 en los 8 bytes
xor eax, eax
ret
main endp
Análisis Detallado¶
1. Asignación a ejemplo.a = 100¶
dword ptr: El compilador sabe queaes unint(4 bytes).- Escribe
0x64en[ejemplo+0]a[ejemplo+3].
Memoria después:
2. Asignación a ejemplo.b = 'A'¶
byte ptr: El compilador sabe quebes unchar(1 byte).- Escribe
0x41en[ejemplo+0](sobreescribe el byte bajo dea).
Memoria después:
3. Asignación a ejemplo.c = 10.32¶
movsd: Mueve undouble(8 bytes) usando registros SSE.- Escribe toda la representación de
10.32en[ejemplo+0]a[ejemplo+7].
Memoria después:
Reconocimiento en IDA¶
En IDA, con símbolos:
Sin símbolos:
- Verás múltiples escrituras a la misma dirección con diferentes tamaños.
- Esto es un indicador fuerte de unión.
mov dword ptr ds:140005050h, 64h
mov byte ptr ds:140005050h, 41h
movsd qword ptr ds:140005050h, xmm0
Clave: Misma dirección base, diferentes tamaños de operando (dword, byte, qword).
Parte 5: Caso Avanzado - Estructura dentro de Unión¶
Concepto¶
Una unión puede contener una estructura y un array, permitiendo acceder a los mismos datos de dos formas diferentes.
Código Completo¶
#include <iostream>
using namespace std;
struct stCoor3D {
int X, Y, Z;
};
union unCoor3D {
struct stCoor3D N; // Estructura con campos X, Y, Z
int Coor[3]; // Array de 3 enteros
} Punto;
int main() {
// Asignar usando campos de estructura
Punto.N.X = 10;
Punto.N.Y = 20;
// Asignar usando array
Punto.Coor[2] = 30;
// Imprimir usando estructura
cout << "Punto.N.X = " << Punto.N.X << endl;
// Imprimir usando array (mismo valor)
cout << "Punto.Coor[0] = " << Punto.Coor[0] << endl;
cout << "Punto.N.Y = " << Punto.N.Y << endl;
cout << "Punto.Coor[1] = " << Punto.Coor[1] << endl;
cout << "Punto.N.Z = " << Punto.N.Z << endl;
cout << "Punto.Coor[2] = " << Punto.Coor[2] << endl;
return 0;
}
Salida¶
Punto.N.X = 10
Punto.Coor[0] = 10
Punto.N.Y = 20
Punto.Coor[1] = 20
Punto.N.Z = 30
Punto.Coor[2] = 30
Análisis de Memoria¶
Tamaño de stCoor3D:
Tamaño de int Coor[3]:
Tamaño de la unión:
Disposición en memoria:
Offset: 0 1 2 3 4 5 6 7 8 9 10 11
+---+---+---+---+---+---+---+---+---+---+---+---+
N.X: | int (X) |
N.Y: | | int (Y) |
N.Z: | | | int (Z) |
+---+---+---+---+---+---+---+---+---+---+---+---+
Coor[0]:|int (Coor[0]) |
Coor[1]:| | int (Coor[1]) |
Coor[2]:| | | int (Coor[2]) |
+---+---+---+---+---+---+---+---+---+---+---+---+
Equivalencias:
Punto.N.X≡Punto.Coor[0](offset 0)Punto.N.Y≡Punto.Coor[1](offset 4)Punto.N.Z≡Punto.Coor[2](offset 8)
Ensamblador Real (MSVC x64)¶
; Datos en .data
unCoor3D Punto DB 0cH DUP (?) ; 12 bytes (0x0C) para la unión
main PROC
sub rsp, 40 ; Reservar stack frame (0x28)
; === Asignaciones ===
; Punto.N.X = 10 (acceso directo por offset)
mov DWORD PTR unCoor3D Punto, 10
; Punto.N.Y = 20 (acceso directo por offset)
mov DWORD PTR unCoor3D Punto+4, 20
; Punto.Coor[2] = 30 (acceso por índice calculado)
mov eax, 4
imul rax, rax, 2 ; 4 * 2 = 8 (offset para Coor[2])
lea rcx, OFFSET FLAT:unCoor3D Punto
mov DWORD PTR [rcx+rax], 30 ; Escribir en Punto + 8
; === Imprimir Punto.N.X (acceso por offset directo) ===
mov edx, DWORD PTR unCoor3D Punto ; Cargar Punto.N.X (offset 0)
mov rcx, QWORD PTR __imp_std::cout
call QWORD PTR __imp_std::ostream::operator<<(int)
lea rdx, OFFSET FLAT:std::endl
mov rcx, rax
call QWORD PTR __imp_std::ostream::operator<<
; === Imprimir Punto.Coor[0] (acceso por índice calculado) ===
mov eax, 4
imul rax, rax, 0 ; 4 * 0 = 0
lea rcx, OFFSET FLAT:unCoor3D Punto
mov edx, DWORD PTR [rcx+rax] ; Cargar Punto.Coor[0]
mov rcx, QWORD PTR __imp_std::cout
call QWORD PTR __imp_std::ostream::operator<<(int)
lea rdx, OFFSET FLAT:std::endl
mov rcx, rax
call QWORD PTR __imp_std::ostream::operator<<
; === Imprimir Punto.N.Y (acceso por offset directo) ===
mov edx, DWORD PTR unCoor3D Punto+4 ; Cargar Punto.N.Y (offset 4)
mov rcx, QWORD PTR __imp_std::cout
call QWORD PTR __imp_std::ostream::operator<<(int)
lea rdx, OFFSET FLAT:std::endl
mov rcx, rax
call QWORD PTR __imp_std::ostream::operator<<
; === Imprimir Punto.Coor[1] (acceso por índice calculado) ===
mov eax, 4
imul rax, rax, 1 ; 4 * 1 = 4
lea rcx, OFFSET FLAT:unCoor3D Punto
mov edx, DWORD PTR [rcx+rax] ; Cargar Punto.Coor[1]
mov rcx, QWORD PTR __imp_std::cout
call QWORD PTR __imp_std::ostream::operator<<(int)
lea rdx, OFFSET FLAT:std::endl
mov rcx, rax
call QWORD PTR __imp_std::ostream::operator<<
; === Imprimir Punto.N.Z (acceso por offset directo) ===
mov edx, DWORD PTR unCoor3D Punto+8 ; Cargar Punto.N.Z (offset 8)
mov rcx, QWORD PTR __imp_std::cout
call QWORD PTR __imp_std::ostream::operator<<(int)
lea rdx, OFFSET FLAT:std::endl
mov rcx, rax
call QWORD PTR __imp_std::ostream::operator<<
; === Imprimir Punto.Coor[2] (acceso por índice calculado) ===
mov eax, 4
imul rax, rax, 2 ; 4 * 2 = 8
lea rcx, OFFSET FLAT:unCoor3D Punto
mov edx, DWORD PTR [rcx+rax] ; Cargar Punto.Coor[2]
mov rcx, QWORD PTR __imp_std::cout
call QWORD PTR __imp_std::ostream::operator<<(int)
lea rdx, OFFSET FLAT:std::endl
mov rcx, rax
call QWORD PTR __imp_std::ostream::operator<<
xor eax, eax ; return 0
add rsp, 40 ; 0x28
ret 0
main ENDP
Notas sobre el código real:
- Sin variables temporales: Esta versión sin strings literales es más directa; no guarda punteros base en el stack.
- Patrón claro de offset vs índice:
- Acceso a
.N.X/Y/Z→mov edx, DWORD PTR Punto+0/4/8(offset fijo) - Acceso a
.Coor[i]→imul rax, 4 * i+lea rcx+mov edx, [rcx+rax](índice calculado) - Stack frame pequeño (0x28 = 40 bytes): Solo shadow space para llamadas, sin locals complejas.
Patrones de Reconocimiento¶
Acceso como Estructura (Offset Fijo)¶
Clave: Offsets constantes (+0, +4, +8).
Acceso como Array (Índice Calculado)¶
imul eax, 4 ; índice * sizeof(int)
lea rcx, [Punto] ; Base del array
mov edx, [rcx+rax] ; Carga con índice
Clave: Multiplicación por tamaño del elemento + lea base + acceso indexado.
Optimización del Compilador¶
En el código original, el compilador puede optimizar las primeras asignaciones:
; Optimización: Escribir X e Y juntos como qword
mov qword ptr [Punto], 14000000Ah ; 0x14 = 20, 0x0A = 10
Interpretación:
- Byte 0-3:
0x0000000A(10) - Byte 4-7:
0x00000014(20)
Esto es equivalente a:
Parte 6: Uniones Anónimas (Struct inline + Array)¶
Idea¶
Puedes declarar una estructura anónima dentro de la unión y un array que se superponen. Como la struct no tiene nombre de tipo ni nombre de objeto, accedes directo a los campos (X, Y, Z) como si fueran variables sueltas.
Código (anónimo)¶
union Punto3D {
struct { int X, Y, Z; }; // struct anónima sin nombre de objeto
int Coor[3]; // array superpuesto
} Punto;
int main() {
Punto.X = 1; Punto.Y = 2; Punto.Z = 3;
cout << Punto.X << " " << Punto.Y << " " << Punto.Z << "\n";
cout << Punto.Coor[0] << " " << Punto.Coor[1] << " " << Punto.Coor[2] << "\n";
Punto.Coor[0] = 4; Punto.Coor[1] = 5; Punto.Coor[2] = 6;
cout << Punto.X << " " << Punto.Y << " " << Punto.Z << "\n";
cout << Punto.Coor[0] << " " << Punto.Coor[1] << " " << Punto.Coor[2] << "\n";
}
Salida:
Claves:
- X/Y/Z y Coor[0..2] son el mismo bloque de 12 bytes (offsets 0,4,8).
- Si intentas darle nombre al objeto (N) aun sin tipo, ya no es anónima y debes acceder vía Punto.N.X.
Parte 7: Inicialización de Uniones (primer miembro)¶
Regla¶
Una unión solo se inicializa por su primer miembro en la declaración. Si pasas un literal que no cabe o es de otro tipo, el compilador lo convertirá al tipo del primer campo (con posibles truncamientos).
Ejemplo (truncamiento)¶
union EjemploInit {
int a; // primer miembro
char b;
double c;
};
double valor = 3.1416;
EjemploInit x = { valor }; // Se inicializa `a`, no `c`
Lo que sucede:
- valor (double, 8 bytes) se convierte a int → a = 3 (trunca la parte decimal).
- b y c quedan sin inicializar; si los lees, verás basura.
- MSVC emite warning de pérdida de datos si los warnings están activos.
Patrón en ensamblador¶
Verás una conversión a entero antes de escribir en la unión:
; valor (double) en xmm?
cvttsd2si eax, xmm0 ; convertir double a int (truncar)
mov DWORD PTR x, eax ; escribir en el primer campo (int)
Parte 8: Discriminadores (uniones etiquetadas)¶
Problema¶
En una unión grande (libro/revista/película), sin un campo adicional no sabes qué variante está activa al leer.
Solución: Tagged union¶
enum Tipo { Libro = 0, Revista = 1, Pelicula = 2 };
struct TipoLibro { int codigo; char autor[80]; char titulo[80]; char editorial[32]; int anno; };
struct TipoRevista { int codigo; char nombre[32]; int mes; int anno; };
struct TipoPelicula{ int codigo; char titulo[80]; char director[80]; char productora[32]; int anno; };
struct Ejemplar {
Tipo tipo; // discriminador (no se solapa con la unión)
union {
TipoLibro l;
TipoRevista r;
TipoPelicula p;
} datos; // la unión real
};
Ejemplar tabla[100];
tabla[0].tipo = Libro;
tabla[0].datos.l.codigo = 1; // resto de campos libro...
tabla[1].tipo = Revista;
tabla[1].datos.r.codigo = 2; // resto de campos revista...
Patrón de memoria¶
tipovive fuera de la unión → no se pisa con los campos dedatos.- El tamaño de
Ejemplar= sizeof(discriminador) + sizeof(variant más grande).
Reconocimiento en reversing¶
- Verás un campo pequeño (byte/int) cercano al inicio del objeto que no se solapa con la región de datos grande.
- Accesos condicionados: primero se lee el discriminador, luego se escoge qué offsets usar.
- La región grande muestra reusos/offsets diferentes pero misma base → típico de unión.
Parte 9: Reconocimiento en Reversing¶
Indicadores de Unión en Ensamblador¶
1. Múltiples Escrituras a la Misma Dirección con Diferentes Tamaños¶
Conclusión: Probablemente una unión con int, char, y double.
2. Reutilización de Variable con Diferentes Tipos¶
; Primero se usa como entero
mov dword ptr [var], 1234h
add eax, [var]
; Luego se usa como float
movss xmm0, dword ptr [var]
addss xmm0, xmm1
Conclusión: Variable reutilizada; puede ser unión o simplemente reuso temporal.
3. Acceso a Mismos Datos de Dos Formas¶
; Acceso como estructura
mov eax, [var] ; Campo X
mov ebx, [var+4] ; Campo Y
; Acceso como array
imul ecx, 4
mov edx, [var+rcx] ; Array[índice]
Conclusión: Unión con estructura + array.
Checklist de Reconocimiento¶
- ¿Ves escrituras a la misma dirección base con diferentes tamaños (
byte,dword,qword)? → Probable unión. - ¿Una variable cambia de "tipo" (entero → float → puntero) durante la ejecución? → Posible unión o reuso temporal.
- ¿Acceso a datos mediante offsets fijos Y cálculos de índice sobre la misma base? → Unión con estructura + array.
- ¿El tamaño de la "estructura" en IDA es menor de lo esperado si fueran campos independientes? → Probable unión.
Ejemplo de IDA sin Símbolos¶
Código observado:
mov dword ptr ds:140005050h, 64h
mov byte ptr ds:140005050h, 41h
movsd qword ptr ds:140005050h, xmm0
Interpretación:
- Misma dirección base (
140005050h). - Tres tamaños diferentes (
dword,byte,qword). - Conclusión: Unión con 3 campos.
Reconstrucción en IDA:
Resumen y Casos de Uso¶
Tabla Comparativa Final¶
| Aspecto | Estructura | Unión |
|---|---|---|
| Memoria | Suma de campos (+ padding) | Campo más grande |
| Independencia | Campos independientes | Campos superpuestos |
| Coexistencia | Todos los campos válidos simultáneamente | Solo un campo válido a la vez |
| Uso típico | Datos permanentes y relacionados | Datos temporales, alternativas |
| Ejemplo | struct Persona { char nombre[50]; int edad; } |
union Dato { int entero; float flotante; } |
Casos de Uso de Uniones¶
1. Acceso a Bytes Individuales de un Entero¶
union IntBytes {
int valor;
unsigned char bytes[4];
};
IntBytes dato;
dato.valor = 0x12345678;
cout << hex << (int)dato.bytes[0] << endl; // 0x78 (little-endian)
cout << hex << (int)dato.bytes[1] << endl; // 0x56
cout << hex << (int)dato.bytes[2] << endl; // 0x34
cout << hex << (int)dato.bytes[3] << endl; // 0x12
Uso: Manipular bits individuales sin máscaras.
2. Interpretación de Datos Binarios¶
union Paquete {
unsigned int raw;
struct {
unsigned char tipo;
unsigned char tamaño;
unsigned short checksum;
} campos;
};
Uso: Leer un entero de 4 bytes como campos estructurados.
3. Ahorro de Memoria en Estructuras Grandes¶
Uso: Solo un tipo de dato activo a la vez; ahorra memoria.
4. Dos Formas de Acceso (Estructura + Array)¶
Uso: Acceso por nombre (x, y, z) o por índice (coord[i]).
Advertencias al Usar Uniones¶
1. Comportamiento Indefinido si Lees Campo Incorrecto
2. Datos Temporales, No Permanentes
Si escribes en un campo, los otros quedan con "basura" o valores sobrescritos.
3. Cuidado con Punteros
Si escribes valor, el puntero queda inválido.
Consejo de Narvaja¶
Cuando veas en reversing que una variable se reutiliza para diferentes tipos, pregúntate: ¿es una unión formal, o el programador está siendo astuto para ahorrar memoria? En ambos casos, entiende que solo un tipo es válido en un momento dado.
Las uniones son menos comunes que las estructuras, pero cuando aparecen, suelen estar relacionadas con protocolos de red, formatos binarios, o optimización de memoria. Detectarlas es clave para entender el flujo de datos.
Ejemplos de Reconocimiento en IDA¶
Ejemplo 1: Detectar Unión Simple¶
Código en IDA:
Conclusión:
- Misma dirección (
[rsp+20h]). - Diferentes tamaños (
dword,byte). - Es una unión con al menos 2 campos.
Ejemplo 2: Detectar Unión con Estructura + Array¶
Código en IDA:
; Acceso como estructura
mov eax, [rbx]
mov ecx, [rbx+4]
; Acceso como array
imul edx, 4
mov esi, [rbx+rdx]
Conclusión:
- Base
rbxaccedida con offsets fijos (+0,+4). - Base
rbxaccedida con índice calculado (+rdx). - Probable unión con estructura + array.
Reconstrucción:
Ejemplo 3: Detectar Reutilización Temporal¶
Código en IDA:
; Fase 1: uso como entero
mov dword ptr [var], 12345
add eax, [var]
; Fase 2: uso como float
movss xmm0, dword ptr [var]
Conclusión:
- Variable
varusada primero como entero, luego como float. - No hay "limpieza" entre usos.
- Probable unión o reuso intencional.
Notas Adicionales¶
Diferencias con C¶
En C, las uniones funcionan igual que en C++. No hay diferencias sintácticas.
Uniones Anónimas (C++11)¶
struct Evento {
int tipo;
union { // Unión anónima
int dato_entero;
float dato_float;
};
};
Evento e;
e.dato_entero = 10; // Acceso directo, sin nombre de unión
Uniones y Optimizaciones¶
Con /O2, el compilador puede:
- Eliminar uniones triviales y usar registros directamente.
- Combinar escrituras (como vimos con el
qwordoptimizado).
Para aprender, usa /Od sin optimizaciones.