Exercícios de ponteiros em C

Reunimos 12 exercícios com ponteiros, do nível iniciante ao avançado. Na maioria deles, a função main já está pronta e você escreve o corpo da função 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.

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

Ponteiros (12 exercícios)

P01 Iniciante

Exercício 1. swap: trocar dois valores

Escreva a função void swap(int *a, int *b), que troca os valores de duas variáveis int. A função main já está pronta (ela lê dois inteiros, chama swap e mostra o resultado no formato "a = valor, b = valor").
Exemplo de execução (a entrada está sublinhada)
3 5 a = 5, b = 3
Seu código
"Executar" roda seu código com a entrada acima. "Corrigir" testa com 4 entradas preparadas.
Use uma variável temporária tmp. *a é a própria "variável para onde a aponta" (o a da main), então a troca é tmp = *a; *a = *b; *b = tmp;.
#include <stdio.h>

void swap(int *a, int *b) {
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

int main(void) {
    int a, b;
    scanf("%d %d", &a, &b);
    swap(&a, &b);
    printf("a = %d, b = %d\n", a, b);
    return 0;
}
Explicação: Se você escrever com passagem por valor, como void swap(int a, int b), só as cópias são trocadas dentro da função, e as variáveis da main não mudam. Quem chama passa o endereço com &a, e a função altera o valor apontado com *a. Se você esquecer o *, como em a = b;, só o ponteiro (uma variável local) é alterado e nada acontece.
P02 Iniciante

Exercício 2. Retornar quociente e resto ao mesmo tempo

Para os inteiros a (0 ≤ a ≤ 1000000) e b (1 ≤ b ≤ 1000), escreva a função void divmod(int a, int b, int *q, int *r), que devolve por meio de argumentos ponteiro o quociente e o resto de a ÷ b. A função main já está pronta.
Exemplo de execução (a entrada está sublinhada)
17 5 Quociente: 3 Resto: 2
Seu código
"Executar" roda seu código com a entrada acima. "Corrigir" testa com 5 entradas preparadas.
Com return só dá para devolver um valor. Receba os endereços das variáveis que devem guardar os resultados (&q, &r) e escreva no local apontado, como em *q = a / b;.
#include <stdio.h>

void divmod(int a, int b, int *q, int *r) {
    *q = a / b;
    *r = a % b;
}

int main(void) {
    int a, b, q, r;
    scanf("%d %d", &a, &b);
    divmod(a, b, &q, &r);
    printf("Quociente: %d\n", q);
    printf("Resto: %d\n", r);
    return 0;
}
Explicação: Usar argumentos ponteiro quando "você quer devolver dois ou mais resultados" é um padrão clássico (é pelo mesmo motivo que se coloca & no scanf). Se você esquecer o * e escrever q = a / b;, estará atribuindo um int a um ponteiro, e o compilador mostra um aviso (a partir do gcc 14, um erro). Mesmo que o programa rodasse, o q da main não seria alterado e continuaria sem valor inicial.
P03 Iniciante

Exercício 3. Soma de um array com ponteiro

Escreva a função int sum(const int *a, int n), que retorna a soma de um array de int com n elementos. Não use o índice [ ]: avance um ponteiro elemento por elemento para somar. A função main já está pronta (ela lê N (1 ≤ N ≤ 100) e N inteiros (cada um entre −10000 e 10000) e mostra a soma).
Exemplo de execução (a entrada está sublinhada)
5 3 1 4 1 5 Soma: 14
Seu código
"Executar" roda seu código com a entrada acima. "Corrigir" testa com 4 entradas preparadas.
Com for (const int *p = a; p < a + n; p++), p aponta, em ordem, do primeiro ao último elemento. O valor do elemento apontado é *p.
#include <stdio.h>

int sum(const int *a, int n) {
    int s = 0;
    for (const int *p = a; p < a + n; p++) {
        s += *p;
    }
    return s;
}

int main(void) {
    int n, a[100];
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }
    printf("Soma: %d\n", sum(a, n));
    return 0;
}
Explicação: Ao passar o nome do array a para uma função, o que é passado é o endereço do primeiro elemento. a + i é o endereço do elemento i posições à frente, e a[i] significa o mesmo que *(a + i). const int * é a promessa de que "esta função não altera o valor apontado"; se você escrever *p = 0; sem querer, dá erro de compilação. a + n aponta para uma posição depois do último elemento, então a condição usa < (com <= a leitura sai dos limites).
P04 Iniciante

