C Structs

Até aqui, cada informação morava numa variável separada: nome, idade, nota. Para uma aluna, funciona. Para uma turma inteira, vira uma bagunça de variáveis soltas e de arrays paralelos que precisam andar sempre na mesma ordem. Uma struct junta várias informações que andam juntas num tipo novo, criado por você: o tipo struct Aluno, que tem nome, idade e nota. Nesta aula você vai declarar a sua primeira struct, preencher, guardar várias num array, copiar e mostrar na tela.

Você vai aprender
  • O que é uma struct e como declarar uma: struct Aluno { … };.
  • Como criar uma variável do tipo struct Aluno e acessar os campos com o ponto: a.nota.
  • Por que um campo sem valor guarda lixo, e como zerar tudo com {0} ou {} (C23).
  • Como preencher com chaves, pela ordem ou pelo nome do campo (.nome = …, C99).
  • Como guardar uma turma inteira num array de structs.
  • Como copiar uma struct com =, e por que == não compara structs.
  • Como colocar uma struct dentro de outra e como mostrar os campos com printf.

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 (veja Compilar com gcc). Você pode repetir cada um no editor do guia.


A ideia: uma ficha de cadastro

Pense na secretaria de uma escola. Para cada aluno existe uma ficha de papel, sempre com os mesmos espaços: Nome, Idade, Nota. A ficha em branco é o modelo; cada ficha preenchida é um aluno de verdade.

A struct é o modelo da ficha. Ela diz quais espaços existem e o tipo de cada um. Depois você cria quantas fichas preenchidas quiser, todas no mesmo formato.

Na secretariaEm CExemplo
O modelo da fichaa struct (um tipo novo)struct Aluno { … };
Cada espaço da fichaum campo (ou membro)int idade;
Uma ficha preenchidauma variável do tipo struct Alunostruct Aluno a;
Ler o espaço "Idade" de uma fichao operador pontoa.idade

O nome vem do inglês structure, "estrutura": uma estrutura de dados montada com peças menores. Em C, uma struct guarda só dados: não tem funções dentro, nem construtores, nem partes "privadas", como as classes de outras linguagens. As funções que trabalham com ela ficam do lado de fora, como sempre.


1. Declarar a struct e criar uma variável

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

struct Aluno {
    char nome[50];
    int idade;
    double nota;
};

int main(void) {
    struct Aluno a;
    strcpy(a.nome, "Ana");
    a.idade = 20;
    a.nota = 8.5;

    printf("%s tem %d anos e tirou %.1f\n", a.nome, a.idade, a.nota);

    a.nota = 9.0;
    printf("Nota corrigida: %.1f\n", a.nota);
    return 0;
}
struct Aluno
Começa a declaração de um tipo novo. Aluno é a etiqueta (tag) da struct. Por costume, começa com maiúscula, para não confundir com variáveis.
{ … };
Entre as chaves ficam os campos. Repare no ponto e vírgula depois da chave: na struct ele é obrigatório (esquecer é um erro clássico, que você verá em "Quando dá errado").
char nome[50];
Um campo: tipo + nome, igual a declarar uma variável. Este é um array de 50 char: o nome fica guardado dentro da ficha (até 49 letras mais o '\0').
int idade; double nota;
Os campos podem ter tipos diferentes entre si. Essa é a grande diferença para um array, em que todos os elementos têm o mesmo tipo.
struct Aluno a;
Cria uma variável a do tipo struct Aluno. Em C, o nome do tipo são as duas palavras: struct Aluno. Escrever só Aluno a; é erro (dá para encurtar com typedef, na próxima aula, mas uma coisa de cada vez).
strcpy(a.nome, "Ana");
O ponto entra na ficha a e pega o campo nome. Como nome é um array de char, o texto entra com strcpy (ou snprintf), não com = (veja Funções de String).
a.idade = 20;
Campos numéricos se comportam como variáveis comuns: dá para guardar, ler e mudar com =.
$ gcc -std=c23 -Wall -Wextra aluno.c -o aluno
$ ./aluno
Ana tem 20 anos e tirou 8.5
Nota corrigida: 9.0
Onde declarar a struct

