Como isso é possível? Introdução ao paradoxo do sizeof
\n\nImagine: 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?"
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\nPor que 1 + 4 ≠ 5? A física do processo
\n\nOs 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.
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\nComo 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.
Regras de alinhamento: como o compilador toma a decisão
\n\nO 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
Vamos analisar um exemplo mais complexo:
\n\nstruct 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\nAná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