Aula 27a — Structs e typedef Parte 1 de 2

O que é, como declarar, inicializar, usar typedef e passar para funções

1. O que é uma struct?

Imagine que você quer representar um aluno no programa. Ele tem matrícula (int), nome (char[]) e nota (float). Sem struct, você precisaria de três variáveis soltas — e à medida que o programa crescesse, ficaria impossível de gerenciar. Em Java você criaria uma classe. Em C, você usa uma struct.

Uma struct (structure) é um tipo composto que agrupa variáveis de tipos diferentes sob um único nome. Cada variável dentro da struct é chamada de campo (ou field/membro). A struct define a estrutura dos dados — mas ao contrário de uma classe Java, ela não contém métodos.
Java — classe com atributos e métodos
public class Aluno {
    int    matricula;
    String nome;
    float  nota;

    // Java permite métodos:
    float calcularSituacao() {
        return nota >= 7.0f ? 1 : 0;
    }
}
C — struct com campos, sem métodos
struct Aluno {
    int   matricula;
    char  nome[50];
    float nota;

    // ❌ C não tem métodos em struct
    // funções ficam fora da struct
};
Struct em C = "classe somente dados" em Java. Pense em uma struct como uma classe Java que tem apenas atributos — sem construtores, sem métodos, sem encapsulamento por modificadores de acesso. Em C, as funções que operam sobre a struct ficam fora dela e recebem um ponteiro como parâmetro.

2. Declarando e Usando uma Struct

Sintaxe básica

/* 1. Definição do tipo struct */
struct Aluno {
    int   matricula;
    char  nome[50];
    float nota1;
    float nota2;
};

/* 2. Declaração de variável do tipo struct */
struct Aluno a1;
struct Aluno a2, a3;   // múltiplas variáveis
⚠️ Em C, a palavra struct é obrigatória ao declarar variáveis — ao contrário de Java, onde o nome da classe é suficiente. Para eliminar essa repetição, use typedef (seção 4).

Acesso com o operador ponto (.)

#include <stdio.h>
#include <string.h>

struct Aluno {
    int   matricula;
    char  nome[50];
    float nota1, nota2;
};

int main() {
    struct Aluno a;

    /* Atribuição campo por campo */
    a.matricula = 1001;
    strcpy(a.nome, "Ana Oliveira");   // strings precisam de strcpy!
    a.nota1 = 8.5f;
    a.nota2 = 9.0f;

    printf("Matrícula: %d\n",   a.matricula);
    printf("Nome:      %s\n",   a.nome);
    printf("Notas:     %.1f  %.1f\n", a.nota1, a.nota2);
    printf("Média:     %.1f\n", (a.nota1 + a.nota2) / 2.0f);

    return 0;
}
Matrícula: 1001
Nome: Ana Oliveira
Notas: 8.5 9.0
Média: 8.8
Java — acesso por ponto
Aluno a = new Aluno();
a.matricula = 1001;
a.nome = "Ana Oliveira"; // OK
a.nota1 = 8.5f;
System.out.println(a.nome);
C — acesso por ponto (igual!)
struct Aluno a;
a.matricula = 1001;
strcpy(a.nome, "Ana"); // strcpy!
a.nota1 = 8.5f;
printf("%s\n", a.nome);
O operador ponto é idêntico ao Java — a.campo funciona da mesma forma. A diferença prática é que campos char[] exigem strcpy em vez de atribuição simples, pois strings em C são arrays (veja Aula 26).

3. Inicialização de Struct

Formas de inicializar

struct Ponto {
    float x;
    float y;
};

struct Produto {
    int   codigo;
    char  nome[40];
    float preco;
};

/* Forma 1 — inicializador posicional (C89) */
struct Ponto p1 = {3.0f, 4.0f};

/* Forma 2 — designated initializers (C99, mais legível) */
struct Produto prod = {
    .codigo = 101,
    .nome   = "Caneta",
    .preco  = 2.50f
};

/* Forma 3 — struct literal (C99) */
struct Ponto p2 = (struct Ponto){.x = 1.0f, .y = 2.0f};

/* Forma 4 — zero-initialize */
struct Produto vazio = {0};   // todos os campos zerados
Java — construtor
// Com construtor explícito:
Produto p = new Produto(101,
    "Caneta", 2.50f);

// Sem construtor:
Produto p = new Produto();
p.codigo = 101;
p.nome   = "Caneta";
p.preco  = 2.50f;
C — designated initializer
// C não tem construtores —
// inicializadores são o recurso mais próximo:
struct Produto p = {
    .codigo = 101,
    .nome   = "Caneta",
    .preco  = 2.50f
};

// ou campo a campo após declaração
Designated initializers (C99) são a forma mais segura de inicializar structs — você nomeia cada campo, então a ordem não importa e campos omitidos ficam zerados automaticamente. É o padrão recomendado em código moderno.

4. typedef — Criando Apelidos de Tipo

Escrever struct Aluno toda vez é verboso. O typedef cria um apelido para um tipo, eliminando a necessidade da palavra-chave struct.

typedef cria um novo nome para um tipo existente. Não cria um tipo diferente — é apenas um alias. O compilador trata os dois nomes como idênticos. Além de structs, é muito usado para int, unsigned char e outros tipos para dar semântica ao código.

Duas formas de combinar struct + typedef

