Ponteiros e funções
Nesta aula, exploramos como ponteiros e funções se combinam em C para permitir passagem por referência, alteração de valores fora do escopo da função e retorno de múltiplos valores. Com exemplos práticos, você aprenderá a usar ponteiros como parâmetros e a retornar valores através deles, uma técnica essencial para programação eficiente em C.
Em C, funções normalmente recebem argumentos por valor, ou seja, uma cópia do dado é passada. Isso impede que a função modifique a variável original. Ponteiros resolvem essa limitação: ao passar o endereço de uma variável, a função pode acessar e alterar diretamente o conteúdo daquela posição de memória. Este mecanismo é chamado de passagem por referência (embora C passe o ponteiro por valor, o efeito é o mesmo).
Dominar ponteiros com funções é fundamental para escrever código modular, eficiente e que retorne múltiplos resultados. Vamos ver os conceitos na prática.
Passagem por referência
Na passagem por referência, a função recebe um ponteiro para a variável original. Dentro da função, usamos o operador de desreferência (*) para acessar e modificar o valor apontado. Isso contrasta com a passagem por valor, onde a função opera sobre uma cópia.
Considere este exemplo clássico: uma função que troca os valores de duas variáveis (swap). Sem ponteiros, a troca não teria efeito fora da função.
#include <stdio.h>
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 5, y = 10;
printf("Antes: x = %d, y = %d\n", x, y);
swap(&x, &y);
printf("Depois: x = %d, y = %d\n", x, y);
return 0;
}No código, swap recebe os endereços de x e y. Dentro dela, *a e *b acessam os valores originais. A troca é refletida em main porque os ponteiros apontam para as mesmas posições de memória.
Alterando valores
Além de trocar, podemos usar ponteiros para modificar variáveis de outros tipos. Por exemplo, uma função que incrementa um inteiro:
void incrementa(int *p) {
(*p)++; // parênteses necessários devido à precedência
}
int main() {
int cont = 0;
incrementa(&cont);
printf("%d\n", cont); // 1
return 0;
}Note que (*p)++ incrementa o valor apontado, enquanto *p++ incrementaria o ponteiro (o que não desejamos). Sempre use parênteses para desreferenciar antes de aplicar operadores.
Outro exemplo comum: funções que retornam status e preenchem um valor. Por exemplo, uma função que lê um número do usuário e retorna 1 se bem-sucedido, 0 caso contrário:
int ler_numero(int *resultado) {
printf("Digite um numero: ");
if (scanf("%d", resultado) == 1) // resultado já é um ponteiro
return 1;
return 0;
}
int main() {
int num;
if (ler_numero(&num))
printf("Voce digitou %d\n", num);
else
printf("Entrada invalida\n");
return 0;
}Retornando via ponteiro
Funções em C só podem retornar um único valor. Para retornar múltiplos valores, usamos parâmetros ponteiro: a função recebe endereços de variáveis e escreve neles. Isso é comum em funções que calculam vários resultados, como dividir um número em quociente e resto.
void divide(int dividendo, int divisor, int *quociente, int *resto) {
*quociente = dividendo / divisor;
*resto = dividendo % divisor;
}
int main() {
int q, r;
divide(17, 5, &q, &r);
printf("17 / 5 = %d, resto %d\n", q, r);
return 0;
}Outra técnica é retornar um ponteiro para uma variável estática ou alocada dinamicamente. Cuidado: nunca retorne ponteiro para variável local (ela deixa de existir após a função). Exemplo seguro:
#include <stdlib.h>
int* cria_vetor(int tamanho) {
int *v = malloc(tamanho * sizeof(int));
return v;
}
int main() {
int *vetor = cria_vetor(10);
if (vetor) {
// usar vetor
free(vetor);
}
return 0;
}Exemplos
Exemplo 1: Função que calcula estatísticas
Uma função que recebe um vetor e retorna a soma e a média via ponteiros:
void estatisticas(int v[], int n, int *soma, float *media) {
*soma = 0;
for (int i = 0; i < n; i++)
*soma += v[i];
*media = (float)*soma / n;
}
int main() {
int dados[] = {10, 20, 30, 40, 50};
int s;
float m;
estatisticas(dados, 5, &s, &m);
printf("Soma: %d, Media: %.2f\n", s, m);
return 0;
}Exemplo 2: Busca em vetor com retorno de posição
Função que busca um valor e retorna o índice via ponteiro, e retorna 1 se encontrado:
int busca(int v[], int n, int chave, int *indice) {
for (int i = 0; i < n; i++) {
if (v[i] == chave) {
*indice = i;
return 1;
}
}
return 0;
}
int main() {
int arr[] = {3, 7, 1, 9, 4};
int pos;
if (busca(arr, 5, 9, &pos))
printf("Encontrado no indice %d\n", pos);
else
printf("Nao encontrado\n");
return 0;
}Boas práticas
Sempre verifique se o ponteiro não é NULL antes de desreferenciar, especialmente em funções que recebem ponteiros. Use nomes claros para parâmetros ponteiro (por exemplo, pResultado). Documente se o parâmetro é de entrada, saída ou ambos. Evite retornar ponteiros para variáveis locais; prefira alocação dinâmica ou parâmetros de saída.
Referências
- cppreference: Ponteiros
- GNU C Manual: Pointers
- TutorialsPoint: Call by Reference
- Programiz: C Pointers and Functions
- Learn-C.org: Pointers
Exercícios
- Escreva uma função
dobraque recebe um ponteiro para inteiro e dobra o valor apontado. Teste no main. - Implemente uma função
min_maxque recebe um vetor de inteiros, seu tamanho, e dois ponteiros para inteiro (min e max). A função deve calcular o menor e o maior valor do vetor e armazená-los nos locais apontados. - Crie uma função
soma_vetorque recebe um vetor e seu tamanho, e retorna a soma via ponteiro (a função retorna void). Teste. - Escreva uma função que recebe dois inteiros e retorna a soma e a diferença através de ponteiros. A função deve retornar 0 se a soma for par, 1 se ímpar.
- Implemente uma função que recebe um ponteiro para inteiro e um inteiro n. A função deve alocar dinamicamente um vetor de n inteiros e retornar o ponteiro para o vetor alocado (via retorno da função). O parâmetro ponteiro deve ser usado para retornar o tamanho do vetor (que é n). Teste.
#include <stdio.h>
void dobra(int *p) {
*p *= 2;
}
int main() {
int x = 5;
dobra(&x);
printf("%d\n", x); // 10
return 0;
}#include <stdio.h>
void min_max(int v[], int n, int *min, int *max) {
*min = *max = v[0];
for (int i = 1; i < n; i++) {
if (v[i] < *min) *min = v[i];
if (v[i] > *max) *max = v[i];
}
}
int main() {
int arr[] = {4, 2, 9, 1, 7};
int menor, maior;
min_max(arr, 5, &menor, &maior);
printf("Menor: %d, Maior: %d\n", menor, maior);
return 0;
}#include <stdio.h>
void soma_vetor(int v[], int n, int *soma) {
*soma = 0;
for (int i = 0; i < n; i++)
*soma += v[i];
}
int main() {
int numeros[] = {1, 2, 3, 4, 5};
int total;
soma_vetor(numeros, 5, &total);
printf("Soma: %d\n", total);
return 0;
}#include <stdio.h>
int opera(int a, int b, int *soma, int *diferenca) {
*soma = a + b;
*diferenca = a - b;
return (*soma) % 2;
}
int main() {
int s, d;
int par = opera(10, 3, &s, &d);
printf("Soma: %d, Diferenca: %d, Soma impar? %d\n", s, d, par);
return 0;
}#include <stdio.h>
#include <stdlib.h>
int* cria_vetor(int n, int *tamanho) {
*tamanho = n;
int *v = malloc(n * sizeof(int));
return v;
}
int main() {
int tam;
int *vet = cria_vetor(5, &tam);
if (vet) {
printf("Vetor alocado com %d elementos\n", tam);
free(vet);
}
return 0;
}