Structs em C
LidoNesta aula, você aprenderá a criar e usar structs em C, desde a definição básica até o acesso a membros, inicialização e o uso de typedef para simplificar a declaração. Através de exemplos práticos, você dominará a criação de tipos de dados compostos, essenciais para organizar informações relacionadas em seus programas.
As structs (estruturas) são um dos pilares da programação em C, permitindo agrupar diferentes tipos de dados em uma única unidade lógica. Com elas, você pode criar tipos personalizados que representam entidades do mundo real, como um ponto em um gráfico, um registro de aluno ou um endereço. Nesta aula, vamos explorar desde a definição básica até técnicas avançadas de inicialização e o uso de typedef para tornar seu código mais limpo e legível.
Dominar structs é essencial para avançar em tópicos como listas encadeadas, árvores e manipulação de arquivos, pois elas fornecem a base para a criação de estruturas de dados complexas. Vamos começar!
Definição
Uma struct em C é um tipo de dado definido pelo usuário que agrupa um conjunto de variáveis de tipos diferentes sob um único nome. A sintaxe básica é:
struct nome_estrutura {
tipo1 membro1;
tipo2 membro2;
// ...
};Por exemplo, para representar um ponto em um plano cartesiano, podemos definir:
struct Ponto {
int x;
int y;
};Essa definição cria um novo tipo chamado struct Ponto, mas ainda não aloca memória para nenhuma variável. Para criar uma variável desse tipo, fazemos:
struct Ponto p1;Aqui, p1 é uma variável do tipo struct Ponto, contendo dois campos inteiros: x e y. É importante notar que a definição da struct deve terminar com ponto e vírgula, e que a struct pode ser definida fora de qualquer função (geralmente no topo do arquivo) para ser usada globalmente, ou dentro de uma função, mas nesse caso o escopo fica restrito.
Além de tipos primitivos, podemos incluir outros tipos, como arrays, ponteiros e até outras structs dentro de uma struct, criando composições mais complexas. Por exemplo:
struct Aluno {
char nome[50];
int idade;
float notas[3];
};Nesse caso, cada variável struct Aluno conterá um array de caracteres para o nome, um inteiro para a idade e um array de floats para as notas.
Acesso a membros (.)
Para acessar os membros de uma struct, usamos o operador ponto (.). A sintaxe é variavel.membro. Vamos ver um exemplo completo:
#include <stdio.h>
struct Ponto {
int x;
int y;
};
int main() {
struct Ponto p1;
p1.x = 10;
p1.y = 20;
printf("Ponto: (%d, %d)\n", p1.x, p1.y);
return 0;
}Ao executar, o programa imprime Ponto: (10, 20). O operador ponto permite ler e escrever nos membros da struct. Podemos também usar o valor dos membros em expressões, como int soma = p1.x + p1.y;.
Quando temos um ponteiro para uma struct, usamos o operador seta (->) para acessar os membros, em vez do ponto. Por exemplo:
struct Ponto *ptr = &p1;
ptr->x = 30;
printf("%d\n", ptr->y);Isso é frequentemente usado em funções que recebem ponteiros para structs para modificar os valores originais. Mas não se preocupe, aprofundaremos isso em aulas futuras.
Inicialização
Assim como variáveis comuns, structs podem ser inicializadas no momento da declaração. A forma mais simples é fornecer os valores entre chaves, na ordem em que os membros foram declarados:
struct Ponto p1 = {10, 20};Isso atribui 10 a x e 20 a y. Se você quiser inicializar apenas alguns membros, pode usar a inicialização designada (C99 em diante), especificando o nome do membro:
struct Ponto p2 = {.y = 20};Nesse caso, x é inicializado com 0 (valor padrão para inteiros) e y com 20. Isso torna o código mais legível e menos propenso a erros de ordem.
Também é possível inicializar uma struct com os valores de outra struct do mesmo tipo, o que faz uma cópia membro a membro:
struct Ponto p3 = p1;Essa atribuição copia os valores de p1 para p3. É importante lembrar que, se a struct contiver ponteiros, a cópia será superficial, ou seja, os ponteiros apontarão para os mesmos dados, o que pode causar problemas se você modificar o conteúdo apontado.
typedef
O uso de typedef permite criar um nome alternativo para um tipo existente, incluindo structs. Isso evita ter que escrever a palavra-chave struct toda vez que declaramos uma variável. Por exemplo:
typedef struct {
int x;
int y;
} Ponto;Agora podemos declarar variáveis diretamente com Ponto:
Ponto p1;
p1.x = 10;
Note que na definição com typedef não precisamos dar um nome à struct, pois estamos criando um alias. Podemos também usar o nome da struct após a palavra-chave struct para referências futuras, mas isso é opcional. Por exemplo:
typedef struct Ponto {
int x;
int y;
} Ponto;Assim, tanto struct Ponto quanto Ponto podem ser usados. O uso de typedef é especialmente útil para tipos complexos, como os que serão vistos em estruturas de dados, melhorando a legibilidade do código.
Vamos ver um exemplo completo usando typedef:
#include <stdio.h>
typedef struct {
char nome[50];
int idade;
float altura;
} Pessoa;
int main() {
Pessoa pessoa1 = {"Maria", 25, 1.70};
printf("Nome: %s\n", pessoa1.nome);
printf("Idade: %d\n", pessoa1.idade);
printf("Altura: %.2f\n", pessoa1.altura);
return 0;
}Nesse exemplo, a struct Pessoa é definida com typedef e inicializada com três valores na ordem correta. Perceba como o código fica mais limpo e fácil de entender.
Boas práticas
Ao trabalhar com structs, algumas boas práticas ajudam a evitar erros e melhorar a manutenção:
- Use nomes significativos para a struct e seus membros, em minúsculas ou com camelCase, seguindo a convenção do projeto.
- Inicialize sempre as structs antes de usá-las, para evitar valores lixo.
- Prefira usar
typedefpara simplificar a declaração, mas lembre-se de que isso esconde o fato de ser uma struct, o que pode ser útil ou não dependendo do contexto. - Quando passar structs para funções, considere passar por referência (ponteiro) para evitar cópias desnecessárias, especialmente se a struct for grande.
- Se uma struct contém ponteiros que apontam para memória alocada dinamicamente, você deve criar funções para inicializar e liberar essa memória corretamente, evitando vazamentos.
Exercícios
- Defina uma struct chamada
Retanguloque tenha dois campos:larguraealtura, ambos do tipo float. Em seguida, crie uma variável dessa struct e a inicialize com valores 5.0 e 3.0. Imprima a área do retângulo. - Usando a struct
Ponto(com campos x e y), escreva uma função que receba dois pontos e retorne a distância entre eles. A fórmula é: d = sqrt((x2-x1)^2 + (y2-y1)^2). Use a funçãosqrtda biblioteca math.h. - Crie uma struct
Alunocom nome (array de 50 caracteres) e nota (float). Usandotypedef, declare um array de 3 alunos e preencha com dados fornecidos pelo usuário. Depois, calcule e imprima a média das notas. - Explique a diferença entre usar
struct Ponto p1;ePonto p1;quando você temtypedef struct { ... } Ponto;. - Dada a struct
Livrocom título (string) e ano (int), escreva um programa que leia os dados de 5 livros e imprima apenas aqueles publicados após 2000.
#include <stdio.h>
struct Retangulo {
float largura;
float altura;
};
int main() {
struct Retangulo ret = {5.0, 3.0};
float area = ret.largura * ret.altura;
printf("Área: %.2f\n", area);
return 0;
}#include <stdio.h>
#include <math.h>
struct Ponto {
int x;
int y;
};
double distancia(struct Ponto p1, struct Ponto p2) {
int dx = p2.x - p1.x;
int dy = p2.y - p1.y;
return sqrt(dx*dx + dy*dy);
}
int main() {
struct Ponto a = {0, 0};
struct Ponto b = {3, 4};
printf("Distância: %.2f\n", distancia(a, b));
return 0;
}#include <stdio.h>
typedef struct {
char nome[50];
float nota;
} Aluno;
int main() {
Aluno alunos[3];
float soma = 0;
for (int i = 0; i < 3; i++) {
printf("Nome do aluno %d: ", i+1);
scanf("%49s", alunos[i].nome);
printf("Nota: ");
scanf("%f", &alunos[i].nota);
soma += alunos[i].nota;
}
printf("Média: %.2f\n", soma/3);
return 0;
}struct Ponto p1; usa o nome da struct definido originalmente, enquanto Ponto p1; usa o alias criado via typedef. Ambos são equivalentes, mas o segundo é mais conciso e evita repetir a palavra struct. Na prática, muitos programadores preferem usar typedef para simplificar a sintaxe, especialmente em tipos complexos.#include <stdio.h>
typedef struct {
char titulo[100];
int ano;
} Livro;
int main() {
Livro livros[5];
for (int i = 0; i < 5; i++) {
printf("Título do livro %d: ", i+1);
scanf("%99s", livros[i].titulo);
printf("Ano de publicação: ");
scanf("%d", &livros[i].ano);
}
printf("\nLivros publicados após 2000:\n");
for (int i = 0; i < 5; i++) {
if (livros[i].ano > 2000) {
printf("%s (%d)\n", livros[i].titulo, livros[i].ano);
}
}
return 0;
}