Exercício 4. Contar o tamanho de uma string

Escreva com ponteiros, sem usar strlen, a função int my_strlen(const char *s), que retorna o tamanho de uma string (o número de caracteres antes do '\0'). A função main já está pronta (ela lê uma string sem espaços, de letras e dígitos (1 a 99 caracteres), e mostra o tamanho).
Exemplo de execução (a entrada está sublinhada)
pointer Tamanho: 7
Seu código
"Executar" roda seu código com a entrada acima. "Corrigir" testa com 4 entradas preparadas.
Enquanto *s não for '\0', conte avançando o ponteiro um caractere por vez com s++. while (*s != '\0') também pode ser escrito como while (*s).
#include <stdio.h>

int my_strlen(const char *s) {
    int len = 0;
    while (*s != '\0') {
        len++;
        s++;
    }
    return len;
}

int main(void) {
    char str[100];
    scanf("%99s", str);
    printf("Tamanho: %d\n", my_strlen(str));
    return 0;
}
Explicação: Em C, uma string é um array de char terminado por '\0' (caractere nulo). O tamanho não é o tamanho do array (100), e sim o número de caracteres até o '\0'. O que s++ avança é o ponteiro dentro da função (uma cópia), então o str da main não se move. Se você confundir s com *s e escrever while (s != '\0'), como '\0' é uma constante de valor 0, a comparação vira "s é NULL?". Como s nunca vira NULL, o laço não para e continua lendo fora dos limites do array (o compilador também avisa).
P05 Intermediário

Exercício 5. Retornar o máximo e o mínimo

Escreva a função void minmax(const int *a, int n, int *max, int *min), que encontra o maior e o menor valor de um array de int com n elementos e os escreve nas variáveis apontadas pelos argumentos ponteiro max e min. A função main já está pronta (ela lê N (1 ≤ N ≤ 100) e N inteiros (cada um entre −10000 e 10000) e mostra o resultado).
Exemplo de execução (a entrada está sublinhada)
6 4 -2 9 0 9 3 Máximo: 9 Mínimo: -2
Seu código
"Executar" roda seu código com a entrada acima. "Corrigir" testa com 5 entradas preparadas.
Primeiro, inicialize com o primeiro elemento: *max = *min = a[0];. Depois compare com os elementos a partir do segundo e atualize. A comparação é feita com *max (o valor apontado).
#include <stdio.h>

void minmax(const int *a, int n, int *max, int *min) {
    *max = a[0];
    *min = a[0];
    for (int i = 1; i < n; i++) {
        if (a[i] > *max) *max = a[i];
        if (a[i] < *min) *min = a[i];
    }
}

int main(void) {
    int n, a[100], max, min;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }
    minmax(a, n, &max, &min);
    printf("Máximo: %d\n", max);
    printf("Mínimo: %d\n", min);
    return 0;
}
Explicação: Se o valor inicial for 0, quando todos os números são negativos (teste 3) o máximo vira 0. O seguro é inicializar com o primeiro elemento do array. Se você esquecer o *, como em if (a[i] > max), estará comparando um valor com um endereço, e o compilador avisa.
P06 Intermediário

Exercício 6. Inverter a ordem de um array

Escreva a função void reverse(int *a, int n), que inverte no próprio array a ordem dos n elementos de um array de int. Use dois ponteiros, um apontando para o início e outro para o fim, e troque os elementos enquanto eles se aproximam. A função main já está pronta (ela lê N (1 ≤ N ≤ 100) e N inteiros e mostra o array depois de reverse, separado por espaços).
Exemplo de execução (a entrada está sublinhada)
5 1 2 3 4 5 5 4 3 2 1
Seu código
"Executar" roda seu código com a entrada acima. "Corrigir" testa com 4 entradas preparadas.
Prepare int *left = a; e int *right = a + n - 1; e, enquanto left < right, troque *left e *right e avance para dentro com left++ e right--.
#include <stdio.h>

