Estruturas em C: Declaração, Inicialização e Manipulação de Memória

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.

Tags: c-lang structs pointers memory-allocation data-structures

Publicado em 7-15 12:19