Bem-vindo ao décimo sexto capítulo da Especialização em Engenharia de Sistemas com Linguagem C (C17/C23)! ⚡

Na Engenharia de Software e de Sistemas, quase nunca trabalhamos apenas com tipos primitivos soltos. Precisamos modelar entidades complexas do mundo real: cabeçalhos de pacotes TCP/IP, registros de banco de dados, nós de árvores e descritores de dispositivos de hardware. Neste capítulo, você dominará os Tipos Compostos Heterogêneos (struct), a mecânica de alinhamento de hardware (Struct Padding), o compartilhamento de memória com union, a semântica de enum e os Campos de Bits (Bit-fields).

Structs, Struct Padding, Unions e Enumerações em C


🗺️ Mapa Conceitual do Capítulo

graph TD
    A["Tipos Compostos em C"] --> B["1. Estruturas (struct & typedef)"]
    A --> C["2. Alinhamento & Struct Padding"]
    A --> D["3. Uniões de Memória (union)"]
    A --> E["4. Enumerações e Bit-Fields"]

    B --> B1["Operador Ponto (.) vs Seta (->) com ponteiros"]
    C --> C1["Alinhamento de Barramento de Memória da CPU"]
    D --> D1["Campos sobrepostos compartilhando o mesmo Byte 0"]
    E --> E1["enum para códigos limpos e unsigned int f : 1 para bits"]

🏛️ 1. Estruturas Heterogêneas (struct) e typedef

Uma struct agrupa variáveis de tipos diferentes sob um único bloco contíguo de memória:

#include <stdio.h>
#include <stdint.h>

// Definição idiomática com typedef
typedef struct {
    uint32_t id;
    char nome[32];
    double tensaoVolts;
    bool operacional;
} SensorIndustrial;

int main(void) {
    // 1. Inicialização direta
    SensorIndustrial s1 = {101, "Termopar_Forno", 3.28, true};

    // 2. Acesso direto com operador ponto (.)
    printf("Sensor #%u: %s (%.2lf V)\n", s1.id, s1.nome, s1.tensaoVolts);

    // 3. Acesso por ponteiro com operador seta (->)
    SensorIndustrial *ptr = &s1;
    ptr->tensaoVolts = 3.35; // Equivalente a (*ptr).tensaoVolts = 3.35;

    return 0;
}

📐 2. Alinhamento de Memória de Hardware e Struct Padding

A CPU lê a memória RAM em palavras de 4 bytes (32 bits) ou 8 bytes (64 bits). Para otimizar os ciclos de clock do barramento de dados, o compilador insere bytes de enchimento vazios (Padding) entre os membros da estrutura.

Exemplo de Impacto do Padding no Tamanho Total:

// ❌ Estrutura Desordenada (Gasta 24 Bytes na RAM):
struct Desordenada {
    char a;      // 1 Byte  + 7 Bytes de PADDING
    double b;    // 8 Bytes
    char c;      // 1 Byte  + 7 Bytes de PADDING
}; // sizeof = 24 Bytes!

// ✅ Estrutura Otimizada por Tamanho Decrescente (Gasta 16 Bytes na RAM):
struct Otimizada {
    double b;    // 8 Bytes
    char a;      // 1 Byte
    char c;      // 1 Byte  + 6 Bytes de PADDING no final
}; // sizeof = 16 Bytes! (Economia de 33% de memória!)

[!TIP] Regra de Ouro de Otimização: Ordene os membros da sua struct do maior tipo para o menor (double $\rightarrow$ int $\rightarrow$ short $\rightarrow$ char) para minimizar o padding inserido pelo compilador.


🧬 3. Uniões de Memória Compartilhada (union)

Uma union armazena múltiplos campos no mesmo endereço físico de memória. O tamanho total da união é exatamente o tamanho do seu maior membro:

typedef union {
    uint32_t valorInteiro;   // 4 Bytes
    uint8_t bytes[4];        // 4 Bytes individuais
} ConversorRegistrador;

int main(void) {
    ConversorRegistrador reg;
    reg.valorInteiro = 0x12345678;

    // Em arquiteturas Little-Endian (x86/ARM), os bytes são lidos em ordem inversa:
    printf("Byte 0: 0x%02X\n", reg.bytes[0]); // 0x78
    printf("Byte 3: 0x%02X\n", reg.bytes[3]); // 0x12

    return 0;
}