void reverse(int *a, int n) {
    int *left = a;
    int *right = a + n - 1;
    while (left < right) {
        int tmp = *left;
        *left = *right;
        *right = tmp;
        left++;
        right--;
    }
}

int main(void) {
    int n, a[100];
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }
    reverse(a, n);
    for (int i = 0; i < n; i++) {
        printf("%d ", a[i]);
    }
    printf("\n");
    return 0;
}
Explicação: O fim é a + n - 1, e não a + n (a + n fica fora do array). Para um array, o que é passado é o endereço do início, então alterá-lo dentro da função também muda o array da main. Se você continuar trocando até o fim (até left chegar ao último elemento), cada par é trocado duas vezes e tudo volta ao original; por isso, o ponto-chave é parar no meio.
P07 Intermediário

Exercício 7. Converter minúsculas em maiúsculas

Escreva a função void to_upper(char *s), que troca na própria string as letras minúsculas (a a z) por maiúsculas. Os demais caracteres ficam como estão. Não use funções de biblioteca como toupper: trate um caractere por vez com ponteiro. A função main já está pronta (ela lê uma string sem espaços, de letras e dígitos (1 a 99 caracteres), e mostra o resultado da conversão).
Exemplo de execução (a entrada está sublinhada)
Hello2026c HELLO2026C
Seu código
"Executar" roda seu código com a entrada acima. "Corrigir" testa com 5 entradas preparadas.
Repita com while (*s != '\0') e, se 'a' <= *s && *s <= 'z', troque com *s = *s - 'a' + 'A';. Não esqueça o s++ no final.
#include <stdio.h>

void to_upper(char *s) {
    while (*s != '\0') {
        if ('a' <= *s && *s <= 'z') {
            *s = *s - 'a' + 'A';
        }
        s++;
    }
}

int main(void) {
    char str[100];
    scanf("%99s", str);
    to_upper(str);
    printf("%s\n", str);
    return 0;
}
Explicação: Caracteres são códigos (inteiros), então *s - 'a' + 'A' desloca de minúscula para maiúscula. Como a função altera a string, o argumento é char * sem const. Se você colocar o s++ dentro do if, o laço para no primeiro caractere que não é minúsculo e vira um laço infinito.
P08 Intermediário

Exercício 8. Procurar um valor e retornar um ponteiro

Escreva a função int *find(int *a, int n, int key), que procura o valor key, a partir do início, em um array de int com n elementos e retorna um ponteiro para o primeiro elemento encontrado. Se não encontrar, retorna NULL. A função main já está pronta: ela calcula o índice pela diferença entre o ponteiro retornado e o início do array (subtração de ponteiros) e o mostra. A entrada é, nesta ordem, N (1 ≤ N ≤ 100), N inteiros e key.
Exemplo de execução (a entrada está sublinhada)
6 5 8 2 8 1 3 8 Posição: 1
Seu código
"Executar" roda seu código com a entrada acima. "Corrigir" testa com 5 entradas preparadas.
Percorra com for (int *p = a; p < a + n; p++) e, quando *p == key, dê return nesse p. Se sair do laço, é porque não encontrou: retorne NULL.
#include <stdio.h>

int *find(int *a, int n, int key) {
    for (int *p = a; p < a + n; p++) {
        if (*p == key) {
            return p;
        }
    }
    return NULL;
}

int main(void) {
    int n, a[100], key;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }
    scanf("%d", &key);

    int *p = find(a, n, key);
    if (p == NULL) {
        printf("Não encontrado\n");
    } else {
        printf("Posição: %d\n", (int)(p - a));
    }
    return 0;
}
Explicação: A subtração p - a entre dois ponteiros para o mesmo array não dá o número de bytes, e sim quantos elementos de distância há entre eles (por isso é o índice). O ponteiro retornado aponta para o array a da main, então continua válido depois que a função termina. Por outro lado, retornar o endereço de uma variável local da função é um erro (ela deixa de existir quando a função termina).
P09 Intermediário

