DMarlon Studio

Lección 13 · 13 de 20

Uniones y campos de bits · C

Guarde valores de distinto tipo en una unión con etiqueta y empaquete una fecha en bits con campos de bits en C, comprobando su tamaño con sizeof.

C · Uniones y campos de bits en DMarlon Studio: el código del ejemplo y su salida en la terminal

En una unión todos los miembros empiezan en la misma dirección y comparten los mismos bytes: solo uno tiene sentido a la vez. Por eso ocupa lo que su miembro más grande; aquí, sizeof config[0].value es 8, lo que mide un double o un puntero.

Como la unión no recuerda qué miembro se escribió, se acompaña de una etiqueta: en Setting, el campo kind (un enum) dice si el valor es INT, REAL o TEXT. Esto se llama unión con etiqueta y es la forma clásica en C de guardar un valor que puede ser de varios tipos, como los ajustes de un archivo de configuración.

Al inicializar se elige el miembro con su nombre: {.i = 8080}, {.d = 2.5}, {.s = "es"}. show mira la etiqueta con un switch y lee solo el miembro que corresponde.

Los campos de bits dicen cuántos bits ocupa cada campo: day : 5 llega hasta 31, month : 4 hasta 15 y year : 12 hasta 4095. Los tres caben en un solo unsigned int, y sizeof release es 4 bytes en vez de 12. El orden exacto de los bits depende del compilador, así que no sirven para leer formatos binarios ajenos.

La última unión lee los bytes de un float como un entero de 32 bits: 1.0f se guarda como 0x3F800000 según el estándar IEEE 754. En C, leer un miembro distinto del último escrito está permitido y reinterpreta los bytes.

Código · main.c

#include <stdint.h>#include <stdio.h>// A tagged union: the enum says which member of the union is in use.typedef enum { INT, REAL, TEXT } Kind;typedef struct {    const char *name;    Kind kind;    union { int i; double d; const char *s; } value;  // all share the same bytes} Setting;// Bit fields: a date packed into 21 bits of one unsigned int.typedef struct { unsigned day : 5, month : 4, year : 12; } Date;void show(const Setting *s) {    switch (s->kind) {    case INT:  printf("%-9s = %d\n", s->name, s->value.i); break;    case REAL: printf("%-9s = %.2f\n", s->name, s->value.d); break;    case TEXT: printf("%-9s = \"%s\"\n", s->name, s->value.s); break;    }}int main(void) {    Setting config[] = {{"port", INT, {.i = 8080}}, {"timeout", REAL, {.d = 2.5}},                        {"language", TEXT, {.s = "es"}}};    for (int k = 0; k < 3; k++) show(&config[k]);    printf("sizeof union: %zu (its biggest member)\n", sizeof config[0].value);    Date release = {.day = 14, .month = 3, .year = 2026};    printf("Date %02u/%02u/%u in %zu bytes\n", release.day, release.month, release.year, sizeof release);    union { float f; uint32_t bits; } pun = {.f = 1.0f};  // read the bytes of a float    printf("1.0f is stored as 0x%08X\n", (unsigned)pun.bits);    return 0;}

Salida · cc main.c -o main && ./main

port      = 8080
timeout   = 2.50
language  = "es"
sizeof union: 8 (its biggest member)
Date 14/03/2026 in 4 bytes
1.0f is stored as 0x3F800000

Para recordar

  • Los miembros de una unión comparten los mismos bytes.
  • Una unión ocupa lo que su miembro más grande.
  • Una etiqueta (enum) indica qué miembro es el válido.
  • campo : n ocupa solo n bits.

Ejercicio

Añada a Kind el tipo FLAG con un miembro int on en la unión, cree el ajuste dark_mode y haga que show escriba on u off.