Exercícios de structs (struct) em C

Reunimos 11 exercícios com structs, do nível iniciante ao avançado. Na maioria deles, a definição da struct e parte da função main já estão prontas, e você escreve a parte pedida. Ao clicar em "Corrigir", seu código é testado com várias entradas usando gcc de verdade, e a saída é conferida.

Como usar estes exercícios
  1. Leia o enunciado e o exemplo de execução e escreva seu código no campo abaixo
  2. Clique em "▶ Executar" para testar com a sua própria entrada
  3. Clique em "✔ Corrigir" para verificar a saída com várias entradas preparadas
  4. Travou? Abra a "Dica". Resolveu? Compare com "Solução e explicação"
Regras da correção
  • Imprima a saída exatamente como no exemplo. Não imprima mensagens pedindo a entrada, como "Digite um número"
  • Espaços no fim das linhas e linhas em branco no final são ignorados
  • Seu código é salvo automaticamente neste navegador

Obs.: Executar e Corrigir enviam seu código ao Wandbox, um serviço externo e gratuito de compilação, que o executa com gcc.

11 exercícios (Iniciante 3 · Intermediário 5 · Avançado 3)

Structs (11 exercícios)

T01 Iniciante

Exercício 1. Struct de um ponto

A struct struct point, que representa coordenadas (membros x e y), já está definida. Leia os inteiros x e y (cada um entre −10000 e 10000), guarde-os nos membros da variável p, mostre-os no formato "(x, y)" e, na linha seguinte, mostre o quadrado da distância até a origem (x² + y²).
Exemplo de execução (a entrada está sublinhada)
3 4 (3, 4) Quadrado da distância à origem: 25
Seu código
"Executar" roda seu código com a entrada acima. "Corrigir" testa com 4 entradas preparadas.
Os membros são acessados com ponto (.), como em p.x. Para o scanf, passe &p.x, como faria com uma variável comum (o . se liga antes do &, então isso significa &(p.x)).
#include <stdio.h>

struct point {
    int x;
    int y;
};

int main(void) {
    struct point p;

    scanf("%d %d", &p.x, &p.y);
    printf("(%d, %d)\n", p.x, p.y);
    printf("Quadrado da distância à origem: %d\n", p.x * p.x + p.y * p.y);
    return 0;
}
Explicação: Uma struct é um tipo que agrupa vários valores relacionados em um só. O nome do tipo é struct point inteiro, então na declaração da variável não dá para omitir o struct (para omitir, use typedef). É comum esquecer o ponto e vírgula de }; no fim da definição da struct; preste atenção.
T02 Iniciante

Exercício 2. Dados de um aluno

A struct struct student, com nome e nota, já está definida. Leia o nome (letras sem espaços, 1 a 19 caracteres) e a nota (0 a 100), guarde-os na variável s e mostre o nome, a nota e o resultado ("Aprovado" se a nota for 60 ou mais, "Reprovado" caso contrário).
Exemplo de execução (a entrada está sublinhada)
sato 72 Nome: sato Nota: 72 Resultado: Aprovado
Seu código
"Executar" roda seu código com a entrada acima. "Corrigir" testa com 5 entradas preparadas.
O nome é uma string, então em scanf("%19s", s.name) não se coloca & (o nome do array já é o endereço do início). Para a nota, use &s.score.
#include <stdio.h>

struct student {
    char name[20];
    int score;
};

int main(void) {
    struct student s;

    scanf("%19s %d", s.name, &s.score);
    printf("Nome: %s\n", s.name);
    printf("Nota: %d\n", s.score);
    if (s.score >= 60) {
        printf("Resultado: Aprovado\n");
    } else {
        printf("Resultado: Reprovado\n");
    }
    return 0;
}
Explicação: Membros de uma struct também podem ser arrays (strings). O 19 de %19s significa "ler no máximo 19 caracteres", deixando espaço para o '\0' para não estourar o array (20 elementos). Note também que não dá para atribuir a um membro string com s.name = "sato"; (fora da inicialização, use strcpy).
T03 Iniciante

Exercício 3. Função que converte um horário em segundos