/* Forma 1 — separado (mais claro para iniciantes) */
struct Aluno {
    int   matricula;
    char  nome[50];
    float nota;
};
typedef struct Aluno Aluno;   // cria alias "Aluno" para "struct Aluno"

/* Forma 2 — combinado (mais comum na prática) */
typedef struct {
    int   matricula;
    char  nome[50];
    float nota;
} Aluno;

/* Com typedef, não precisa mais de "struct" na declaração: */
Aluno a1;          // ✅ limpo
struct Aluno a2;   // ✅ ainda funciona na Forma 1
Java — nome da classe direto
// Em Java o nome da classe
// já é o tipo — sem prefixo:
Aluno a = new Aluno();
Produto p = new Produto();
C sem typedef vs. com typedef
/* Sem typedef — verboso */
struct Aluno a;
struct Produto p;

/* Com typedef — limpo como Java */
Aluno a;
Produto p;

typedef para outros tipos

/* Dando semântica a tipos primitivos */
typedef int    Matricula;
typedef float  Nota;
typedef char   Nome[50];

Matricula id   = 1001;
Nota      n1   = 8.5f;
Nome      nome = "Ana";   // cuidado: Nome é array, usar strcpy para atribuir!

/* typedef com ponteiro — idioma comum em código C profissional */
typedef int *PonteiroInt;
PonteiroInt p = &id;    // equivale a: int *p = &id;
⚠️ typedef não cria tipos "seguros": typedef int Metros; typedef int Kilos; não impede somar metros com quilos — o compilador os trata como int idênticos. É apenas documentação.

5. Structs em Funções

Structs podem ser passadas para funções por valor (cópia de todos os campos) ou por ponteiro (8 bytes, acessa o original). Para structs grandes, o ponteiro é sempre preferível.

Java — objetos sempre por referência
void exibir(Aluno a) {
    // Java: a é referência ao objeto
    // modificações afetam o original
    System.out.println(a.nome);
}

void incrementar(Aluno a) {
    a.nota += 1.0f; // altera o original
}
C — escolha explícita: valor ou ponteiro
// Por valor: cópia — lento, não altera original
void exibir(Aluno a) {
    printf("%s\n", a.nome);
}

// Por ponteiro: rápido, altera original
void incrementar(Aluno *a) {
    a->nota += 1.0f;
}
#include <stdio.h>
#include <string.h>

typedef struct {
    int   matricula;
    char  nome[50];
    float nota1, nota2;
} Aluno;

/* Só lê — const evita modificação acidental */
void exibirAluno(const Aluno *a) {
    float media = (a->nota1 + a->nota2) / 2.0f;
    printf("%-6d %-20s %.1f  %.1f  %.1f  %s\n",
           a->matricula, a->nome,
           a->nota1, a->nota2, media,
           media >= 7.0f ? "Aprovado" : "Reprovado");
}

/* Modifica — ponteiro sem const */
void aplicarBonus(Aluno *a, float bonus) {
    a->nota1 += bonus;
    a->nota2 += bonus;
    if (a->nota1 > 10.0f) a->nota1 = 10.0f;
    if (a->nota2 > 10.0f) a->nota2 = 10.0f;
}

int main() {
    Aluno turma[] = {
        {1001, "Ana Oliveira",  8.0f, 7.5f},
        {1002, "Bruno Santos",  5.0f, 6.0f},
        {1003, "Carla Mendes",  9.5f, 9.0f}
    };
    int n = 3;

    printf("%-6s %-20s %-5s  %-5s  %-5s  %s\n",
           "Mat.", "Nome", "N1", "N2", "Med.", "Situação");
    printf("------------------------------------------------------\n");
    for (int i = 0; i < n; i++)
        exibirAluno(&turma[i]);

    printf("\nApós bônus de 0.5:\n");
    printf("------------------------------------------------------\n");
    for (int i = 0; i < n; i++)
        aplicarBonus(&turma[i], 0.5f);
    for (int i = 0; i < n; i++)
        exibirAluno(&turma[i]);

    return 0;
}
Mat. Nome N1 N2 Med. Situação
------------------------------------------------------
1001 Ana Oliveira 8.0 7.5 7.8 Aprovado
1002 Bruno Santos 5.0 6.0 5.5 Reprovado
1003 Carla Mendes 9.5 9.0 9.2 Aprovado

Após bônus de 0.5:
------------------------------------------------------
1001 Ana Oliveira 8.5 8.0 8.2 Aprovado
1002 Bruno Santos 5.5 6.5 6.0 Reprovado
1003 Carla Mendes 10.0 9.5 9.8 Aprovado
Regra prática: Use const Tipo *p quando a função só lê — eficiente (8 bytes) e protegido contra modificação acidental. Use Tipo *p quando precisa modificar. Nunca passe structs grandes por valor — o custo de cópia é proporcional ao tamanho da struct.

Quiz — Aula 27a

1. Qual a principal diferença entre uma struct em C e uma class em Java?

2. Dado typedef struct { int x; int y; } Ponto;, qual declaração de variável está correta?

3. Qual é a saída?

typedef struct { int a; int b; } Par;
int main() {
    Par p = {10, 20};
    Par q = p;
    q.a = 99;
    printf("%d %d", p.a, q.a);
}

4. Em Java, passar um objeto para um método permite que o método modifique seus campos. Em C, para uma função modificar os campos de uma struct, é necessário: