Operador seta, structs aninhadas, arrays de structs, sizeof e padding
struct, typedef e passagem por ponteiro — temas da Parte 1.
->) vs. Ponto (.)Existem dois operadores de acesso a campos de struct — qual usar depende se você tem a variável diretamente ou um ponteiro para ela:
| Situação | Operador | Exemplo | Equivalente |
|---|---|---|---|
| Variável é a própria struct | . (ponto) |
aluno.nota |
— |
| Variável é um ponteiro para struct | -> (seta) |
ptr->nota |
(*ptr).nota |
typedef struct { int x; int y; } Ponto;
Ponto p = {3, 4};
Ponto *pp = &p;
/* Estas três expressões acessam o mesmo campo: */
printf("%d\n", p.x); // variável — usa ponto
printf("%d\n", pp->x); // ponteiro — usa seta
printf("%d\n", (*pp).x); // desreferência explícita + ponto (verboso)
/* ❌ Erros comuns: */
// printf("%d\n", p->x); — p é struct, não ponteiro: use ponto
// printf("%d\n", pp.x); — pp é ponteiro: use seta
Aluno a = new Aluno(); a.nota = 8.5f; // Java usa . sempre // O objeto em Java é sempre // acessado por referência, // mas a sintaxe é sempre ponto
Aluno a; // struct direta Aluno *p = &a; // ponteiro para struct a.nota = 8.5f; // ponto p->nota = 8.5f; // seta (modifica o mesmo a)
*, use ->. Se não tem *, use ..
Um campo de uma struct pode ser outra struct — o equivalente de um atributo cujo tipo é outra classe em Java:
#include <stdio.h>
#include <string.h>
typedef struct {
int dia;
int mes;
int ano;
} Data;
typedef struct {
char nome[60];
char cpf[15];
Data nascimento; // campo do tipo Data (struct dentro de struct)
float salario;
} Funcionario;
void exibir(const Funcionario *f) {
printf("Nome: %s\n", f->nome);
printf("CPF: %s\n", f->cpf);
printf("Nascimento: %02d/%02d/%04d\n",
f->nascimento.dia, // acesso: seta + ponto
f->nascimento.mes,
f->nascimento.ano);
printf("Salário: R$ %.2f\n\n", f->salario);
}
int main() {
Funcionario f1 = {
.nome = "Carlos Mota",
.cpf = "123.456.789-00",
.nascimento = {.dia = 15, .mes = 8, .ano = 1990},
.salario = 5800.0f
};
Funcionario f2;
strcpy(f2.nome, "Beatriz Lima");
strcpy(f2.cpf, "987.654.321-00");
f2.nascimento.dia = 22;
f2.nascimento.mes = 3;
f2.nascimento.ano = 1995;
f2.salario = 4300.0f;
exibir(&f1);
exibir(&f2);
return 0;
}
f->nascimento.dia — seta para o primeiro nível (f é ponteiro para Funcionario), ponto para o segundo (nascimento é um campo direto, não ponteiro). Em Java seria f.nascimento.dia sempre.
Arrays de structs são extremamente comuns — é a forma natural de representar coleções de entidades como turmas, catálogos ou filas:
#include <stdio.h>
#include <string.h>
typedef struct {
char nome[40];
float preco;
int estoque;
} Produto;
/* Encontra o produto mais barato — retorna ponteiro para ele */
Produto *maisBarato(Produto *cat, int n) {
Produto *menor = &cat[0];
for (int i = 1; i < n; i++)
if (cat[i].preco < menor->preco)
menor = &cat[i];
return menor;
}
/* Ordena por preço (selection sort) */
void ordenarPorPreco(Produto *cat, int n) {
for (int i = 0; i < n - 1; i++) {
int minIdx = i;
for (int j = i + 1; j < n; j++)
if (cat[j].preco < cat[minIdx].preco)
minIdx = j;
if (minIdx != i) {
Produto temp = cat[i]; // structs podem ser copiadas por =
cat[i] = cat[minIdx];
cat[minIdx] = temp;
}
}
}
int main() {
Produto catalogo[] = {
{"Caderno", 12.90f, 50},
{"Caneta", 2.50f, 200},
{"Mochila", 89.90f, 15},
{"Borracha", 1.80f, 100},
{"Régua", 4.50f, 75}
};
int n = 5;
Produto *barato = maisBarato(catalogo, n);
printf("Mais barato: %s (R$ %.2f)\n\n", barato->nome, barato->preco);
ordenarPorPreco(catalogo, n);
printf("Catálogo ordenado por preço:\n");
printf("%-15s %8s %8s\n", "Produto", "Preço", "Estoque");
printf("-------------------------------\n");
for (int i = 0; i < n; i++)
printf("%-15s R$%6.2f %8d\n",
catalogo[i].nome,
catalogo[i].preco,
catalogo[i].estoque);
return 0;
}
=: Ao contrário de arrays (que decaem para ponteiro), structs em C podem ser copiadas com = — o compilador copia campo a campo automaticamente. Isso é equivalente a criar um novo objeto com os mesmos valores em Java. Por isso o swap na função de ordenação funciona corretamente com uma variável Produto temp.
sizeof e PaddingO tamanho de uma struct não é necessariamente a soma dos tamanhos dos seus campos. O compilador pode inserir bytes de preenchimento (padding) entre campos para garantir alinhamento de memória eficiente:
#include <stdio.h>
typedef struct {
char c; // 1 byte
int i; // 4 bytes — alinhado em múltiplo de 4
char c2; // 1 byte
} Exemplo;
typedef struct {
int i; // 4 bytes
char c; // 1 byte
char c2; // 1 byte
} ExemploOtimizado;
int main() {
printf("sizeof(char) = %zu\n", sizeof(char));
printf("sizeof(int) = %zu\n", sizeof(int));
printf("sizeof(Exemplo) = %zu\n", sizeof(Exemplo));
printf("sizeof(ExemploOtimizado) = %zu\n", sizeof(ExemploOtimizado));
return 0;
}
| Aspecto | C (struct) |
Java (class) |
|---|---|---|
| Sintaxe de declaração | struct Nome { campos; }; |
class Nome { campos; métodos; } |
| Métodos | ❌ Não — funções ficam fora da struct | ✅ Sim — métodos são parte da classe |
| Construtor | ❌ Não — usar função de inicialização | ✅ Sim — construtores automáticos |
| Encapsulamento | ❌ Todos os campos são públicos | ✅ private, protected, public |
| Herança | ❌ Não suportada | ✅ extends |
| Passagem para função | Por valor (cópia) ou por ponteiro (explícito) | Referência automática (implícita) |
| Acesso a campos | var.campo ou ptr->campo |
Sempre obj.campo |
Cópia com = |
✅ Copia todos os campos | ❌ Copia apenas a referência |
| Padding de memória | ⚠️ Visível — ordem dos campos afeta sizeof |
✅ Transparente — JVM gerencia |
struct como uma classe Java que só tem atributos públicos, sem construtores, sem métodos e sem herança. As "funções membro" ficam fora da struct e recebem um ponteiro como primeiro parâmetro — é o padrão que as aulas de listas lineares (Aulas 29+) vão explorar extensivamente.