Escreva a função int to_seconds(struct time t), que recebe por valor a struct struct time (horas, minutos e segundos) e retorna o total de segundos desde 0h00min00s. A função main já está pronta (ela lê horas (0 a 23), minutos (0 a 59) e segundos (0 a 59) e mostra no formato "hh:mm:ss = total s").
Exemplo de execução (a entrada está sublinhada)
1 2 3 01:02:03 = 3723 s
Seu código
"Executar" roda seu código com a entrada acima. "Corrigir" testa com 4 entradas preparadas.
Uma hora tem 3600 segundos e um minuto tem 60. Dentro da função, os membros podem ser usados como t.h, t.m e t.s.
#include <stdio.h>

struct time {
    int h;
    int m;
    int s;
};

int to_seconds(struct time t) {
    return t.h * 3600 + t.m * 60 + t.s;
}

int main(void) {
    struct time t;
    scanf("%d %d %d", &t.h, &t.m, &t.s);
    printf("%02d:%02d:%02d = %d s\n", t.h, t.m, t.s, to_seconds(t));
    return 0;
}
Explicação: Assim como um int, uma struct pode ser passada por valor. Nesse caso, a função recebe uma cópia da struct inteira, então mesmo fazendo t.h = 0; dentro da função, o t da main não muda. Para uma função que só lê os dados, a passagem por valor basta. %02d significa "largura 2, completando com 0 se faltar".
T04 Intermediário

Exercício 4. Boletim com array de structs

Os dados de N alunos (1 ≤ N ≤ 50) já foram lidos para um array da struct Student (nome e nota), nomeada com typedef (a primeira parte da main já está pronta). Continue o código: mostre a média com 1 casa decimal e, na linha seguinte, o nome e a nota de quem tirou a maior nota. Se houver mais de um com a maior nota, mostre quem foi lido primeiro.
Exemplo de execução (a entrada está sublinhada)
3 sato 72 ito 95 kato 64 Média: 77.0 Maior nota: ito (95 pts)
Seu código
"Executar" roda seu código com a entrada acima. "Corrigir" testa com 5 entradas preparadas.
Se você guardar quem tem a maior nota pelo índice best, e não pela nota, consegue pegar o nome junto. Comece com best = 0 e atualize só quando s[i].score > s[best].score (com >=, passaria a ser o último).
#include <stdio.h>

typedef struct {
    char name[20];
    int score;
} Student;

int main(void) {
    int n;
    Student s[50];

    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%19s %d", s[i].name, &s[i].score);
    }

    int sum = 0, best = 0;
    for (int i = 0; i < n; i++) {
        sum += s[i].score;
        if (s[i].score > s[best].score) {
            best = i;
        }
    }
    printf("Média: %.1f\n", (double)sum / n);
    printf("Maior nota: %s (%d pts)\n", s[best].name, s[best].score);
    return 0;
}
Explicação: Com typedef struct { ... } Student;, dá para usar só Student como nome do tipo, sem escrever struct. O membro de um elemento de um array de structs é escrito na ordem "índice do array → ponto", como em s[i].score. Para a média, se um dos lados não for double, como em (double)sum / n, a divisão é entre inteiros e a parte decimal é descartada.
T05 Intermediário

Exercício 5. Atualizar uma conta por ponteiro

Escreva duas funções que recebem por ponteiro e alteram a struct Account (titular e saldo), que representa uma conta.
• void deposit(Account *a, int amount): aumenta o saldo em amount
• int withdraw(Account *a, int amount): se houver saldo suficiente, diminui amount e retorna 1; senão, não faz nada e retorna 0
A função main já está pronta (ela lê o titular e o saldo inicial, depois o número de operações Q e Q linhas "+ valor" (depósito) ou "- valor" (saque), e mostra o resultado de cada operação). Os valores e o saldo inicial estão entre 0 e 100000, e Q entre 1 e 20.
Exemplo de execução (a entrada está sublinhada)
sato 1000 3 + 500 - 2000 - 1500 Saldo de sato: 1500 Saldo insuficiente Saldo de sato: 1500 Saldo de sato: 0
Seu código
"Executar" roda seu código com a entrada acima. "Corrigir" testa com 4 entradas preparadas.
O membro da struct apontada pelo ponteiro a é escrito como a->balance (o mesmo que (*a).balance). No saque, fica mais fácil se você sair primeiro no caso de saldo insuficiente, com if (a->balance < amount) return 0;.
#include <stdio.h>

typedef struct {
    char name[20];
    int balance;
} Account;

void deposit(Account *a, int amount) {
    a->balance += amount;
}

int withdraw(Account *a, int amount) {
    if (a->balance < amount) {
        return 0;
    }
    a->balance -= amount;
    return 1;
}

int main(void) {
    Account acc;
    int q;
    scanf("%19s %d", acc.name, &acc.balance);
    scanf("%d", &q);
    for (int i = 0; i < q; i++) {
        char op;
        int amount;
        scanf(" %c %d", &op, &amount);
        if (op == '+') {
            deposit(&acc, amount);
        } else if (!withdraw(&acc, amount)) {
            printf("Saldo insuficiente\n");
        }
        printf("Saldo de %s: %d\n", acc.name, acc.balance);
    }
    return 0;
}
Explicação: Se a struct for passada por valor (void deposit(Account a, int amount)), só a cópia é alterada e o saldo da main não aumenta. Para alterar, passe por ponteiro e use . com uma variável struct e -> com um ponteiro para struct. Escrever a.balance dá erro de compilação, e *a.balance é interpretado como *(a.balance); por isso use (*a).balance, com parênteses, ou a->balance.
T06 Intermediário

Exercício 6. Função que retorna o ponto médio

Escreva a função Point midpoint(Point a, Point b), que recebe duas structs Point (coordenadas, com membros x e y do tipo double) e retorna como struct o ponto médio entre elas. A função main já está pronta (ela lê as coordenadas dos dois pontos, x1 y1 x2 y2 (inteiros entre −1000 e 1000), e mostra o ponto médio com 1 casa decimal).
Exemplo de execução (a entrada está sublinhada)
0 0 3 4 Ponto médio: (1.5, 2.0)
Seu código
"Executar" roda seu código com a entrada acima. "Corrigir" testa com 4 entradas preparadas.
Calcule membro por membro, como em m.x = (a.x + b.x) / 2;, guarde em m e devolva com return m;.
#include <stdio.h>

typedef struct {
    double x;
    double y;
} Point;

Point midpoint(Point a, Point b) {
    Point m = {0.0, 0.0};
    m.x = (a.x + b.x) / 2;
    m.y = (a.y + b.y) / 2;
    return m;
}

int main(void) {
    Point a, b;
    scanf("%lf %lf %lf %lf", &a.x, &a.y, &b.x, &b.y);
    Point m = midpoint(a, b);
    printf("Ponto médio: (%.1f, %.1f)\n", m.x, m.y);
    return 0;
}
Explicação: Uma função só retorna um valor com return, mas com uma struct dá para devolver vários membros de uma vez. A struct retornada é copiada para quem chamou, então não há problema em retornar a variável local m (é diferente de retornar o endereço de uma variável local). Se os membros fossem int, (0 + 3) / 2 daria 1; por isso aqui eles são double. Para ler um double com scanf, use %lf.
T07 Intermediário

Exercício 7. Notas de 3 matérias (array dentro da struct)

Há uma struct Student com um nome e um array de notas de 3 matérias. Escreva a função void print_report(const Student *s), que mostra o total e a média de um aluno. A saída é uma linha por aluno: "nome total média" (média com 1 casa decimal). A função main já está pronta (ela lê N (1 ≤ N ≤ 20) e N linhas "nome nota1 nota2 nota3" (notas de 0 a 100) e chama print_report para cada aluno).
Exemplo de execução (a entrada está sublinhada)
2 sato 80 70 92 ito 100 100 99 sato 242 80.7 ito 299 99.7
Seu código
"Executar" roda seu código com a entrada acima. "Corrigir" testa com 3 entradas preparadas.
Um elemento do membro array é escrito como s->score[j]. Calcule o total com for (int j = 0; j < 3; j++) e a média com total / 3.0.
#include <stdio.h>

typedef struct {
    char name[20];
    int score[3];
} Student;

void print_report(const Student *s) {
    int total = 0;
    for (int j = 0; j < 3; j++) {
        total += s->score[j];
    }
    printf("%s %d %.1f\n", s->name, total, total / 3.0);
}

int main(void) {
    int n;
    Student st[20];
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%19s", st[i].name);
        for (int j = 0; j < 3; j++) {
            scanf("%d", &st[i].score[j]);
        }
    }
    for (int i = 0; i < n; i++) {
        print_report(&st[i]);
    }
    return 0;
}
Explicação: Quando a struct é grande, em vez de copiá-la inteira com passagem por valor, o comum é passá-la por ponteiro. Se a função só lê os dados, usar const Student * faz com que uma alteração acidental seja apontada como erro de compilação. Se você dividir entre inteiros com total / 3, a parte decimal é descartada e, além disso, um int é passado para %.1f, o que estraga a exibição.
T08 Intermediário

Exercício 8. O ponto está dentro do retângulo? (structs aninhadas)

Estão definidos o ponto Point (x e y int) e o retângulo Rect, que tem o canto inferior esquerdo corner (do tipo Point), a largura w e a altura h (uma struct dentro de outra struct). Escreva a função int contains(const Rect *r, Point p), que retorna 1 se o ponto p estiver dentro do retângulo (incluindo as bordas) e 0 caso contrário. Estar dentro significa que x está entre corner.x e corner.x + w, e y está entre corner.y e corner.y + h (inclusive). A função main já está pronta (ela lê x y w h do retângulo, o número de pontos Q e os Q pontos, e mostra "dentro" ou "fora" para cada ponto). As coordenadas estão entre −1000 e 1000, e w e h entre 1 e 1000.
Exemplo de execução (a entrada está sublinhada)
0 0 4 3 3 1 1 4 3 5 0 (1, 1): dentro (4, 3): dentro (5, 0): fora
Seu código
"Executar" roda seu código com a entrada acima. "Corrigir" testa com 4 entradas preparadas.
Membros aninhados são escritos encadeando um após o outro. A partir do ponteiro r, fica r->corner.x (r é ponteiro, então ->; corner é a própria struct, então .). Junte as 4 condições com &&.
#include <stdio.h>

typedef struct {
    int x;
    int y;
} Point;

typedef struct {
    Point corner;
    int w;
    int h;
} Rect;

int contains(const Rect *r, Point p) {
    return r->corner.x <= p.x && p.x <= r->corner.x + r->w &&
           r->corner.y <= p.y && p.y <= r->corner.y + r->h;
}

int main(void) {
    Rect r;
    int q;
    scanf("%d %d %d %d", &r.corner.x, &r.corner.y, &r.w, &r.h);
    scanf("%d", &q);
    for (int i = 0; i < q; i++) {
        Point p;
        scanf("%d %d", &p.x, &p.y);
        printf("(%d, %d): %s\n", p.x, p.y, contains(&r, p) ? "dentro" : "fora");
    }
    return 0;
}
Explicação: A escolha entre -> e . segue a regra: "se o lado esquerdo é um ponteiro, ->; se é uma struct, .". r->corner é uma struct do tipo Point, então o que vem depois é .x. Escrever r->corner->x dá erro. O resultado da comparação (0 ou 1) pode ser retornado diretamente com return. Como as bordas contam, use <=.
T09 Avançado

Exercício 9. Ordenar o boletim por nota

Escreva a função void sort_by_score(Student *s, int n), que ordena os nomes e notas de N alunos (1 ≤ N ≤ 50) da maior nota para a menor. Alunos com a mesma nota devem manter a ordem de entrada. A função main já está pronta (ela lê os dados e, depois de ordenar, mostra uma linha "posição nome nota" por aluno; a posição é simplesmente contada a partir de 1).
Exemplo de execução (a entrada está sublinhada)
4 sato 72 ito 95 kato 72 abe 88 1 ito 95 2 abe 88 3 sato 72 4 kato 72
Seu código
"Executar" roda seu código com a entrada acima. "Corrigir" testa com 4 entradas preparadas.
Com bubble sort, compare vizinhos e troque só quando s[j].score < s[j + 1].score. Structs podem ser atribuídas inteiras, como em Student tmp = s[j]; s[j] = s[j + 1]; s[j + 1] = tmp;.
#include <stdio.h>

typedef struct {
    char name[20];
    int score;
} Student;

void sort_by_score(Student *s, int n) {
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - 1 - i; j++) {
            if (s[j].score < s[j + 1].score) {
                Student tmp = s[j];
                s[j] = s[j + 1];
                s[j + 1] = tmp;
            }
        }
    }
}

int main(void) {
    int n;
    Student s[50];
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%19s %d", s[i].name, &s[i].score);
    }
    sort_by_score(s, n);
    for (int i = 0; i < n; i++) {
        printf("%d %s %d\n", i + 1, s[i].name, s[i].score);
    }
    return 0;
}
Explicação: Na atribuição = entre structs, todos os membros são copiados (inclusive o array de caracteres name). Se você trocar só as notas, os pares nome–nota ficam desencontrados, então troque a struct inteira. Se a comparação for <=, a troca acontece também em caso de empate, e os alunos com a mesma nota deixam de ficar na ordem de entrada (teste 4). O bubble sort que troca vizinhos "só quando é menor" é uma ordenação estável, que preserva a ordem de valores iguais.
T10 Avançado

Exercício 10. Soma e simplificação de frações

Há uma struct Fraction com numerador num e denominador den. Escreva as duas funções a seguir (a função gcd, que calcula o máximo divisor comum, já está pronta).
• Fraction reduce(Fraction f): retorna a fração simplificada. Deixe o denominador positivo e, se o numerador for 0, use 0/1
• Fraction add(Fraction a, Fraction b): retorna a + b simplificada
A função main já está pronta (ela lê as frações a e b na ordem "numerador denominador numerador denominador" e mostra no formato "a/b + c/d = resultado"; as frações de entrada são mostradas como foram lidas, sem simplificar). O numerador está entre −1000 e 1000, e o denominador entre 1 e 1000. Mesmo que o denominador do resultado seja 1, mostre no formato "2/1".
Exemplo de execução (a entrada está sublinhada)
1 2 1 3 1/2 + 1/3 = 5/6
Seu código
"Executar" roda seu código com a entrada acima. "Corrigir" testa com 6 entradas preparadas.
Com denominador comum, a/b + c/d = (a×d + c×b) / (b×d). Em add, guarde isso em c e faça return reduce(c);. Em reduce, calcule g = gcd(f.num, f.den) e divida numerador e denominador por g (se o numerador for 0, o gcd é o próprio denominador, e o resultado vira 0/1).
#include <stdio.h>

typedef struct {
    int num;
    int den;
} Fraction;

int gcd(int a, int b) {
    if (a < 0) a = -a;
    if (b < 0) b = -b;
    while (b != 0) {
        int r = a % b;
        a = b;
        b = r;
    }
    return a;
}

Fraction reduce(Fraction f) {
    if (f.den < 0) {
        f.num = -f.num;
        f.den = -f.den;
    }
    int g = gcd(f.num, f.den);
    f.num /= g;
    f.den /= g;
    return f;
}

Fraction add(Fraction a, Fraction b) {
    Fraction c = {0, 1};
    c.num = a.num * b.den + b.num * a.den;
    c.den = a.den * b.den;
    return reduce(c);
}

int main(void) {
    Fraction a, b;
    scanf("%d %d %d %d", &a.num, &a.den, &b.num, &b.den);
    Fraction c = add(a, b);
    printf("%d/%d + %d/%d = %d/%d\n", a.num, a.den, b.num, b.den, c.num, c.den);
    return 0;
}
Explicação: reduce segue o padrão de receber a struct por valor e retornar a versão corrigida. O f recebido é uma cópia, então alterá-lo não afeta quem chamou; só o valor devolvido com return f; é usado. Por isso, se você só chamar reduce(c); sem usar o retorno, a fração não é simplificada (use c = reduce(c); ou return reduce(c);). Nas entradas deste exercício o denominador é positivo, então o ajuste de sinal é só uma precaução, mas ao lidar com frações negativas, a regra "deixar o denominador sempre positivo" deixa a exibição consistente.
T11 Avançado