Exercício 9. Três valores em ordem crescente

Escreva a função void sort3(int *a, int *b, int *c), que reorganiza os valores de três variáveis int de modo que *a ≤ *b ≤ *c. Para as trocas, você pode usar a função swap já fornecida. A função main já está pronta (ela lê três inteiros e, depois de sort3, mostra-os separados por espaços).
Exemplo de execução (a entrada está sublinhada)
9 2 5 2 5 9
Seu código
"Executar" roda seu código com a entrada acima. "Corrigir" testa com 6 entradas preparadas.
Com três passos fica ordenado: "se *a > *b, troca", "se *b > *c, troca" e "de novo, se *a > *b, troca". Como a já é um ponteiro, passe-o para swap sem &: swap(a, b).
#include <stdio.h>

void swap(int *a, int *b) {
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

void sort3(int *a, int *b, int *c) {
    if (*a > *b) swap(a, b);
    if (*b > *c) swap(b, c);
    if (*a > *b) swap(a, b);
}

int main(void) {
    int x, y, z;
    scanf("%d %d %d", &x, &y, &z);
    sort3(&x, &y, &z);
    printf("%d %d %d\n", x, y, z);
    return 0;
}
Explicação: Após os dois primeiros passos, o maior valor já está em *c, mas a ordem entre *a e *b ainda não está definida, por isso o terceiro passo é necessário (dá para conferir com a entrada 2 3 1). Escrever swap(&a, &b) passaria um int **, e os tipos não batem. O ponto-chave é que o ponteiro recebido pode ser repassado como está.
P10 Avançado

Exercício 10. Array dinâmico com malloc

Leia um inteiro N (1 ≤ N ≤ 100000), aloque com malloc espaço para N ints e leia N inteiros (cada um entre −1000 e 1000). Mostre a média com 2 casas decimais e, na linha seguinte, os valores maiores que a média, na ordem de entrada e separados por espaços (se não houver nenhum, não mostre nada depois de "Maiores que a média:"). Se a alocação falhar, mostre "Falha ao alocar memória" e termine; no final, sempre faça free.
Exemplo de execução (a entrada está sublinhada)
5 3 8 1 9 4 Média: 5.00 Maiores que a média: 8 9
Seu código
"Executar" roda seu código com a entrada acima. "Corrigir" testa com 5 entradas preparadas.
Aloque com int *a = malloc(sizeof(int) * n);; se a == NULL, falhou. Depois de alocado, dá para usar a[i] como um array comum. Para "maior que a média", não compare com decimais: comparando com inteiros, a[i] * n > sum, não há erro de arredondamento.
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int n;
    scanf("%d", &n);

    int *a = malloc(sizeof(int) * n);
    if (a == NULL) {
        printf("Falha ao alocar memória\n");
        return 1;
    }

    long long sum = 0;
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
        sum += a[i];
    }
    printf("Média: %.2f\n", (double)sum / n);

    printf("Maiores que a média:");
    for (int i = 0; i < n; i++) {
        if ((long long)a[i] * n > sum) {
            printf(" %d", a[i]);
        }
    }
    printf("\n");

    free(a);
    return 0;
}
Explicação: Quando N só é conhecido durante a execução, aloque com malloc apenas o necessário. A memória alocada deve sempre ser liberada com free depois do uso (se esquecer, há vazamento de memória). malloc retorna NULL quando falha, então confira antes de usar. Em C, o retorno de malloc (void *) pode ser atribuído a um int * sem cast. Se você esquecer de multiplicar por sizeof(int) e escrever malloc(n), só N bytes são alocados, e a escrita sai dos limites.
P11 Avançado

Exercício 11. Nova string juntando duas strings

