Estruturas (struct)

Uma struct é como um formulário personalizado — ela empacota diferentes tipos de informação (nome, idade, nota) em uma única unidade que você pode gerenciar como um todo.

Definição e Declaração de struct

Definindo um Tipo struct

C
struct Student {
    char name[20];
    int age;
    float score;
};

Isso define um tipo struct Student. Note que o ponto e vírgula no final é obrigatório.

Declarando Variáveis struct

C
struct Student s1;
struct Student s2, s3;

Você também pode declarar variáveis ao mesmo tempo em que define o tipo:

C
struct Point {
    int x;
    int y;
} p1, p2;

struct Anônima

Se você só precisa dela uma vez, pode omitir o nome do tipo:

C
struct {
    int width;
    int height;
} box;
⚠️ Atenção: Uma struct anônima não pode ser usada para declarar novas variáveis depois. É tipicamente usada apenas em cenários pontuais.

Inicialização de Variáveis struct

Inicialização Sequencial

C
struct Student s1 = {"Zhang", 20, 89.5};

Inicializador Designado (C99)

C
struct Student s2 = {.name = "Li", .score = 92.0, .age = 21};
💡 Dica: Inicializadores designados não precisam seguir a ordem de declaração. Membros não especificados são automaticamente zerados.

Inicialização Parcial

C
struct Student s3 = {"Wang"};

name é inicializado; os membros restantes são automaticamente definidos como 0.

Acesso a Membros

Operador Ponto .

Acesse membros através de uma variável struct:

C
struct Student s = {"Zhao", 19, 78.5};
printf("Nome: %s\n", s.name);
printf("Idade: %d\n", s.age);
s.score = 85.0;

Operador Seta ->

Acesse membros através de um ponteiro de struct:

C
struct Student s = {"Zhao", 19, 78.5};
struct Student *ps = &s;
printf("Nome: %s\n", ps->name);
ps->age = 20;

ps->name é equivalente a (*ps).name. O operador -> é um açúcar sintático — mais conciso ao trabalhar com ponteiros.

💡 Dica: Macete: use ponto para variáveis, use seta para ponteiros.

Exemplo

C
#include <stdio.h>

struct Book {
    char title[50];
    char author[30];
    float price;
};

int main(void) {
    struct Book b1 = {.title = "The C Programming Language", .author = "K&R", .price = 45.0};
    struct Book *pb = &b1;

    printf("Titulo: %s\n", pb->title);
    printf("Autor: %s\n", pb->author);
    printf("Preco: %.1f\n", pb->price);

    pb->price = 55.0;
    printf("Apos desconto: %.1f\n", b1.price);
    return 0;
}
▶ Experimente
TEXT
Titulo: The C Programming Language
Autor: K&R
Preco: 45.0
Apos desconto: 55.0

Array de struct

Coloque múltiplas structs em um array para gerenciamento unificado.

C
struct Student class[3] = {
    {"Zhang", 20, 89.5},
    {"Li", 21, 92.0},
    {"Wang", 19, 78.0}
};

for (int i = 0; i < 3; i++) {
    printf("%s %d %.1f\n", class[i].name, class[i].age, class[i].score);
}
TEXT
Zhang 20 89.5
Li 21 92.0
Wang 19 78.0

Percorra um array de struct com um ponteiro:

C
struct Student *p = class;
for (int i = 0; i < 3; i++, p++) {
    printf("%s %.1f\n", p->name, p->score);
}

Ponteiro de struct

O uso mais comum de ponteiros de struct: evitar copiar a struct inteira ao passá-la entre funções.

C
void print_student(struct Student *ps) {
    printf("%s, idade %d, nota %.1f\n", ps->name, ps->age, ps->score);
}

int main(void) {
    struct Student s = {"Zhang", 20, 89.5};
    print_student(&s);
    return 0;
}
TEXT
Zhang, idade 20, nota 89.5
⚠️ Atenção: Passar por valor copia a struct inteira. Se a struct tem muitos membros ou contém arrays grandes, o custo é significativo. Passar um ponteiro copia apenas um endereço.

struct como Parâmetro de Função

Passagem por Valor

C
void add_score(struct Student s, float bonus) {
    s.score += bonus;
}
⚠️ Atenção: Ao passar por valor, modificações dentro da função afetam uma cópia — a variável original não é alterada.

Passagem por Ponteiro

C
void add_score(struct Student *s, float bonus) {
    s->score += bonus;
}

Modificações através do ponteiro dentro da função afetam diretamente a variável original.

Exemplo

C
#include <stdio.h>

struct Score {
    char name[20];
    int chinese;
    int math;
    int english;
};

int total(struct Score *s) {
    return s->chinese + s->math + s->english;
}

void boost(struct Score *s, int bonus) {
    s->chinese += bonus;
    s->math += bonus;
    s->english += bonus;
}

int main(void) {
    struct Score stu = {.name = "Zhou", .chinese = 80, .math = 75, .english = 88};
    printf("Total original: %d\n", total(&stu));
    boost(&stu, 5);
    printf("Total apos bonus: %d\n", total(&stu));
    return 0;
}
▶ Experimente
TEXT
Total original: 243
Total apos bonus: 258

Atribuição e Comparação de struct

Atribuição

Structs do mesmo tipo podem ser atribuídas diretamente — os membros são copiados um a um:

C
struct Student s1 = {"Zhang", 20, 89.5};
struct Student s2;
s2 = s1;

Comparação

⚠️ Atenção: Structs não podem ser comparadas com == ou !=. Você deve comparar os membros individualmente:

C
int equal(struct Student *a, struct Student *b) {
    return a->age == b->age && a->score == b->score;
}

structs Aninhadas

Um membro de uma struct pode ser outra struct:

C
struct Date {
    int year;
    int month;
    int day;
};

struct Employee {
    char name[20];
    struct Date birthday;
    float salary;
};

struct Employee e = {"Chen", {1995, 6, 15}, 8000.0};
printf("Aniversario: %d-%02d-%02d\n", e.birthday.year, e.birthday.month, e.birthday.day);
TEXT
Aniversario: 1995-06-15

❓ Perguntas Frequentes

P: Qual a diferença entre . e ->? R: . acessa membros através de uma variável struct; -> acessa membros através de um ponteiro de struct. p->name é equivalente a (*p).name.

P: Structs podem ser comparadas com ==? R: Não. C não suporta comparação com == para structs. Você deve comparar membros individualmente ou usar memcmp (mas cuidado com bytes de padding).

P: Devo passar uma struct por valor ou por ponteiro? R: Geralmente passe por ponteiro — é mais eficiente e permite modificar os dados originais. Para cenários de somente leitura, adicione proteção const: const struct Student *s.

P: Por que a definição de uma struct deve terminar com ponto e vírgula? R: Porque declarações de variáveis podem seguir imediatamente após a chave de fechamento. O ponto e vírgula diz ao compilador que a lista de declaração está completa — omiti-lo causa erro de sintaxe.

📖 Resumo

📝 Exercícios

  1. Defina struct Rectangle com largura e altura. Escreva funções float area(struct Rectangle *r) e float perimeter(struct Rectangle *r), depois calcule e imprima os resultados em main
  2. Defina struct Date e escreva uma função int date_compare(struct Date *a, struct Date *b) que retorna -1/0/1 indicando se a é anterior / igual / posterior a b
  3. Defina um array de struct de alunos (5 alunos), ordene por nota da maior para a menor, e imprima os resultados
100%