Exercício 11. Controle de estoque

Usando um array da struct Item (nome, preço unitário e quantidade em estoque), escreva duas funções que processam pedidos.
• Item *find_item(Item *items, int n, const char *name): retorna um ponteiro para o produto cujo nome é igual a name, ou NULL se não houver
• int order(Item *items, int n, const char *name, int qty): processa um pedido e retorna o valor da venda. Se o produto não existir, mostra "nome: produto não encontrado" e retorna 0. Se o estoque for menor que qty, mostra "nome: estoque insuficiente (restam k unid.)" e retorna 0. Se houver estoque, diminui o estoque, mostra "nome: qty unid. x R$ preço = R$ valor" e retorna o valor
A função main já está pronta (ela lê o número de produtos N (1 ≤ N ≤ 20) e N linhas "nome preço estoque", o número de pedidos Q (1 ≤ Q ≤ 20) e Q linhas "nome quantidade" e, no final, mostra o total de vendas e o que resta de cada produto). O preço unitário vai de 1 a 10000, e o estoque e as quantidades de 0 a 100.
Exemplo de execução (a entrada está sublinhada)
3 apple 120 10 milk 200 2 bread 150 5 4 apple 3 milk 5 egg 1 milk 2 apple: 3 unid. x R$ 120 = R$ 360 milk: estoque insuficiente (restam 2 unid.) egg: produto não encontrado milk: 2 unid. x R$ 200 = R$ 400 Total de vendas: R$ 760 apple: restam 7 unid. milk: restam 0 unid. bread: restam 5 unid.
Seu código
"Executar" roda seu código com a entrada acima. "Corrigir" testa com 4 entradas preparadas.
Para comparar strings, não use ==; use strcmp(items[i].name, name) == 0. Em find_item, quando encontrar, retorne &items[i] (items + i dá no mesmo). Em order, receba com Item *it = find_item(items, n, name); e use it->stock e it->price.
#include <stdio.h>
#include <string.h>

typedef struct {
    char name[20];
    int price;
    int stock;
} Item;

Item *find_item(Item *items, int n, const char *name) {
    for (int i = 0; i < n; i++) {
        if (strcmp(items[i].name, name) == 0) {
            return &items[i];
        }
    }
    return NULL;
}

int order(Item *items, int n, const char *name, int qty) {
    Item *it = find_item(items, n, name);
    if (it == NULL) {
        printf("%s: produto não encontrado\n", name);
        return 0;
    }
    if (it->stock < qty) {
        printf("%s: estoque insuficiente (restam %d unid.)\n", name, it->stock);
        return 0;
    }
    it->stock -= qty;
    int amount = it->price * qty;
    printf("%s: %d unid. x R$ %d = R$ %d\n", name, qty, it->price, amount);
    return amount;
}

int main(void) {
    int n, q;
    Item items[20];
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%19s %d %d", items[i].name, &items[i].price, &items[i].stock);
    }

    int total = 0;
    scanf("%d", &q);
    for (int i = 0; i < q; i++) {
        char name[20];
        int qty;
        scanf("%19s %d", name, &qty);
        total += order(items, n, name, qty);
    }

    printf("Total de vendas: R$ %d\n", total);
    for (int i = 0; i < n; i++) {
        printf("%s: restam %d unid.\n", items[i].name, items[i].stock);
    }
    return 0;
}
Explicação: O que find_item retorna é um ponteiro para um elemento do array da main, então it->stock -= qty; diminui o estoque de verdade. Se você copiar a struct com Item it = items[i]; e diminuir o estoque, só a cópia muda, e o estoque do array continua o mesmo. Ler it->stock quando o retorno é NULL pode derrubar o programa, então sempre verifique antes. items[i].name == name compara endereços, então não dá igual mesmo que as strings sejam iguais.

Revise estas aulas

← Voltar para todos os exercícios