Escreva a função char *concat(const char *a, const char *b), que monta, em uma área alocada com malloc, a string formada por a seguida de b, e retorna um ponteiro para o início dela. Se a alocação falhar, retorna NULL. A função main já está pronta (ela lê duas strings sem espaços, de letras e dígitos (cada uma com 1 a 19 caracteres), mostra o resultado e seu tamanho e depois faz free). Você pode usar as funções de string.h.
Exemplo de execução (a entrada está sublinhada)
abc xyz abcxyz (6 caracteres)
Seu código
"Executar" roda seu código com a entrada acima. "Corrigir" testa com 4 entradas preparadas.
O tamanho necessário é strlen(a) + strlen(b) + 1 bytes (o +1 é para o '\0' final). Depois de alocar, copie a com strcpy(s, a); e junte b com strcat(s, b);.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *concat(const char *a, const char *b) {
    char *s = malloc(strlen(a) + strlen(b) + 1);
    if (s == NULL) {
        return NULL;
    }
    strcpy(s, a);
    strcat(s, b);
    return s;
}

int main(void) {
    char a[20], b[20];
    scanf("%19s %19s", a, b);

    char *s = concat(a, b);
    if (s == NULL) {
        printf("Falha ao alocar memória\n");
        return 1;
    }
    printf("%s (%d caracteres)\n", s, (int)strlen(s));
    free(s);
    return 0;
}
Explicação: Criar um array local dentro da função, como char s[40];, e retornar o endereço dele é um erro. Quando a função termina, o array deixa de existir e o ponteiro retornado fica inválido. A área alocada com malloc continua existindo até o free, então pode ser retornada. Em compensação, quem recebe (a main) fica responsável pelo free. Se você esquecer o +1, o '\0' é escrito fora da área alocada.
P12 Avançado

Exercício 12. Ordenar nomes com ponteiro para ponteiro

Leia os nomes de N pessoas (1 ≤ N ≤ 10; letras sem espaços, cada nome com 1 a 19 caracteres) e mostre-os em ordem lexicográfica (a ordem do strcmp). O array de nomes names em si não é mexido: só é reordenado o conteúdo do array p de ponteiros que apontam para cada nome. Escreva as duas funções a seguir.
• void swap_ptr(char **x, char **y): troca o conteúdo (os ponteiros) de duas variáveis char *
• void sort_names(char *p[], int n): faz um bubble sort usando swap_ptr
A função main já está pronta e mostra, uma linha cada, a ordem após a ordenação e a ordem original.
Exemplo de execução (a entrada está sublinhada)
4 sato ito kato abe Ordenados: abe ito kato sato Ordem original: sato ito kato abe
Seu código
"Executar" roda seu código com a entrada acima. "Corrigir" testa com 4 entradas preparadas.
Como queremos alterar uma variável do tipo char * dentro da função, recebemos o endereço dela (char **). O corpo de swap_ptr tem a mesma forma do swap de int; só o tipo da variável temporária passa a ser char *. Na ordenação, quando strcmp(p[j], p[j + 1]) > 0, chame swap_ptr(&p[j], &p[j + 1]);.
#include <stdio.h>
#include <string.h>

void swap_ptr(char **x, char **y) {
    char *tmp = *x;
    *x = *y;
    *y = tmp;
}

void sort_names(char *p[], int n) {
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - 1 - i; j++) {
            if (strcmp(p[j], p[j + 1]) > 0) {
                swap_ptr(&p[j], &p[j + 1]);
            }
        }
    }
}

int main(void) {
    int n;
    char names[10][20];
    char *p[10];

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

    sort_names(p, n);

    printf("Ordenados:");
    for (int i = 0; i < n; i++) printf(" %s", p[i]);
    printf("\nOrdem original:");
    for (int i = 0; i < n; i++) printf(" %s", names[i]);
    printf("\n");
    return 0;
}
Explicação: Assim como "para alterar um int, passe um int *", "para alterar um char *, passe um char **": essa é a base do ponteiro para ponteiro. Com swap_ptr(char *x, char *y), só as cópias dentro da função seriam trocadas. Diferente de trocar o conteúdo das strings com strcpy, aqui só os ponteiros são trocados, então nenhum caractere é copiado. strcmp segue a ordem dos códigos dos caracteres, então as maiúsculas (K) vêm antes das minúsculas (a). Escrever p[j] > p[j + 1] compararia endereços.

Revise estas aulas

← Voltar para todos os exercícios