Em C, uma struct (estrutura) é um tipo de dado composto que permite agrupar variáveis de diferentes tipos sob um único identificador. Esse recurso é fundamental para modelar entidades complexas do mundo real, onde múltiplos atributos com tipos distintos precisam estar logicamente interligados.
Declaração de Tipos de Estrutura
Definição Básica
Para criar uma estrutura, utiliza-se a palavra-chave struct, seguida por um nome (tag) e um bloco de chaves contendo as declarações dos membros. Cada membro pode possuir um tipo de dado diferente.
struct Employee {
char full_name[50];
int age;
double base_salary;
}; // O ponto e vírgula é estritamente obrigatório ao final da definição
A Struct como Tipo de Dado Personalizado
Uma vez que a estrutura é declarada, o compilador a reconhece como um novo tipo de dado válido. Assim como variáveis primitivas, você pode instanciar objetos desse tipo em diferentes escopos:
struct Employee global_staff; // Instância global
int main() {
struct Employee local_staff; // Instância local
return 0;
}
Flexibilidade dos Membross
Os membros internos de uma estrutura não se limitam a tipos escalares. Eles podem ser arrays, ponteiros, ou até mesmo outras estruturas previamente definidas (aninhamento).
Inicialização e Acesso aos Membros
Inicialização de Variáveis
A atribuição inicial de valores a uma estrutura pode ser realizada no momento da sua declaração, fornecendo uma lista de valores entre chaves {}, separados por vírgulas e na ordem exata em que os membros foram declarados.
struct Employee dev = {"Ana Costa", 28, 7500.50};
Operador Ponto (.) e Estruturas Aninhadas
Para interagir com os campos individuais de uma variável de estrutura, utiliza-se o operador ponto .. Este operador aceita a variável à esquerda e o membro à direita.
Quando lidamos com estruturas aninhadas (uma estrutura dentro de outra), a cadeia de operadores ponto permite acessar os campos mais profundos.
#include <stdio.h>
struct Address {
char street[40];
int zip_code;
};
struct Staff {
struct Address location;
char department[30];
int years_active;
};
int main() {
// Inicialização de estrutura aninhada
struct Staff manager = { {"Av. Paulista", 01310100}, "Diretoria", 12 };
// Acesso via operador ponto
printf("CEP: %d\n", manager.location.zip_code);
printf("Setor: %s\n", manager.department);
return 0;
}
Ponteiros para Estruturas
Ao trabalhar com endereços de memória de estruturas, declaramos ponteiros para o tipo. Para acessar os membros através de um ponteiro, a linguagem C fornece o operador seta ->, que é uma abreviação sintática para (*ponteiro).membro.
struct Staff manager;
struct Staff *ptr = &manager;
// As duas linhas abaixo são equivalentes, mas a segunda é o padrão da indústria
printf("%d\n", (*ptr).location.zip_code);
printf("%d\n", ptr->location.zip_code);
Passagem de Estruturas para Funções
Ao modularizar o código, frequentemente precisamos enviar estruturas para funções. Existem duas estratégias principais de alocação de memória para essa operação.
Passagem por Valor
Neste paradigma, o compilador cria uma cópia bit a bit de toda a estrutura e a empilha no escopo da função chamada. Qualquer modificação nos parâmetros não reflete na estrutura original.
#include <stdio.h>
struct Product {
int sku;
float price;
};
void calculateTax(struct Product item) {
item.price *= 1.15; // Adiciona 15% de imposto apenas na cópia local
printf("Preco com imposto (interno): %.2f\n", item.price);
}
int main() {
struct Product laptop = {8834, 1200.00};
calculateTax(laptop);
// O valor original permanece intacto
printf("Preco original: %.2f\n", laptop.price);
return 0;
}
Passagem por Referência
Nesta abordagem, apenas o endereço de memória da estrutura é passado. A função recebe um ponteiro e opera diretamente sobre os dados originais na memória.
#include <stdio.h>
struct Product {
int sku;
float price;
};
void applyDiscount(struct Product *item) {
item->price *= 0.80; // Aplica 20% de desconto no objeto original
}
int main() {
struct Product laptop = {8834, 1200.00};
applyDiscount(&laptop); // Envia o endereço
printf("Preco com desconto: %.2f\n", laptop.price);
return 0;
}
Impacto na Performance e Uso da Pilha
A escolha entre passagem por valor e por referência tem implicações diretas na arquitetura de memória. A passagem por valor exige que o sistema operacional reserve espaço contíguo na pilha (stack) para duplicar cada membro da estrutura. Se a estrutura contiver grandes arrays ou múltiplos campos, esse processo de cópia (push na pilha) gera um overhead considerável, degradando a performance da aplicação. Em contraste, a passagem por referência transfere apenas um ponteiro (geralmente 4 ou 8 bytes, dependnedo da arquitetura do processador). Consequentemente, a passagem por endereço é a técnica padrão e maiss eficiente para manipular estruturas em C.