Declare a struct fora do main, lá em cima, depois dos #include. Assim todas as funções do arquivo conhecem o tipo struct Aluno. Em projetos maiores, ela vai para um arquivo .h (veja Arquivos .h e .c).


2. Campos sem valor: lixo da memória

O que tem em a.idade se você nunca guardou nada lá? Uma variável local sem valor inicial não vale zero: vale o que estava naquele pedaço da memória antes (veja Variáveis). Com os campos de uma struct é igual:

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

struct Aluno {
    char nome[50];
    int idade;
    double nota;
};

int main(void) {
    struct Aluno a;
    strcpy(a.nome, "Ana");
    printf("%s, %d anos, nota %g\n", a.nome, a.idade, a.nota);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra lixo.c -o lixo
lixo.c: In function ‘main’:
lixo.c:13:5: warning: ‘a.nota’ is used uninitialized [-Wuninitialized]
   13 |     printf("%s, %d anos, nota %g\n", a.nome, a.idade, a.nota);
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
lixo.c:11:18: note: ‘a’ declared here
   11 |     struct Aluno a;
      |                  ^
lixo.c:13:5: warning: ‘a.idade’ is used uninitialized [-Wuninitialized]
   13 |     printf("%s, %d anos, nota %g\n", a.nome, a.idade, a.nota);
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
lixo.c:11:18: note: ‘a’ declared here
   11 |     struct Aluno a;
      |                  ^
$ ./lixo
Ana, 0 anos, nota 0         « zero desta vez, por sorte

O gcc avisou duas vezes: ‘a.nota’ is used uninitialized = "a.nota é usado sem ter recebido valor". Nesta execução saiu zero, mas é sorte: o C não promete nada, e em outro programa (ou com outra opção de compilação) sai um número sem sentido. Ler um valor que nunca foi guardado é comportamento indefinido.

A solução: começar zerado

Diferente do C++, uma struct em C não pode ter valores padrão escritos dentro dela. O jeito é zerar na hora de criar a variável, com chaves:

struct Aluno a = {0};      /* todos os campos zerados (qualquer versão do C) */
struct Aluno b = {};       /* o mesmo, a partir do C23 */

Com {0}, o primeiro campo recebe 0 e todos os que você não citou também viram zero: os números ficam 0 e o nome fica com todos os bytes '\0' (um texto vazio). As chaves vazias {} fazem o mesmo e são oficiais a partir do C23; antes disso, use {0}, que funciona em todas as versões. Crie suas structs sempre zeradas quando for preenchê-las aos poucos.

Missão: a variável a já existe, zerada. Preencha os três campos com o ponto (o nome com strcpy), para a tela mostrar Bia tem 19 anos e nota 7.5. Rode primeiro sem mudar nada e veja os zeros.

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

struct Aluno {
    char nome[50];
    int idade;
    double nota;
};

int main(void) {
    struct Aluno a = {0};
    /* preencha os três campos de a aqui */

    printf("%s tem %d anos e nota %.1f\n", a.nome, a.idade, a.nota);
    return 0;
}
#include <stdio.h>
#include <string.h>

struct Aluno {
    char nome[50];
    int idade;
    double nota;
};

int main(void) {
    struct Aluno a = {0};
    strcpy(a.nome, "Bia");
    a.idade = 19;
    a.nota = 7.5;

    printf("%s tem %d anos e nota %.1f\n", a.nome, a.idade, a.nota);
    return 0;
}

3. Preencher tudo de uma vez com chaves

Preencher campo por campo dá trabalho. Você pode criar a variável já preenchida, com os valores entre chaves. Pela ordem dos campos, ou citando o nome de cada campo com um ponto na frente (os designadores, do C99):

#include <stdio.h>

struct Aluno {
    char nome[50];
    int idade;
    double nota;
};

int main(void) {
    struct Aluno a = {"Ana", 20, 8.5};
    struct Aluno b = {"Bruno", 19};
    struct Aluno c = {0};
    struct Aluno d = {};
    struct Aluno e = {.nome = "Carla", .idade = 22, .nota = 9.5};
    struct Aluno f = {.nota = 6.0, .nome = "Davi"};

    printf("%s | %d | %.1f\n", a.nome, a.idade, a.nota);
    printf("%s | %d | %.1f\n", b.nome, b.idade, b.nota);
    printf("[%s] | %d | %.1f\n", c.nome, c.idade, c.nota);
    printf("[%s] | %d | %.1f\n", d.nome, d.idade, d.nota);
    printf("%s | %d | %.1f\n", e.nome, e.idade, e.nota);
    printf("%s | %d | %.1f\n", f.nome, f.idade, f.nota);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra chaves.c -o chaves
chaves.c: In function ‘main’:
chaves.c:11:12: warning: missing initializer for field ‘nota’ of ‘struct Aluno’ [-Wmissing-field-initializers]
   11 |     struct Aluno b = {"Bruno", 19};
      |            ^~~~~
chaves.c:6:12: note: ‘nota’ declared here
    6 |     double nota;
      |            ^~~~
$ ./chaves
Ana | 20 | 8.5
Bruno | 19 | 0.0       « faltou a nota: ficou zero
[] | 0 | 0.0          « {0}: tudo zerado, nome vazio
[] | 0 | 0.0          « {}: o mesmo (C23)
Carla | 22 | 9.5
Davi | 0 | 6.0        « designadores fora de ordem, idade pulada: zero
LinhaO que faz
{"Ana", 20, 8.5}Preenche pela ordem: nome, idade, nota. O texto entre aspas pode ir direto para o array nome aqui, na criação (depois, só com strcpy).
{"Bruno", 19}Valores a menos: os campos do fim viram zero. O -Wextra avisa (missing initializer for field ‘nota’, "faltou valor inicial para o campo nota"), porque pode ter sido esquecimento.
{0} e {}Tudo zerado. Esses dois não geram aviso: o compilador entende que zerar era a intenção.
{.nome = "Carla", .idade = 22, .nota = 9.5}Designadores C99: cada valor vem com o nome do campo. Mais fácil de ler, e não depende da ordem dos campos na struct.
{.nota = 6.0, .nome = "Davi"}Em C, os designadores podem vir em qualquer ordem e pular campos; os pulados viram zero, sem aviso. (Em C++, a ordem tem de ser a da declaração.)

Os designadores são ótimos quando a struct tem vários campos do mesmo tipo: em {20, 22} você precisa lembrar qual número é qual; em {.idade = 20, .sala = 22}, não. E se alguém um dia mudar a ordem dos campos na struct, a forma com designadores continua certa.


4. Uma turma inteira: array de structs

Aqui a struct mostra a sua força. Um array de struct Aluno é uma pilha de fichas (veja Arrays). Cada elemento é uma ficha completa:

#include <stdio.h>

struct Aluno {
    char nome[50];
    int idade;
    double nota;
};

int main(void) {
    struct Aluno turma[] = {
        {"Ana", 20, 8.5},
        {"Bruno", 19, 6.0},
        {"Carla", 22, 9.5},
        {"Davi", 21, 7.0},
    };
    size_t n = sizeof turma / sizeof turma[0];

    double soma = 0.0;
    for (size_t i = 0; i < n; i++) {
        printf("%-6s %4.1f\n", turma[i].nome, turma[i].nota);
        soma += turma[i].nota;
    }
    printf("Alunos: %zu\n", n);
    printf("Média da turma: %.2f\n", soma / n);
    printf("Primeiro: %s\n", turma[0].nome);
    return 0;
}
struct Aluno turma[] = { … };
Um array de alunos. Cada {"Ana", 20, 8.5} lá dentro preenche uma ficha, pelas regras da seção 3. Sem número entre os colchetes, o tamanho vem da lista (4). A vírgula depois do último é permitida e facilita acrescentar linhas.
sizeof turma / sizeof turma[0]
O tamanho do array inteiro dividido pelo tamanho de uma ficha = quantas fichas (veja sizeof).
turma[i].nota
Primeiro o [i] pega a ficha da posição i; depois o .nota pega o campo dela.
%-6s %4.1f
Nome alinhado à esquerda em 6 espaços e nota em 4 espaços com uma casa decimal, para formar colunas (veja Formatos do printf).
$ gcc -std=c23 -Wall -Wextra turma.c -o turma
$ ./turma
Ana     8.5
Bruno   6.0
Carla   9.5
Davi    7.0
Alunos: 4
Média da turma: 7.75
Primeiro: Ana

Sem struct, você precisaria de três arrays separados (nomes, idades, notas) e de cuidado para que a posição 2 de cada um fosse sempre a mesma pessoa. Com a struct, cada pessoa é uma peça só: ordenar ou apagar um aluno nunca separa o nome da nota.


5. Copiar com = e passar para funções

Ao contrário dos arrays, uma struct pode ser copiada inteira com =. A cópia leva todos os campos, inclusive o array nome, e fica independente da original. Passar uma struct para uma função também faz uma cópia:

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

struct Aluno {
    char nome[50];
    int idade;
    double nota;
};

void dar_bonus(struct Aluno a) {
    a.nota += 1.0;
    printf("Dentro da função: %.1f\n", a.nota);
}

int main(void) {
    struct Aluno ana = {"Ana", 20, 6.5};
    dar_bonus(ana);
    printf("No main: %.1f\n", ana.nota);

    struct Aluno outra = ana;
    strcpy(outra.nome, "Bia");
    outra.nota = 10.0;
    printf("%s: %.1f | %s: %.1f\n", ana.nome, ana.nota, outra.nome, outra.nota);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra copia.c -o copia
$ ./copia
Dentro da função: 7.5
No main: 6.5                 « a função mudou a cópia dela, não a Ana do main
Ana: 6.5 | Bia: 10.0         « mudar a outra não muda a ana

Guarde esse comportamento de dar_bonus: para uma função mudar a struct de verdade (e para não copiar 64 bytes a cada chamada), ela recebe um ponteiro para a struct. Esse é o assunto da próxima aula, Structs e Ponteiros (->).

Campo ponteiro: a cópia divide o mesmo texto

Aqui o nome é um array (char nome[50]), que mora dentro da ficha e é copiado junto. Se o campo fosse um ponteiro (char *nome, apontando para uma string dinâmica), o = copiaria só o endereço: as duas fichas apontariam para o mesmo texto, e mudar um mudaria o outro. Para copiar o texto também, faça outra.nome = strdup(ana.nome);.

Devolver uma struct e struct dentro de struct

Uma função também pode devolver uma struct inteira. E um campo pode ser, ele mesmo, outra struct: a data de nascimento tem dia, mês e ano, e cabe muito bem numa struct Data:

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

struct Data {
    int dia;
    int mes;
    int ano;
};

struct Aluno {
    char nome[50];
    struct Data nascimento;
    double nota;
};

void mostrar(struct Aluno a) {
    printf("%s, nascida em %02d/%02d/%d, nota %.1f\n",
           a.nome, a.nascimento.dia, a.nascimento.mes, a.nascimento.ano, a.nota);
}

struct Aluno criar(const char *nome, double nota) {
    struct Aluno novo = {0};
    snprintf(novo.nome, sizeof novo.nome, "%s", nome);
    novo.nota = nota;
    return novo;
}

int main(void) {
    struct Aluno ana = {"Ana", {14, 3, 2005}, 8.5};
    mostrar(ana);

    struct Aluno eva = criar("Eva", 7.0);
    eva.nascimento = (struct Data){2, 9, 2006};
    mostrar(eva);

    printf("sizeof(struct Data) = %zu\n", sizeof(struct Data));
    return 0;
}
LinhaO que faz
struct Data nascimento;Um campo que é outra struct. A struct Data precisa estar declarada antes.
a.nascimento.diaDois pontos seguidos: entra em a, depois em nascimento, depois pega o dia.
{"Ana", {14, 3, 2005}, 8.5}Chaves dentro de chaves: as de dentro preenchem a data.
snprintf(novo.nome, sizeof novo.nome, "%s", nome);Copia o nome sem nunca passar dos 50 bytes do campo, mesmo que o texto seja maior (o strcpy não tem essa proteção).
return novo;Devolve a struct inteira (uma cópia). Para structs pequenas, é normal e barato.
(struct Data){2, 9, 2006}Um literal composto C99: cria uma struct Data sem nome, ali mesmo, para atribuir de uma vez.
$ gcc -std=c23 -Wall -Wextra funcoes.c -o funcoes
$ ./funcoes
Ana, nascida em 14/03/2005, nota 8.5
Eva, nascida em 02/09/2006, nota 7.0
sizeof(struct Data) = 12

Três int de 4 bytes: 12 bytes. O %02d escreve o número com dois dígitos, completando com zero à esquerda (3 vira 03).


6. Comparar structs: == não funciona

Copiar com = pode. Comparar com ==, não:

#include <stdio.h>

struct Aluno {
    char nome[50];
    int idade;
    double nota;
};

int main(void) {
    struct Aluno a = {"Ana", 20, 8.5};
    struct Aluno b = a;
    if (a == b) {
        printf("iguais\n");
    }
    return 0;
}
$ gcc -std=c23 -Wall -Wextra compara.c -o compara
compara.c: In function ‘main’:
compara.c:12:11: error: invalid operands to binary == (have ‘struct Aluno’ and ‘struct Aluno’)
   12 |     if (a == b) {
      |           ^~

invalid operands to binary == = "operandos inválidos para o ==": o == só sabe comparar números, caracteres e ponteiros. O C não decide sozinho o que é "dois alunos iguais" (mesmo nome? mesma nota? os dois?). No editor do guia, o Clang diz invalid operands to binary expression ('struct Aluno' and 'struct Aluno').

A solução é escrever uma função que compara campo por campo, do jeito que fizer sentido para o seu programa. Textos se comparam com strcmp (veja Funções de String):

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

struct Aluno {
    char nome[50];
    int idade;
    double nota;
};

bool mesmo_aluno(struct Aluno a, struct Aluno b) {
    return strcmp(a.nome, b.nome) == 0 && a.idade == b.idade && a.nota == b.nota;
}

int main(void) {
    struct Aluno a = {"Ana", 20, 8.5};
    struct Aluno b = a;
    struct Aluno c = {"Ana", 20, 9.0};
    printf("a e b: %s\n", mesmo_aluno(a, b) ? "iguais" : "diferentes");
    printf("a e c: %s\n", mesmo_aluno(a, c) ? "iguais" : "diferentes");
    return 0;
}
$ gcc -std=c23 -Wall -Wextra iguais.c -o iguais
$ ./iguais
a e b: iguais
a e c: diferentes
Por que não comparar os bytes com memcmp?

Parece tentador comparar a memória inteira das duas fichas. O problema são os bytes de preenchimento (padding): o compilador deixa buracos entre os campos para cada um começar num endereço "redondo". Somando os campos do Aluno, dá 50 + 4 + 8 = 62 bytes, mas sizeof(struct Aluno) é 64 no gcc: há 2 bytes de enchimento depois do nome, com conteúdo qualquer. E o que vem depois do '\0' dentro do nome também pode ser lixo. Duas fichas "iguais" podem ter bytes diferentes. Compare campo por campo.


7. Mostrar uma struct na tela

O printf não tem um formato para structs: ele não sabe quais campos existem nem como mostrá-los. Passar a struct inteira dá aviso e lixo na tela:

$ gcc -std=c23 -Wall -Wextra imprimir.c -o imprimir
imprimir.c: In function ‘main’:
imprimir.c:11:14: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘struct Aluno’ [-Wformat=]
   11 |     printf("%s\n", a);
      |             ~^     ~
      |              |     |
      |              |     struct Aluno
      |              char *
$ ./imprimir
«três ou quatro caracteres estranhos»

format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘struct Aluno’ = "o formato %s espera um texto, mas o argumento 2 é uma struct Aluno". O jeito é mostrar campo por campo, de preferência numa função própria, como a mostrar da seção 5. Assim, mudar o formato da ficha na tela é mudar um lugar só.


Formas de preencher uma struct: qual usar

FormaExemploQuando usar
Zerar e preencher campo por campostruct Aluno a = {0}; a.idade = 20;Quando os valores chegam aos poucos (lidos do teclado ou de um arquivo).
Chaves pela ordemstruct Aluno a = {"Ana", 20, 8.5};Structs pequenas, em listas (arrays de structs).
Designadores C99struct Aluno a = {.nome = "Ana", .nota = 8.5};Structs com muitos campos ou campos do mesmo tipo. A mais legível; o que ficar de fora vira zero.
Tudo zerado{0}, ou {} C23Uma ficha em branco. Use {0} se o código precisa compilar em versões antigas.
Literal composto C99a = (struct Aluno){"Bia", 19, 7.5};Trocar todos os campos de uma variável que já existe, numa linha.

Quando dá errado

"expected ‘;’, identifier or ‘(’ before ‘int’": faltou o ; da struct
#include <stdio.h>

struct Aluno {
    char nome[50];
    int idade;
    double nota;
}

int main(void) {
    struct Aluno a = {"Ana", 20, 8.5};
    printf("%s\n", a.nome);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra semponto.c -o semponto
semponto.c:9:1: error: expected ‘;’, identifier or ‘(’ before ‘int’
    9 | int main(void) {
      | ^~~

O que aconteceu: a chave que fecha a struct precisa de ; depois. Funções não precisam (int main(void) { … }), structs precisam. Repare que o gcc aponta a linha seguinte (9), onde ele percebeu o problema: esperava ‘;’ … antes de ‘int’.

Como resolver: ponha o ; depois da } da struct. O Clang, no editor do guia, é mais direto: expected ';' after struct, apontando a linha 7.

"unknown type name ‘Aluno’; use ‘struct’ keyword"
$ gcc -std=c23 -Wall -Wextra semstruct.c -o semstruct
semstruct.c: In function ‘main’:
semstruct.c:10:5: error: unknown type name ‘Aluno’; use ‘struct’ keyword to refer to the type
   10 |     Aluno a;
      |     ^~~~~
      |     struct
semstruct.c:11:6: error: request for member ‘idade’ in something not a structure or union
   11 |     a.idade = 20;
      |      ^
«…»

O que aconteceu: em C, o nome do tipo é struct Aluno, com as duas palavras. Só Aluno é um "nome de tipo desconhecido". O gcc até sugere: use ‘struct’ keyword. Os erros seguintes são consequência: como a não virou uma struct, ela não tem o campo idade. (Quem vem do C++ cai muito nesse, porque lá Aluno a; funciona.)

Como resolver: escreva struct Aluno a;, ou crie um apelido com typedef.

"assignment to expression with array type": a.nome = "Ana";
$ gcc -std=c23 -Wall -Wextra atribuir.c -o atribuir
atribuir.c: In function ‘main’:
atribuir.c:11:12: error: assignment to expression with array type
   11 |     a.nome = "Ana";
      |            ^

O que aconteceu: "atribuição a uma expressão do tipo array". O campo nome é um array, e array não recebe = (veja Strings). Nas chaves da criação ({"Ana", …}) pode, porque ali é inicialização, não atribuição.

Como resolver: strcpy(a.nome, "Ana"); ou, mais seguro, snprintf(a.nome, sizeof a.nome, "%s", texto);.

"has no member named"
$ gcc -std=c23 -Wall -Wextra membro.c -o membro
membro.c: In function ‘main’:
membro.c:11:21: error: ‘struct Aluno’ has no member named ‘email’
   11 |     printf("%s\n", a.email);
      |                     ^

O que aconteceu: o struct Aluno não tem o campo email. Acontece por erro de digitação (a.Nome em vez de a.nome) ou porque você esqueceu de declarar o campo.

Como resolver: confira o nome exato na declaração da struct (maiúsculas contam) ou acrescente o campo lá.

"invalid operands to binary ==": comparar structs

O que aconteceu: a == b com duas structs não compila (seção 6).

Como resolver: escreva uma função que compara os campos que importam, com strcmp para os textos.

A função mudou a struct, mas no main nada mudou

O que aconteceu: o parâmetro é struct Aluno a: a função recebeu uma cópia, como em copia.c.

Como resolver: passe um ponteiro: void dar_bonus(struct Aluno *a), e chame com dar_bonus(&ana). Detalhes em Structs e Ponteiros.


Referência rápida

EscritaSignificado
struct Aluno { char nome[50]; int idade; };Declara um tipo novo com dois campos (não esqueça o ; final).
struct Aluno a;Cria uma variável; campos com lixo se for local.
struct Aluno a = {0}; / = {};Cria com tudo zerado ({} a partir do C23).
struct Aluno a = {"Ana", 20};Preenche pela ordem dos campos; o resto vira zero.
struct Aluno a = {.idade = 20, .nome = "Ana"};Designadores (C99), em qualquer ordem; o resto vira zero.
a.idadeAcessa um campo (ler ou mudar).
a.nascimento.diaCampo de uma struct dentro de outra.
strcpy(a.nome, "Ana");Guarda texto num campo array (nunca a.nome = …).
struct Aluno b = a;Copia todos os campos; b é independente.
a == bErro: compare campo por campo.
struct Aluno turma[30];Um array de structs; turma[i].nota.
(struct Aluno){"Bia", 19, 7.5}Literal composto (C99): uma struct sem nome, criada na hora.
p->notaCampo por um ponteiro: próxima aula.

Exercícios

1. Declarar um campo

Complete a struct Produto com um campo preco do tipo double:

struct Produto { char nome[40]; };

2. Criar a variável

Complete para criar uma variável p do tipo da struct acima, com tudo zerado (forma que funciona em qualquer versão do C):

3. Acessar o campo

Complete para dar o preço de 4.50 ao produto p:

= 4.50;

4. Com struct Aluno { char nome[50]; int idade; double nota; };, quanto vale b.nota depois de struct Aluno b = {"Bruno", 19};?

5. Qual destas linhas não compila?

6. Uma função void f(struct Aluno a) faz a.nota = 10;. O que acontece com a variável que foi passada no main?

Missão final: a melhor nota da turma

Missão: complete a função melhor, que devolve a posição do aluno com a maior nota. Dentro do for, se turma[i].nota for maior que turma[indice].nota, guarde i em indice. A tela deve mostrar Melhor: Carla (9.5).

#include <stdio.h>

struct Aluno {
    char nome[50];
    int idade;
    double nota;
};

size_t melhor(const struct Aluno turma[], size_t n) {
    size_t indice = 0;
    for (size_t i = 1; i < n; i++) {
        /* se a nota de turma[i] for maior que a de turma[indice], guarde i em indice */
    }
    return indice;
}

int main(void) {
    struct Aluno turma[] = {
        {"Ana", 20, 8.5},
        {"Bruno", 19, 6.0},
        {"Carla", 22, 9.5},
        {"Davi", 21, 7.0},
    };
    size_t n = sizeof turma / sizeof turma[0];

    size_t m = melhor(turma, n);
    printf("Melhor: %s (%.1f)\n", turma[m].nome, turma[m].nota);
    return 0;
}
#include <stdio.h>

struct Aluno {
    char nome[50];
    int idade;
    double nota;
};

size_t melhor(const struct Aluno turma[], size_t n) {
    size_t indice = 0;
    for (size_t i = 1; i < n; i++) {
        if (turma[i].nota > turma[indice].nota) {
            indice = i;
        }
    }
    return indice;
}

int main(void) {
    struct Aluno turma[] = {
        {"Ana", 20, 8.5},
        {"Bruno", 19, 6.0},
        {"Carla", 22, 9.5},
        {"Davi", 21, 7.0},
    };
    size_t n = sizeof turma / sizeof turma[0];

    size_t m = melhor(turma, n);
    printf("Melhor: %s (%.1f)\n", turma[m].nome, turma[m].nota);
    return 0;
}

Resumo

  • Uma struct cria um tipo novo que agrupa campos de tipos diferentes: struct Aluno { char nome[50]; int idade; double nota; };. Não esqueça o ; depois da chave.
  • Em C, o tipo se chama struct Aluno (as duas palavras). A struct guarda só dados: nada de funções dentro.
  • Acesse cada campo com o ponto: a.nota. Texto num campo array entra com strcpy/snprintf.
  • Campo sem valor = lixo. Crie com {0} (ou {} no C23) para zerar tudo.
  • Preencha com chaves: {"Ana", 20, 8.5} (pela ordem) ou {.nome = "Ana", .nota = 8.5} (designadores, C99, em qualquer ordem).
  • = copia a struct inteira; == não compila: compare campo por campo.
  • Passar uma struct para uma função copia tudo; para mudar a original, use um ponteiro (próxima aula).