Por que sizeof(estrutura) não é igual à soma de sizeof(campos)? Análise completa

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Como isso é possível? Introdução ao paradoxo do sizeof

\n\n

Imagine: você cria uma estrutura simples em C++ com dois campos — char (1 byte) e int (4 bytes). Logicamente, você assume que seu tamanho será de 5 bytes. Você escreve o código, chama sizeof e... obtém 8 bytes. "Loucura!" — você pensa. "O compilador enlouqueceu?"

\n\n

Esta pergunta é uma das mais populares no Stack Overflow (mais de 200.000 visualizações). E não é sobre um erro do compilador. É sobre como o processador e a memória RAM realmente funcionam em baixo nível. Neste artigo, vamos analisar o alinhamento de estruturas em C++, o conceito de padding (preenchimento), e aprender a prever o tamanho real das estruturas. E o principal — descobriremos como gerenciar isso quando realmente for necessário.

\n\n

Por que 1 + 4 ≠ 5? A física do processo

\n\n

Os processadores modernos não leem a memória byte a byte. Eles trabalham com "palavras" — blocos de 4, 8 ou 16 bytes (dependendo da arquitetura). Se um número inteiro de 4 bytes (int) estiver localizado em um endereço que não seja múltiplo de 4, o processador precisará fazer duas chamadas à memória em vez de uma para ler esse número, e depois "montá-lo" a partir de duas partes. Isso é catastroficamente lento.

\n\n

Para evitar isso, o compilador insere bytes "vazios" (padding) entre os campos da estrutura. Ele alinha cada campo a um endereço múltiplo de seu tamanho. Isso é chamado de alinhamento natural (natural alignment).

\n\n
// Exemplo simples que surpreende a todos\n#include <iostream>\n\nstruct Example {\n    char c;   // 1 byte\n    int  i;   // 4 bytes\n};\n\nint main() {\n    std::cout << "Tamanho do char: " << sizeof(char) << std::endl;    // 1\n    std::cout << "Tamanho do int: "  << sizeof(int)  << std::endl;    // 4\n    std::cout << "Tamanho da struct: " << sizeof(Example) << std::endl; // 8 (!)\n    return 0;\n}
\n\n

Como isso fica na memória? c ocupa o byte 0. Os bytes 1, 2, 3 são padding. E i começa no byte 4. Total: 1 (c) + 3 (vazio) + 4 (i) = 8 bytes.

\n\n

Regras de alinhamento: como o compilador toma a decisão

\n\n

O compilador segue regras simples, mas rigorosas. Conhecendo-as, você poderá prever o tamanho de uma estrutura sem executar o programa.

\n\n
    \n
  • Regra 1: A ordem dos campos é preservada. O compilador não reorganiza os campos na memória (embora alguns compiladores possam fazer isso com otimizações, o padrão não garante isso).
  • \n
  • Regra 2: Cada campo é alinhado a um endereço múltiplo de seu tamanho. char (1 byte) pode estar em qualquer lugar. short (2 bytes) — em endereços pares. int (4 bytes) — em endereços múltiplos de 4. double (8 bytes) — em endereços múltiplos de 8.
  • \n
  • Regra 3: O tamanho de toda a estrutura deve ser múltiplo do alinhamento do maior campo. Isso é necessário para que arrays de estruturas funcionem corretamente. Se a maior variável for double (8 bytes), o tamanho da estrutura será múltiplo de 8.
  • \n
\n\n

Vamos analisar um exemplo mais complexo:

\n\n
struct Complex {\n    char  a; // 1 byte\n    int   b; // 4 bytes\n    short c; // 2 bytes\n    char  d; // 1 byte\n};\n// Qual será o sizeof? Vamos calcular juntos.
\n\n

Análise passo a passo:

\n
    \n
  • a (char) — byte 0.
  • \n
  • b (int) — deve estar em um endereço múltiplo de 4. O próximo endereço assim é 4. Portanto, padding: bytes 1, 2, 3. Total após b: 8 bytes (0-7).
  • \n
  • c (short) — deve estar em um endereço par. A posição atual é 8 (par). Perfeito. Total após c: 10 bytes (0-9).
  • \n
  • d (char) — pode estar em qualquer lugar. Colocamos no byte 10. Total após d: 11 bytes (0-10).
  • \n

Blogs

Book Recommendations