C Structs e Ponteiros (->)
Na aula de Structs, uma função que recebia um struct Aluno trabalhava numa cópia: o bônus na nota sumia quando ela terminava. Agora você vai juntar structs com ponteiros. Com o endereço da struct, uma função consegue mudar a original, structs enormes deixam de ser copiadas a cada chamada, e dá para criar structs com malloc. Tudo isso com um operador novo, que você vai ver em praticamente todo programa C: a seta, ->.
- Como apontar para uma struct e acessar os campos com
(*p).notae com a setap->nota. - Por que
*p.notanão funciona (precedência). - Como uma função muda uma struct de fora recebendo
struct Aluno *. - Por que passar structs grandes por ponteiro, e como
const struct Aluno *protege a original. - Como criar structs com
malloc(sizeof *p)e arrays de structs comcalloc. - Como a lista que cresce, da aula Array que Cresce, fica bem mais limpa com uma struct.
Todos os programas desta aula foram compilados com o gcc 15.2 na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/c, com o comando padrão do guia: gcc -std=c23 -Wall -Wextra arquivo.c -o programa. A struct de exemplo é a mesma da aula anterior: struct Aluno { char nome[50]; int idade; double nota; };.
A ideia: o número da gaveta
A secretaria da escola guarda as fichas dos alunos em gavetas numeradas. Quando a professora precisa ver a ficha da Ana, a secretaria pode tirar uma xerox (dá trabalho, e anotar na xerox não muda a ficha de verdade) ou simplesmente dizer: "está na gaveta 42". Com o número da gaveta, a professora lê a ficha original e, se puder, corrige a nota lá mesmo.
| Na secretaria | Em C |
|---|---|
| Tirar uma xerox da ficha | passar a struct por valor: void f(struct Aluno a) |
| Dar o número da gaveta | passar o endereço: f(&ana), com void f(struct Aluno *a) |
| Ir até a gaveta e ler o campo "Nota" | a->nota |
| "Pode olhar, mas não escreva" | const struct Aluno *a |
1. Ponteiro para struct e a seta
Um ponteiro para struct funciona como qualquer ponteiro: guarda um endereço, que você pega com &. Para chegar num campo, há duas escritas que fazem exatamente a mesma coisa:
#include <stdio.h>
struct Aluno {
char nome[50];
int idade;
double nota;
};
int main(void) {
struct Aluno ana = {"Ana", 20, 8.5};
struct Aluno *p = &ana;
printf("%s\n", (*p).nome);
printf("%s\n", p->nome);
p->nota = 10.0;
printf("Nota da Ana: %.1f\n", ana.nota);
return 0;
}
- struct Aluno *p = &ana;
pé um "ponteiro parastruct Aluno" e guarda o endereço daana. O*na declaração diz "é um ponteiro".- (*p).nome
- Primeiro
*psegue o endereço e chega na structana; depois.nomepega o campo. Os parênteses são obrigatórios: o ponto é mais forte que o*. - p->nome
- A seta (um hífen e um sinal de maior, sem espaço) é um atalho para
(*p).nome. Mesma coisa, menos símbolos. É a forma que todo mundo usa. - p->nota = 10.0;
- Muda o campo da própria
ana, porquepaponta para ela. Não existe cópia nenhuma aqui.
$ gcc -std=c23 -Wall -Wextra seta.c -o seta $ ./seta Ana Ana Nota da Ana: 10.0
Regra para lembrar: tem a struct → ponto (ana.nota); tem um ponteiro para a struct → seta (p->nota).
Por que não *p.nota?
Sem os parênteses, o C lê *p.nota como *(p.nota): "pegue o campo nota de p e siga esse endereço". Mas p é um ponteiro, não uma struct, e não tem campos:
$ gcc -std=c23 -Wall -Wextra semparen.c -o semparen
semparen.c: In function ‘main’:
semparen.c:12:24: error: ‘p’ is a pointer; did you mean to use ‘->’?
12 | printf("%.1f\n", *p.nota);
| ^
| ->
«…»
‘p’ is a pointer; did you mean to use ‘->’? = "p é um ponteiro; você quis usar ->?". O gcc até mostra onde pôr a seta. Foi justamente para fugir dessa armadilha de precedência (veja Precedência) que a seta foi inventada.
2. Uma função que muda a struct
Agora o problema do bônus perdido tem solução. A função recebe o endereço da struct e mexe nela pela seta, como você viu com variáveis simples em Passar por Ponteiro:
#include <stdio.h>
struct Aluno {
char nome[50];
int idade;
double nota;
};
void dar_bonus(struct Aluno *a, double pontos) {
a->nota += pontos;
}
void mostrar(const struct Aluno *a) {
printf("%s (%d anos): %.1f\n", a->nome, a->idade, a->nota);
}
int main(void) {
struct Aluno ana = {"Ana", 20, 6.5};
mostrar(&ana);
dar_bonus(&ana, 1.0);
mostrar(&ana);
return 0;
}
| Linha | O que faz |
|---|---|
void dar_bonus(struct Aluno *a, double pontos) | Recebe o endereço de uma struct. A função pode mudar a original. |
a->nota += pontos; | Soma os pontos na nota da struct de fora (a do main). |
void mostrar(const struct Aluno *a) | Também recebe o endereço (nada de copiar 64 bytes), mas o const promete só olhar. A seção 3 mostra o que o const impede. |
dar_bonus(&ana, 1.0); | Na chamada, o & pega o endereço da ana. |
$ gcc -std=c23 -Wall -Wextra bonus.c -o bonus $ ./bonus Ana (20 anos): 6.5 Ana (20 anos): 7.5 « agora o bônus ficou
Missão: escreva o corpo da função aniversario, que soma 1 na idade do aluno recebido. Use a seta. A tela deve mostrar Ana agora tem 21 anos.
#include <stdio.h>
struct Aluno {
char nome[50];
int idade;
double nota;
};
void aniversario(struct Aluno *a) {
/* some 1 na idade de a, usando a seta */
}
int main(void) {
struct Aluno ana = {"Ana", 20, 8.5};
aniversario(&ana);
printf("%s agora tem %d anos\n", ana.nome, ana.idade);
return 0;
}
#include <stdio.h>
struct Aluno {
char nome[50];
int idade;
double nota;
};
void aniversario(struct Aluno *a) {
a->idade++;
}
int main(void) {
struct Aluno ana = {"Ana", 20, 8.5};
aniversario(&ana);
printf("%s agora tem %d anos\n", ana.nome, ana.idade);
return 0;
}
Rodando o código inicial, o Clang avisa unused parameter 'a' ("parâmetro a não usado"): a função recebe o aluno e não faz nada com ele. E repare: a->idade++ funciona sem parênteses, porque a seta é mais forte que o ++.
3. Structs grandes e o const
Passar uma struct por valor copia todos os bytes dela. Para um Aluno de 64 bytes, tudo bem. Mas structs podem ser enormes. Uma turma com 30 alunos dentro:
#include <stdio.h>
struct Aluno {
char nome[50];
int idade;
double nota;
};
struct Turma {
char sala[10];
struct Aluno alunos[30];
int quantos;
};
double media_copia(struct Turma t) {
double soma = 0.0;
for (int i = 0; i < t.quantos; i++) {
soma += t.alunos[i].nota;
}
return t.quantos > 0 ? soma / t.quantos : 0.0;
}
double media(const struct Turma *t) {
double soma = 0.0;
for (int i = 0; i < t->quantos; i++) {
soma += t->alunos[i].nota;
}
return t->quantos > 0 ? soma / t->quantos : 0.0;
}
int main(void) {
struct Turma t = {.sala = "8A", .quantos = 3};
t.alunos[0] = (struct Aluno){"Ana", 20, 8.5};
t.alunos[1] = (struct Aluno){"Bruno", 19, 6.0};
t.alunos[2] = (struct Aluno){"Carla", 22, 9.5};
printf("Uma struct Turma ocupa %zu bytes\n", sizeof(struct Turma));
printf("Um ponteiro ocupa %zu bytes\n", sizeof(struct Turma *));
printf("Média (cópia): %.2f\n", media_copia(t));
printf("Média (ponteiro): %.2f\n", media(&t));
return 0;
}
$ gcc -std=c23 -Wall -Wextra grande.c -o grande $ ./grande Uma struct Turma ocupa 1944 bytes Um ponteiro ocupa 8 bytes Média (cópia): 8.00 Média (ponteiro): 8.00
As duas funções dão o mesmo resultado. Mas cada chamada de media_copia copia 1944 bytes; cada chamada de media passa só 8 bytes (um endereço). Num laço que chama a função milhares de vezes, a diferença aparece. Repare em t->alunos[i].nota: seta para entrar na turma (que é um ponteiro), colchetes para escolher o aluno e ponto para o campo (porque alunos[i] já é uma struct, não um ponteiro).
O editor roda em WebAssembly de 32 bits, onde endereços ocupam 4 bytes, e não 8 como no gcc de 64 bits. A ideia é a mesma: o ponteiro é minúsculo perto da struct.
O const protege a original
Passar por ponteiro tem um risco: a função pode mudar a original, mesmo quando só devia ler. O const fecha essa porta. Se uma função recebe const struct Aluno * e tenta mudar um campo, o compilador barra:
void mostrar(const struct Aluno *a) {
a->nota = 10.0;
printf("%s: %.1f\n", a->nome, a->nota);
}
$ gcc -std=c23 -Wall -Wextra constmuda.c -o constmuda
constmuda.c: In function ‘mostrar’:
constmuda.c:10:13: error: assignment of member ‘nota’ in read-only object
10 | a->nota = 10.0;
| ^
assignment of member ‘nota’ in read-only object = "atribuição ao campo nota de um objeto somente leitura". Se a função deve mudar a ficha, tire o const; se não deve, o erro acabou de achar um engano seu. A regra prática: struct que a função só lê → const struct X *; struct que a função muda → struct X *. Mais detalhes em const e Ponteiros.
4. Structs no heap: malloc
Uma struct também pode morar no heap, reservada com malloc. É assim quando uma função precisa criar uma struct que continue existindo depois que ela termina. O resultado é um ponteiro, então tudo é com seta:
#include <stdio.h>
#include <stdlib.h>
struct Aluno {
char nome[50];
int idade;
double nota;
};
struct Aluno *novo_aluno(const char *nome, int idade, double nota) {
struct Aluno *a = malloc(sizeof *a);
if (a == NULL) {
return NULL;
}
snprintf(a->nome, sizeof a->nome, "%s", nome);
a->idade = idade;
a->nota = nota;
return a;
}
int main(void) {
struct Aluno *eva = novo_aluno("Eva", 18, 7.0);
if (eva == NULL) {
printf("Sem memória!\n");
return 1;
}
printf("%s, %d anos, nota %.1f\n", eva->nome, eva->idade, eva->nota);
free(eva);
return 0;
}
- struct Aluno *a = malloc(sizeof *a);
- Reserva espaço para uma struct. O
sizeof *aquer dizer "o tamanho daquilo para ondeaaponta", ou seja,sizeof(struct Aluno). É o jeito preferido: se um dia o tipo deamudar, o tamanho acompanha sozinho. (Cuidado:sizeof a, sem o*, é o tamanho do ponteiro; veja "Quando dá errado".) - if (a == NULL) return NULL;
- Confira sempre o
malloc. - snprintf(a->nome, sizeof a->nome, "%s", nome);
- Copia o nome sem passar dos 50 bytes do campo.
- return a;
- Devolve o endereço. A struct continua viva no heap depois que a função termina, e quem recebeu é dono dela.
- free(eva);
- Um
malloc, umfree.
$ gcc -std=c23 -Wall -Wextra -g novo.c -o novo $ ./novo Eva, 18 anos, nota 7.0 $ valgrind --leak-check=full ./novo 2>&1 | grep -E "Eva|heap usage|All heap|ERROR SUM" Eva, 18 anos, nota 7.0 ==72414== total heap usage: 2 allocs, 2 frees, 4,160 bytes allocated ==72414== All heap blocks were freed -- no leaks are possible ==72414== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Dois blocos: os 64 bytes da Eva e o buffer de saída do printf. Os dois liberados, nenhum erro.
Um array de structs no heap
Para uma turma cujo tamanho só se sabe na hora, reserve n structs de uma vez. Com calloc, todas já nascem zeradas (veja calloc e realloc):
#include <stdio.h>
#include <stdlib.h>
struct Aluno {
char nome[50];
int idade;
double nota;
};
int main(void) {
size_t n = 3;
struct Aluno *turma = calloc(n, sizeof *turma);
if (turma == NULL) {
return 1;
}
turma[0] = (struct Aluno){"Ana", 20, 8.5};
turma[1] = (struct Aluno){"Bruno", 19, 6.0};
(turma + 2)->nota = 9.5;
snprintf(turma[2].nome, sizeof turma[2].nome, "Carla");
for (size_t i = 0; i < n; i++) {
printf("%s: %.1f\n", turma[i].nome, turma[i].nota);
}
free(turma);
return 0;
}
$ gcc -std=c23 -Wall -Wextra turma.c -o turma $ ./turma Ana: 8.5 Bruno: 6.0 Carla: 9.5
Atenção a um detalhe que confunde: turma é um ponteiro, mas turma[2] já é uma struct (o colchete segue o endereço, veja Ponteiros e Arrays). Por isso é turma[2].nome, com ponto. A forma (turma + 2)->nota chega no mesmo lugar por outro caminho: turma + 2 é o endereço do terceiro aluno, e a seta entra nele. Prefira os colchetes, que são mais fáceis de ler.
5. A lista que cresce, agora com struct
Na aula Array que Cresce, a lista de preços precisava de três variáveis soltas e de funções com parâmetros como double **precos e (*precos)[*tamanho]. Ficou prometido que a struct ia arrumar isso. Aqui está: as três variáveis viram uma struct Lista, e as funções recebem um ponteiro para ela.
#include <stdio.h>
#include <stdlib.h>
struct Lista {
double *dados;
size_t tamanho;
size_t capacidade;
};
bool adicionar(struct Lista *l, double valor) {
if (l->tamanho == l->capacidade) {
size_t nova = (l->capacidade == 0) ? 4 : l->capacidade * 2;
double *maior = realloc(l->dados, nova * sizeof *maior);
if (maior == NULL) {
return false;
}
l->dados = maior;
l->capacidade = nova;
}
l->dados[l->tamanho] = valor;
l->tamanho++;
return true;
}
void liberar(struct Lista *l) {
free(l->dados);
*l = (struct Lista){0};
}
int main(void) {
struct Lista precos = {0};
double preco;
while (scanf("%lf", &preco) == 1) {
if (!adicionar(&precos, preco)) {
printf("Sem memória!\n");
break;
}
}
double total = 0.0;
for (size_t i = 0; i < precos.tamanho; i++) {
total += precos.dados[i];
}
printf("%zu itens (capacidade %zu)\n", precos.tamanho, precos.capacidade);
printf("Total: R$ %.2f\n", total);
liberar(&precos);
return 0;
}
| Antes (variáveis soltas) | Agora (struct) |
|---|---|
double *precos = NULL; size_t tamanho = 0; size_t capacidade = 0; | struct Lista precos = {0}; |
adicionar(double **precos, size_t *tamanho, size_t *capacidade, double valor) | adicionar(struct Lista *l, double valor) |
(*precos)[*tamanho] = valor; | l->dados[l->tamanho] = valor; |
(*tamanho)++; | l->tamanho++; |
adicionar(&precos, &tamanho, &capacidade, preco) | adicionar(&precos, preco) |
Três linhas para zerar no liberar | *l = (struct Lista){0}; (um literal composto zerado copiado por cima) |
$ gcc -std=c23 -Wall -Wextra -g lista.c -o lista $ ./lista < compras.txt 10 itens (capacidade 16) Total: R$ 72.73 $ valgrind --leak-check=full ./lista < compras.txt 2>&1 | tail -5 ==72473== ==72473== All heap blocks were freed -- no leaks are possible ==72473== ==72473== For lists of detected and suppressed errors, rerun with: -s ==72473== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
O compras.txt é o mesmo da aula Array que Cresce, com dez preços. A lógica é idêntica à da outra aula (começa em 4, dobra, confere o realloc, libera no fim); o resultado também. Mudou só a organização: o que anda junto fica junto. Esse padrão (uma struct com os dados e funções que recebem um ponteiro para ela) é o jeito C de criar "tipos com comportamento", e você vai usá-lo nas listas ligadas, pilhas e filas e em todo projeto maior.
Uma struct pode ter um campo que é ponteiro para o próprio tipo: struct No { int valor; struct No *proximo; };. Cada nó sabe onde está o próximo, como os vagões de um trem. Não pode ter uma struct No inteira dentro de si (ela teria tamanho infinito), mas um ponteiro para ela, sim. Essa é a base das Listas Ligadas.
Ponto ou seta? Por valor ou por ponteiro? Qual usar
| Você tem… | Use | Exemplo |
|---|---|---|
| uma variável struct | ponto | ana.nota |
| um ponteiro para struct | seta | p->nota |
um elemento de array de structs (mesmo que o array venha de malloc) | colchetes e ponto | turma[i].nota |
| um ponteiro para struct que tem uma struct dentro | seta, depois ponto | p->nascimento.dia |
| Parâmetro | O que acontece | Quando usar |
|---|---|---|
const struct Aluno *a | A função olha a original, sem copiar e sem poder mudar. | O padrão para structs que a função só lê. |
struct Aluno *a | A função mexe na original. | Quando o objetivo é mudar (dar_bonus, adicionar). |
struct Aluno a | A função recebe uma cópia inteira. | Structs pequenas (uma struct Data de 12 bytes, um ponto {x, y}) quando a função quer uma cópia para mexer à vontade. |
Quando dá errado
struct Aluno ana = {"Ana", 20, 8.5};
struct Aluno *p = &ana;
printf("%.1f\n", p.nota); /* ponto num ponteiro */
printf("%.1f\n", ana->nota); /* seta numa struct */
$ gcc -std=c23 -Wall -Wextra pontoponteiro.c -o pontoponteiro
pontoponteiro.c: In function ‘main’:
pontoponteiro.c:12:23: error: ‘p’ is a pointer; did you mean to use ‘->’?
12 | printf("%.1f\n", p.nota);
| ^
| ->
pontoponteiro.c:13:25: error: invalid type argument of ‘->’ (have ‘struct Aluno’)
13 | printf("%.1f\n", ana->nota);
| ^~
«…»
O que significa: o primeiro erro é o ponto usado num ponteiro (o gcc sugere a seta). O segundo é o contrário: invalid type argument of ‘->’ (have ‘struct Aluno’) = "argumento de tipo inválido para -> (é uma struct Aluno)": a seta só funciona com ponteiros.
Como resolver: olhe a declaração da variável. Tem * (struct Aluno *p)? Seta. Não tem? Ponto.
O que aconteceu: a função recebeu const struct Aluno * e tentou mudar um campo (seção 3).
Como resolver: se a função deve mudar a struct, tire o const do parâmetro; se não deve, apague a linha que muda.
NULL: "Falha de segmentação"
#include <stdio.h>
#include <string.h>
struct Aluno {
char nome[50];
int idade;
double nota;
};
struct Aluno *procurar(struct Aluno turma[], size_t n, const char *nome) {
for (size_t i = 0; i < n; i++) {
if (strcmp(turma[i].nome, nome) == 0) {
return &turma[i];
}
}
return NULL;
}
int main(void) {
struct Aluno turma[] = {{"Ana", 20, 8.5}, {"Bruno", 19, 6.0}};
struct Aluno *p = procurar(turma, 2, "Bruno");
printf("Nota do Bruno: %.1f\n", p->nota);
p = procurar(turma, 2, "Zeca");
printf("Nota do Zeca: %.1f\n", p->nota);
return 0;
}
$ gcc -std=c23 -Wall -Wextra -g nulo.c -o nulo $ ./nulo Nota do Bruno: 6.0 Falha de segmentação (imagem do núcleo gravada) ./nulo
O que aconteceu: não existe Zeca na turma, e procurar devolveu NULL, o "endereço nenhum". O p->nota tentou ler um campo no endereço nenhum, e o sistema derrubou o programa. No editor do guia acontece o mesmo: Falha de segmentação (imagem do núcleo gravada), código 139.
Como resolver: toda função que pode devolver NULL (malloc, procurar, fopen…) pede um if (p == NULL) antes da seta. Veja Ponteiro Nulo.
malloc(sizeof a): "allocation of insufficient size"
struct Aluno *a = malloc(sizeof a); /* faltou o * */
$ gcc -std=c23 -Wall -Wextra -g tamanho.c -o tamanho
tamanho.c: In function ‘novo_aluno’:
tamanho.c:11:23: warning: allocation of insufficient size ‘8’ for type ‘struct Aluno’ with size ‘64’ [-Walloc-size]
11 | struct Aluno *a = malloc(sizeof a);
| ^~~~~~
$ gcc -std=c23 -Wall -Wextra -g -fsanitize=address tamanho.c -o tamanho
$ ./tamanho
=================================================================
==72752==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7321299e0044 at pc 0x5913c9918362 bp 0x7ffc4dbffc20 sp 0x7ffc4dbffc10
WRITE of size 4 at 0x7321299e0044 thread T0
#0 0x5913c9918361 in novo_aluno /home/ana/c/tamanho.c:16
#1 0x5913c99183d0 in main /home/ana/c/tamanho.c:22
«…»
0x7321299e0044 is located 44 bytes after 8-byte region [0x7321299e0010,0x7321299e0018)
«…»
O que significa: sizeof a é o tamanho do ponteiro a (8 bytes), não da struct (64). O gcc 15 percebe e avisa: allocation of insufficient size ‘8’ for type ‘struct Aluno’ with size ‘64’ = "reserva de tamanho insuficiente: 8 bytes para um tipo de 64". O sanitizer mostra a consequência: a linha 16 (a->idade = idade;) escreveu 44 bytes depois do fim do bloco de 8. No editor do guia, o Clang dá o mesmo aviso (com '4', o tamanho do ponteiro lá), e o programa pode até mostrar a Eva certinha: um estouro de memória calado.
Como resolver: malloc(sizeof *a), com o asterisco: "o tamanho daquilo para onde a aponta". Ou malloc(sizeof(struct Aluno)).
Referência rápida
| Escrita | Significado |
|---|---|
struct Aluno *p = &ana; | Ponteiro para uma struct que já existe. |
p->nota | Campo pela seta; o mesmo que (*p).nota. |
*p.nota | Erro: é lido como *(p.nota). |
void f(struct Aluno *a) | A função pode mudar a struct de fora; chame com f(&ana). |
void f(const struct Aluno *a) | A função só lê, sem copiar. |
struct Aluno *a = malloc(sizeof *a); | Uma struct no heap (confira NULL; termine com free(a)). |
struct Aluno *t = calloc(n, sizeof *t); | n structs zeradas no heap; acesse com t[i].nota. |
p->nascimento.dia | Struct dentro de struct, a partir de um ponteiro. |
*p = (struct Lista){0}; | Zera a struct apontada inteira de uma vez. |
struct No { int valor; struct No *proximo; }; | Struct com ponteiro para o próprio tipo (listas ligadas). |
Exercícios
1. A seta
p é um struct Aluno *. Complete com a seta para mostrar a idade:
2. Sem a seta
Escreva p->nota da outra forma, com * e ponto:
3. Reservar uma struct
Complete para reservar uma struct Aluno no heap, do jeito que acompanha o tipo do ponteiro:
4. Uma função só precisa mostrar uma struct grande. Qual parâmetro é o melhor?
5. turma veio de calloc(n, sizeof *turma). Como se lê o nome do primeiro aluno?
6. O que sizeof p mede, quando p é um struct Aluno *?
Missão final: conserte a fábrica de alunos
Missão: a função novo_aluno devolve o endereço de uma variável local, que deixa de existir quando a função termina (o Clang avisa). No gcc, esse programa diz Sem memória!; no editor, parece funcionar por sorte. Conserte: reserve a struct com malloc(sizeof *a), confira o NULL e preencha os campos com a seta. O programa deve mostrar Eva, 18 anos, nota 7.0 e compilar sem nenhum aviso.
#include <stdio.h>
#include <stdlib.h>
struct Aluno {
char nome[50];
int idade;
double nota;
};
struct Aluno *novo_aluno(const char *nome, int idade, double nota) {
struct Aluno a = {0};
snprintf(a.nome, sizeof a.nome, "%s", nome);
a.idade = idade;
a.nota = nota;
return &a;
}
int main(void) {
struct Aluno *eva = novo_aluno("Eva", 18, 7.0);
if (eva == NULL) {
printf("Sem memória!\n");
return 1;
}
printf("%s, %d anos, nota %.1f\n", eva->nome, eva->idade, eva->nota);
free(eva);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
struct Aluno {
char nome[50];
int idade;
double nota;
};
struct Aluno *novo_aluno(const char *nome, int idade, double nota) {
struct Aluno *a = malloc(sizeof *a);
if (a == NULL) {
return NULL;
}
snprintf(a->nome, sizeof a->nome, "%s", nome);
a->idade = idade;
a->nota = nota;
return a;
}
int main(void) {
struct Aluno *eva = novo_aluno("Eva", 18, 7.0);
if (eva == NULL) {
printf("Sem memória!\n");
return 1;
}
printf("%s, %d anos, nota %.1f\n", eva->nome, eva->idade, eva->nota);
free(eva);
return 0;
}
Resumo
- Um ponteiro para struct guarda o endereço dela:
struct Aluno *p = &ana;. p->notaé o mesmo que(*p).nota. Sem parênteses,*p.notaé erro.- Variável struct → ponto; ponteiro para struct → seta;
turma[i]já é struct → ponto. - Para uma função mudar a struct, passe o endereço:
void f(struct Aluno *a)ef(&ana). - Para só ler sem copiar, use
const struct Aluno *: o compilador barra qualquer mudança. - Structs no heap:
malloc(sizeof *p)(com o*!), confira oNULL, termine comfree. - Uma struct com os dados e funções que recebem um ponteiro para ela organiza o código: a lista que cresce virou
adicionar(&lista, valor).