CCT-UFCA/Ciência da Computação/Introdução à Programação/Função e Recursividade
Função e Recursividade
[editar | editar código]Ao desenvolver programas, é comum que determinadas tarefas precisem ser executadas diversas vezes em momentos diferentes. Escrever o mesmo bloco de código repetidamente, além de tornar o programa mais extenso e difícil de entender, aumenta a chance de erros e dificulta sua manutenção. Para resolver esse problema, as linguagens de programação oferecem as funções, que permitem organizar o código em blocos reutilizáveis e bem definidos.
Na linguagem C, uma função é um conjunto de instruções que executa uma tarefa específica e pode ser chamado de diferentes partes do programa, sempre que for necessário realizar essa mesma tarefa. Além de tornar o código mais organizado e legível, o uso de funções permite dividir problemas complexos em partes menores e mais fáceis de resolver.
Definição e Estrutura de uma Função em C
[editar | editar código]Cada função em C possui três elementos fundamentais: o tipo de dado que ela retorna, o seu nome e uma lista (possivelmente vazia) de parâmetros, que são os dados necessários para que a função realize sua tarefa. A definição de uma função também inclui seu corpo, ou seja, o bloco de código que contém as instruções a serem executadas.
A estrutura básica de uma função é a seguinte:
tipo_de_retorno nome_da_funcao(parâmetros) {
// corpo da função
return valor;
}
O tipo de retorno indica o tipo de dado que a função irá devolver ao seu chamador, como int, foat, char, entre outros. Se a função não precisar retornar nenhum valor, utiliza-se o tipo void. O nome da função é um identificador que permite referenciar e executar aquele bloco de código. Já os parâmetros, colocados entre parênteses, são variáveis que recebem valores na chamada da função e podem ser utilizados dentro do seu corpo. Veja um exemplo concreto do que foi visto até aqui:
#include <stdio.h>
int soma(int a, int b) {
return a + b;
}
void printar_resultado(double numero) {
printf("%.3f", numero);
}
int main() {
int resultado = soma(5, 7);
printar_resultado(resultado);
return 0;
}
O programa faz a soma de 5 e 7 utilizando a função soma e exibe o resultado utilizando a função printar_resultado. Vamos analizar melhor essas funções:
main:
[editar | editar código]A função possui retorno do tipo int e é nomeada como main. Essa é a função principal, obrigatória em todos os programas escritos na linguagem C. O corpo dessa função consiste em uma chamada para a função soma, passando os valores 5 e 7 como argumentos. O retorno dessa chamada é armazenado na variável resultado e, em seguida, passado como argumento para a função printar_resultado.
Além disso, na linha 16, há a palavra reservada return seguida de 0, que indica que o programa foi executado com sucesso, sem erros.
⚠️ Observe que resultado é uma variável do tipo int, enquanto a função printar_resultado recebe um argumento do tipo double. Nesse caso, ocorre uma conversão implícita de tipo (coerção), ou seja, o valor inteiro é convertido automaticamente para double na chamada da função.
soma:
[editar | editar código]A função soma possui retorno do tipo int e é responsável por calcular e retornar a soma de dois números inteiros recebidos como parâmetros (a e b).
printar_resultado:
[editar | editar código]A função printar_resultado possui retorno do tipo void, ou seja, ela não retorna nenhum valor. Por esse motivo, não utiliza a palavra reservada return com valores, embora seja possível utilizar apenas return; para encerrar a execução da função antecipadamente.
Veja um exemplo de como ela é estruturada:
void printar_resultado(double numero) {
printf("%.3f", numero);
return;
}
Atenção: não é permitido adicionar nenhuma variável nem valor após a palavra return em funções do tipo void. Apenas return; isolado pode ser utilizado, se necessário.
Declaração de Protótipos
[editar | editar código]Quando uma função é utilizada antes de sua definição no código, é necessário informar ao compilador sua existência por meio de um protótipo. O protótipo é uma declaração que especifica o tipo de retorno, o nome da função e os tipos dos parâmetros, mas sem seu corpo. Ele permite que o compilador conheça a assinatura da função antes de sua implementação.
Por exemplo:
#include <stdio.h>
int soma(int a, int b);
void printar_resultado(double numero);
int main() {
int resultado = soma(5, 7);
printar_resultado(resultado);
return 0;
}
int soma(int a, int b) {
return a + b;
}
void printar_resultado(double numero) {
printf("%.3f", numero);
}
Escopo e Tempo de Vida de Variáveis
[editar | editar código]Ao trabalhar com funções, é fundamental compreender o conceito de escopo, que define onde uma variável pode ser acessada dentro do código. Variáveis declaradas dentro de uma função são chamadas de variáveis locais, e só existem durante a execução dessa função. Por outro lado, variáveis declaradas fora de qualquer função possuem escopo global e podem ser acessadas por qualquer parte do programa.
Outro conceito relacionado é o tempo de vida das variáveis. Variáveis locais possuem um tempo de vida limitado à execução da função. Sempre que a função é chamada, a variável é criada, e quando a função termina, a variável é destruída. Se for necessário manter o valor de uma variável local entre diferentes chamadas da função, utiliza-se o modificador static. Isso faz com que a variável seja criada uma única vez e preserve seu valor entre as execuções da função.
Recursividade
[editar | editar código]A recursividade é um conceito importante na programação, no qual uma função chama a si mesma para resolver um problema. Essa abordagem é particularmente útil para problemas que podem ser divididos em subproblemas menores e semelhantes ao problema original.
Como funciona a recursividade:
[editar | editar código]Uma função recursiva precisa obrigatoriamente conter dois elementos fundamentais:
- Caso base: uma condição que encerra as chamadas recursivas e impede que a função se chame indefinidamente.
- Chamada recursiva: é a situação em que a função se chama novamente, mas com um problema menor.
Sem o caso base, a recursividade se tornaria infinita, causando um erro chamado estouro de pilha, que acontece quando a memória destinada às chamadas de funções (a pilha de execução) se esgota.
Exemplos:
[editar | editar código]Fibonacci
[editar | editar código]A sequência de Fibonacci é uma série de números em que cada número é a soma dos dois anteriores, começando por 0 e 1. A definição matemática é:
- fibonacci(0) = 0
- fibonacci(1) = 1
- fibonacci(2) = 1 + 0 = 1
- fibonacci(3) = fibonacci(2) + fibonacci(1) = 2
- fibonacci(4) = fibonacci(3) + fibonacci(2) = 3
- ...
A definição matemática é:
- fibonacci(0) = 0
- fibonacci(1) = 1
- fibonacci(n) = fibonacci(n-1) + fibonacci(n-2)
Em C ficaria:
int fibonacci(int n) {
if (n == 0) return 0;
if (n == 1) return 1;
return fibonacci(n - 1) + fibonacci(n - 2);
}
Fatorial
[editar | editar código]O fatorial de um número inteiro n é o produto de todos os números inteiros positivos menores ou iguais a n. Por definição, fatorial de 0 é 1.
A definição matemática é:
- fatorial(0) = 1 (caso base)
- fatorial(n) = n × fatorial(n-1) (chamada recursiva)
Em C ficaria:
int fatorial(int n) {
if (n == 0 || n == 1) {
return 1;
} else {
return n * fatorial(n - 1);
}
}
Vantagens, Desvantagens e Cuidados com a Recursividade
[editar | editar código]A recursividade permite resolver problemas de forma mais elegante e próxima da lógica matemática, sendo especialmente útil em algoritmos que lidam com estruturas hierárquicas, como árvores e grafos. No entanto, ela pode ser menos eficiente em termos de tempo e uso de memória, devido ao alto custo das chamadas de função empilhadas. Sempre que possível, uma solução iterativa tende a ser mais eficiente na linguagem C.
Ao utilizar recursão, é fundamental garantir a existência de um caso base bem definido e que cada chamada recursiva reduza o problema, aproximando-o desse caso base. Além disso, é importante avaliar se a recursão realmente traz benefícios, pois, em muitos casos, uma abordagem com laços (for, while) pode ser mais adequada e performática.