🏷️ 4. Enumerações (enum) e Campos de Bits (Bit-Fields)

1. Enumerações Fortes (enum):

Eliminam “números mágicos” e documentam estados do sistema:

typedef enum {
    ESTADO_OFFLINE = 0,
    ESTADO_CONECTANDO = 1,
    ESTADO_ONLINE = 2,
    ESTADO_ERRO_CRITICO = 99
} EstadoRede;

2. Campos de Bits (Bit-Fields):

Permitem empacotar múltiplos flags booleanos no nível de bits individuais:

typedef struct {
    unsigned int habilitado  : 1; // Ocupa exatamente 1 BIT (0 ou 1)
    unsigned int modoLeitura : 1; // 1 BIT
    unsigned int nivelAlarme : 2; // 2 BITS (0 a 3)
    unsigned int codigoErro  : 4; // 4 BITS (0 a 15)
} RegistradorHardware; // Ocupa apenas 1 Byte / 4 Bytes em vez de 4 inteiros!

🔍 5. Diagnóstico & Resolução de Problemas (Troubleshooting)

Sintoma Observado Causa Provável Como Resolver
sizeof(MinhaStruct) é maior que a soma manual dos seus campos O compilador inseriu bytes de Struct Padding para alinhamento de barramento. Reordene os campos do maior para o menor ou use a diretiva de empacotamento __attribute__((packed)).
Erro de compilação: request for member in something not a structure or union Uso do operador ponto . em um ponteiro de estrutura em vez da seta ->. Utilize o operador seta: ptr->campo.
Ao gravar em um campo da union, o outro campo foi corrompido Os campos de uma union compartilham a mesma memória física. Lembre-se de que apenas um membro da união pode ser válido por vez.

🏆 6. Desafio Prático de Consolidação

Enunciado do Desafio: Desenvolva um programa em C chamado parser_pacote_telemetria.c:

  1. Crie uma enum TipoComando { CMD_PING = 1, CMD_LEITURA = 2, CMD_REINICIAR = 3 };.
  2. Crie uma union CargaUtil contendo: float temperatura; e uint32_t codigoRaw;.
  3. Crie a struct PacoteRede contendo:
    • uint8_t idOrigem;
    • uint8_t idDestino;
    • TipoComando comando;
    • CargaUtil payload;
  4. Crie a função void processarPacote(const PacoteRede *pacote); que imprima os campos e o tamanho total da estrutura (sizeof) em bytes.
🔍 Ver Solução Comentada do Desafio
#include <stdio.h>
#include <stdint.h>

typedef enum {
    CMD_PING = 1,
    CMD_LEITURA = 2,
    CMD_REINICIAR = 3
} TipoComando;

typedef union {
    float temperatura;
    uint32_t codigoRaw;
} CargaUtil;

typedef struct {
    uint8_t idOrigem;
    uint8_t idDestino;
    TipoComando comando;
    CargaUtil payload;
} PacoteRede;

void processarPacote(const PacoteRede *pacote) {
    printf("Pacote: Origem #%u -> Destino #%u | Cmd: %d | Temp: %.2lf C\n",
           pacote->idOrigem, pacote->idDestino, pacote->comando, pacote->payload.temperatura);
    printf("Tamanho em memoria (sizeof): %zu bytes\n", sizeof(PacoteRede));
}

int main(void) {
    PacoteRede pkt = {
        .idOrigem = 1,
        .idDestino = 100,
        .comando = CMD_LEITURA,
        .payload = { .temperatura = 23.75f }
    };

    processarPacote(&pkt);
    return 0;
}

🧭 Navegação Rápida

| 📖 Teoria | 📊 Slides | 🧠 Quiz | 💻 Exemplos | 🧩 Exercícios | | :— | :— | :— | :— | :— | | Ler Teoria | Ver Slides | Fazer Quiz | Ver Exemplos | Praticar Exercícios |


🧭 Navegação do Capítulo: ⬅️ Capítulo Anterior · 📚 Sumário do Módulo · ➡️ Próximo Capítulo