Estruturas — Avançado
Structs avançadas são como decorar uma casa — não se trata apenas de construir a estrutura, mas também de usar o espaço de forma eficiente (alinhamento), economizar materiais (bit-fields) e permitir expansão flexível (arrays flexíveis).
Uso Avançado de struct com Funções
Funções que Retornam uma struct
C permite que funções retornem uma struct diretamente — o compilador cuida da cópia:
struct Point make_point(int x, int y) {
struct Point p = {x, y};
return p;
}
int main(void) {
struct Point pt = make_point(3, 4);
printf("(%d, %d)\n", pt.x, pt.y);
return 0;
}
Preenchendo uma struct Através de um Ponteiro
void fill_student(struct Student *s, const char *name, int age, float score) {
strncpy(s->name, name, 19);
s->name[19] = '\0';
s->age = age;
s->score = score;
}
int main(void) {
struct Student stu;
fill_student(&stu, "Zhang", 20, 88.5);
return 0;
}
Proteção const para Parâmetros struct
Para impedir que uma função modifique uma struct, use const no parâmetro ponteiro:
void print_student(const struct Student *s) {
printf("%s %d %.1f\n", s->name, s->age, s->score);
}
const struct Student *s significa que você não pode modificar a struct apontada através de s, mas s pode apontar para outra coisa.
typedef para Alias de Tipos
typedef cria um alias para um tipo, reduzindo a necessidade de escrever repetidamente a palavra-chave struct.
Uso Básico
typedef struct {
char name[20];
int age;
float score;
} Student;
Student s1 = {"Zhang", 20, 89.5};
Student *ps = &s1;
struct Student — basta usar Student diretamente.
typedef com Ponteiros de struct
typedef struct Node {
int data;
struct Node *next;
} Node, *NodePtr;
Node n1 = {10, NULL};
NodePtr head = &n1;
struct Node *next porque o alias ainda não está em vigor naquele ponto.
Outros Usos de typedef
typedef unsigned char Byte;
typedef int (*Comparator)(const void *, const void *);
Byte flag = 0xFF;
Comparator cmp = my_compare;
Exemplo
#include <stdio.h>
#include <string.h>
typedef struct {
char title[50];
int pages;
float price;
} Book;
Book create_book(const char *title, int pages, float price) {
Book b;
strncpy(b.title, title, 49);
b.title[49] = '\0';
b.pages = pages;
b.price = price;
return b;
}
void discount(Book *b, float rate) {
b->price *= rate;
}
void print_book(const Book *b) {
printf("<%s> %d paginas R$%.2f\n", b->title, b->pages, b->price);
}
int main(void) {
Book b1 = create_book("C Programming", 320, 59.0);
print_book(&b1);
discount(&b1, 0.8);
print_book(&b1);
return 0;
}
<C Programming> 320 paginas R$59.00
<C Programming> 320 paginas R$47.20
Alinhamento de Memória em structs
Os membros de uma struct não são empacotados de forma contígua na memória — o compilador insere bytes de padding de acordo com regras de alinhamento.
Regras de Alinhamento
- O offset de cada membro deve ser um múltiplo do tamanho desse membro
- O tamanho total da struct deve ser um múltiplo do tamanho do maior membro
struct Align1 {
char a;
int b;
char c;
};
struct Align2 {
char a;
char c;
int b;
};
printf("%zu\n", sizeof(struct Align1));
printf("%zu\n", sizeof(struct Align2));
12
8
Layout de Align1: a (1 byte) + 3 bytes padding + b (4 bytes) + c (1 byte) + 3 bytes padding = 12.
Layout de Align2: a (1 byte) + c (1 byte) + 2 bytes padding + b (4 bytes) = 8.
#pragma pack
Você pode especificar o limite de alinhamento para comprimir uma struct:
#pragma pack(push, 1)
struct Packed {
char a;
int b;
char c;
};
#pragma pack(pop)
printf("%zu\n", sizeof(struct Packed));
6
Bit-Fields
Bit-fields alocam espaço para membros bit a bit, economizando memória.
struct Flags {
unsigned int ready : 1;
unsigned int error : 1;
unsigned int mode : 3;
unsigned int : 0;
unsigned int count : 12;
};
readyocupa 1 bit (0 ou 1)errorocupa 1 bitmodeocupa 3 bits (0-7): 0força o alinhamento para a próxima unidade de armazenamentocountocupa 12 bits
struct Flags f = {1, 0, 5, 1024};
printf("ready=%u error=%u mode=%u count=%u\n", f.ready, f.error, f.mode, f.count);
printf("Tamanho da struct: %zu\n", sizeof(f));
ready=1 error=0 mode=5 count=1024
Tamanho da struct: 8
&f.ready é ilegal).
Membros de Array Flexível
O C99 permite que o último membro de uma struct seja um array de comprimento 0, chamado de Membro de Array Flexível.
typedef struct {
int len;
int data[];
} IntVec;
Ao usar, aloque espaço extra conforme necessário:
int n = 5;
IntVec *v = (IntVec *)malloc(sizeof(IntVec) + sizeof(int) * n);
v->len = n;
for (int i = 0; i < n; i++) {
v->data[i] = i * 100;
}
for (int i = 0; i < v->len; i++) {
printf("%d ", v->data[i]);
}
free(v);
0 100 200 300 400
sizeof para obter o tamanho de um array flexível.
❓ Perguntas Frequentes
P: Qual a diferença entre typedef e #define para alias de tipos? R: typedef é processado pelo compilador, segue regras de escopo e lida com tipos de ponteiro corretamente. #define é uma substituição de texto do pré-processador que pode causar erros sutis. Prefira typedef.
P: Por que o alinhamento de memória existe? R: CPUs leem memória de forma mais eficiente em limites alinhados, e algumas arquiteturas geram falhas em acessos não alinhados. O compilador adiciona padding automaticamente para compatibilidade e desempenho.
P: Pode-se obter o endereço de um bit-field? R: Não. Um membro bit-field pode ser menor que um byte e não tem endereço independente. O operador
&não pode ser usado em bit-fields.
P: Array flexível ou membro ponteiro — qual é melhor? R: Arrays flexíveis armazenam dados de forma contígua com uma única alocação e desalocação, e são amigáveis ao cache. Membros ponteiro podem apontar para qualquer lugar, oferecendo mais flexibilidade mas exigindo duas alocações e dois frees. Arrays flexíveis são a escolha mais simples.
📖 Resumo
- Funções podem retornar structs; para structs grandes, prefira preencher através de um ponteiro para eficiência
typedefcria alias de tipos, simplificando nomes de tipos struct- Structs têm alinhamento de memória — a ordenação dos membros afeta o tamanho total
#pragma packpode sobrescrever regras de alinhamento, útil para análise de protocolos e cenários similares- Bit-fields alocam espaço bit a bit, economizando memória, mas não são endereçáveis nem portáveis
- Membros de array flexível permitem structs de tamanho variável com dados contíguos e alocação/desalocação simples
📝 Exercícios
- Defina uma struct contendo char, short, int e double. Tente duas ordenações diferentes de membros e use sizeof para verificar a diferença de tamanho
- Use typedef para definir um tipo de nó de lista ligada, depois escreva funções para criar e percorrer uma lista ligada simples
- Use um membro de array flexível para implementar uma struct de string dinâmica (com len e data[]), suportando uma operação de adicionar caractere



