Ponteiros e funções
Esta aula aborda a integração de ponteiros com funções em C, explicando passagem por referência, alteração de valores e retorno de múltiplos valores via ponteiros. Inclui exemplos práticos e exercícios com respostas.
Em C, funções normalmente recebem argumentos por valor, ou seja, uma cópia do dado é passada. No entanto, com ponteiros é possível simular a passagem por referência, permitindo que a função modifique diretamente as variáveis originais. Isso é essencial para escrever código eficiente e para retornar múltiplos valores.
Nesta aula, veremos como declarar e usar ponteiros como parâmetros de funções, como alterar valores através deles e como retornar resultados indiretamente. Ao final, você será capaz de implementar funções que manipulam dados externos de forma segura e eficaz.
Passagem por referência
Em C, toda passagem de argumento para função é feita por valor. Isso significa que a função recebe uma cópia do valor da variável passada. Qualquer alteração feita no parâmetro dentro da função não afeta a variável original. Para contornar essa limitação, usamos ponteiros: passamos o endereço da variável (um ponteiro) e, dentro da função, acessamos e modificamos o conteúdo desse endereço.
Para declarar um parâmetro como ponteiro, usamos o operador * na declaração. Por exemplo: void func(int *p). Dentro da função, para acessar o valor apontado, usamos o operador de desreferência *p. Ao chamar a função, passamos o endereço da variável com o operador &: func(&var).
#include <stdio.h>
void dobrar(int *num) {
*num = *num * 2;
}
int main() {
int valor = 5;
printf("Antes: %d\n", valor);
dobrar(&valor);
printf("Depois: %d\n", valor);
return 0;
}No exemplo, a função dobrar recebe um ponteiro para int. Dentro dela, *num acessa o valor original e o modifica. Como passamos o endereço de valor com &valor, a alteração persiste após a chamada.
Alterando valores
Alterar valores via ponteiro é útil para modificar múltiplas variáveis em uma única função, economizar memória (evitando cópias de grandes estruturas) e implementar algoritmos como troca de valores (swap).
O padrão é: a função recebe ponteiros para as variáveis a serem alteradas. Dentro, usamos * para ler e escrever nos endereços. É importante lembrar que o ponteiro em si é passado por valor (cópia do endereço), mas o conteúdo apontado é compartilhado.
#include <stdio.h>
void trocar(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 10, y = 20;
printf("Antes: x=%d, y=%d\n", x, y);
trocar(&x, &y);
printf("Depois: x=%d, y=%d\n", x, y);
return 0;
}A função trocar recebe dois ponteiros. A variável temp armazena o valor de *a, depois *a recebe *b e *b recebe temp. Assim, os valores originais são trocados.
Retornando via ponteiro
Funções em C podem retornar apenas um valor usando return. Para retornar múltiplos valores, uma técnica comum é usar parâmetros ponteiro: a função recebe endereços de variáveis onde os resultados serão armazenados. Dessa forma, a função pode “retornar” vários valores indiretamente.
Por exemplo, uma função que calcula o quociente e o resto de uma divisão pode preencher duas variáveis passadas por ponteiro, enquanto retorna um código de erro (ou void).
#include <stdio.h>
int divisao(int dividendo, int divisor, int *quociente, int *resto) {
if (divisor == 0) return -1; // erro
*quociente = dividendo / divisor;
*resto = dividendo % divisor;
return 0; // sucesso
}
int main() {
int q, r;
if (divisao(10, 3, &q, &r) == 0) {
printf("Quociente: %d, Resto: %d\n", q, r);
} else {
printf("Erro: divisão por zero\n");
}
return 0;
}A função divisao recebe dois ponteiros para int (quociente e resto). Ela preenche esses endereços com os resultados e retorna um valor indicando sucesso ou erro. Assim, conseguimos obter dois resultados de uma só vez.
Exemplos
Vamos ver mais exemplos práticos que combinam os conceitos.
Exemplo 1: Função que incrementa um valor
#include <stdio.h>
void incrementar(int *p) {
(*p)++;
}
int main() {
int contador = 0;
incrementar(&contador);
printf("Contador: %d\n", contador);
return 0;
}Exemplo 2: Função que preenche um array via ponteiro
#include <stdio.h>
void preencher(int *arr, int tamanho, int valor) {
for (int i = 0; i < tamanho; i++) {
arr[i] = valor + i;
}
}
int main() {
int vetor[5];
preencher(vetor, 5, 10);
for (int i = 0; i < 5; i++) {
printf("%d ", vetor[i]);
}
printf("\n");
return 0;
}Exemplo 3: Retornando o maior e menor de três números
#include <stdio.h>
void maiorMenor(int a, int b, int c, int *maior, int *menor) {
*maior = a;
*menor = a;
if (b > *maior) *maior = b;
if (b < *menor) *menor = b;
if (c > *maior) *maior = c;
if (c < *menor) *menor = c;
}
int main() {
int x=5, y=9, z=3, max, min;
maiorMenor(x, y, z, &max, &min);
printf("Maior: %d, Menor: %d\n", max, min);
return 0;
}Boas práticas
Ao usar ponteiros em funções, verifique se o ponteiro não é nulo antes de desreferenciá-lo (especialmente se o ponteiro pode vir de uma fonte externa). Use const para parâmetros que não serão modificados (ex.: void func(const int *p)). Documente claramente se a função modifica os valores apontados. Prefira passar estruturas grandes por ponteiro para evitar cópias desnecessárias.
Referências
- cppreference: Ponteiros em C
- GNU C Manual: Pointers
- cppreference: Funções em C
- Learn-C.org: Pointers tutorial
- TutorialsPoint: Pointer to functions
Exercícios
- Escreva uma função chamada
atualizarque recebe um ponteiro para inteiro e dobra o valor apontado. Teste com uma variável na main.✓ Resposta:#include <stdio.h> void atualizar(int *p) { *p = *p * 2; } int main() { int num = 7; atualizar(&num); printf("Valor atualizado: %d\n", num); return 0; } - Crie uma função
somaProdutoque recebe dois inteiros e dois ponteiros para inteiro, e armazena neles a soma e o produto dos dois números. Teste.✓ Resposta:#include <stdio.h> void somaProduto(int a, int b, int *soma, int *produto) { *soma = a + b; *produto = a * b; } int main() { int x=3, y=4, s, p; somaProduto(x, y, &s, &p); printf("Soma: %d, Produto: %d\n", s, p); return 0; } - Implemente uma função
trocarFloatque troca os valores de duas variáveis float usando ponteiros.✓ Resposta:#include <stdio.h> void trocarFloat(float *a, float *b) { float temp = *a; *a = *b; *b = temp; } int main() { float x=1.5, y=2.7; trocarFloat(&x, &y); printf("x=%.1f, y=%.1f\n", x, y); return 0; } - Escreva uma função que recebe um array de inteiros e seu tamanho, e retorna (via ponteiro) a soma e a média dos elementos. A função deve retornar void.✓ Resposta:
#include <stdio.h> void somaMedia(int arr[], int n, int *soma, float *media) { *soma = 0; for (int i = 0; i < n; i++) { *soma += arr[i]; } *media = (float)*soma / n; } int main() { int vet[] = {1,2,3,4,5}; int s; float m; somaMedia(vet, 5, &s, &m); printf("Soma: %d, Média: %.2f\n", s, m); return 0; } - Implemente uma função que recebe um ponteiro para um caractere e converte o caractere para maiúsculo (se for minúsculo). Use a tabela ASCII. Teste com uma letra minúscula.✓ Resposta:
#include <stdio.h> void paraMaiusculo(char *c) { if (*c >= 'a' && *c <= 'z') { *c = *c - 32; } } int main() { char letra = 'b'; paraMaiusculo(&letra); printf("Maiúsculo: %c\n", letra); return 0; }