Baixe o app para aproveitar ainda mais
Prévia do material em texto
Funções Alexandre Mota acm@cin.ufpe.br Motivação Até o momento, estamos escrevendo códigos monolíticos ◦ int main() { ◦ int x, y, z; ◦ // Leituras de x, y e z ◦ if((x>y) && (x>z)) printf(“%d\n”, x); ◦ else { // x <= y || x <= z ◦ if(y>z) printf(“%d\n”, y); ◦ else printf(“%d\n”, z); ◦ } ◦ } Suponha que tivéssemos uma função chamada maior, que recebe 2 inteiros e retorna o maior deles… Motivação Nosso programa anterior ficaria assim ◦ int main() { ◦ int x, y, z; ◦ // leituras de x, y e z ◦ printf(“%d\n”, maior(maior(x, y), z)); ◦ } Temos como vantagens: ◦ Tamanho do código ◦ Um conceito abstrato (maior) que embute um um conjunto de operações que nem precisamos tomar conhecimento, apenas usar… Com isto, dividimos o problema original em problemas menores e mais fáceis de resolver… Definição Matemática Como já sabemos, uma função é uma associação entre elementos de um domínio (entrada) com elementos de um contra-domínio ou imagem (saída) Usa-se frequentemente a seguinte notação: ◦ f : D1 x D2 x … x Dk I1 x I2 x … x Im Exemplos Função identidade para números inteiros ◦ f : Z Z ◦ f(x) = x Função inversa para números reais ◦ inv : R R ◦ inv(x) = x-1, se x ≠ 0 Composição de funções Na Matemática, costuma-se usar mais de uma função para atingir determinado objetivo Sejam as funções f e g, chamar g e em seguida f é ◦ f(g(x)) E o inverso (f e depois g) ◦ g(f(x)) Exemplos Suponha que queiramos calcular o inverso do quadrado de um númeral real diferente de zero, sabendo-se que temos as funções quad e inv disponíveis Então a solução seria ◦ inv(quad(x)) = (x2)-1 Funções em C Um programa na linguagem C é uma combinação de chamadas de funções, iniciada pela função ◦ main(…) A única diferença das funções em C para as da Matemática é que o corpo das funções em C é formado por comandos imperativos (ordens) e não apenas por expressões matemáticas Declarando uma função Da função matemática ◦ f : D1 x D2 x … x Dk I1 x I2 x … x Im ◦ f(x1, x2, …, xk) = … Temos em C a seguinte declaração: ◦ I1 x I2 x … x Im f(D1 x D2 x … x Dk) { … } Imagem (saída da função) Domínio (entrada da função) Nome da função Definição função (ou corpo) Uma restrição de C é que este tipo seja único!!! Exemplo 1 Veja como fica a função identidade para números inteiros em C int ident(int x) { return x; } Chamar tal função é como na Matemática ◦ ident(2) resultará em 2 Chamado de parâmetro formal (Declara variáveis) O resultado da função (“Todo corpo” termina assim) Quaisquer comandos, inclusive declaração de variáveis Chamado de parâmetro real Por isto, funções são tidas como sub-programas Exercício Escreva uma função que recebe um número real e um inteiro como argumentos e retorna um caractere Exercício Escreva a função maior(x, y) para parâmetros inteiros de tal forma que o programa mostrado anterioremente funcione corretamente apresentando o maior número dentre 3 lidos pelo teclado Chamando funções A linguagem C assume uma postura muito pragmática, visando velocidade de processamento Quando uma função é usada como uma expressão, seu valor de retorno é usado Mas uma função pode ser usada como um comando também e aí seu resultado se perde Função como expressão Dada a função ident que definimos anteriormente Ao executar este comando ◦ printf(“%d\n”, ident(2)); Obtemos o número 2 como resposta na tela do computador Função como comando A função ident também pode ser usada como comando. Isto é, isolada em uma linha de comando ◦ ident(2); Neste caso, não há qualquer comportamento visível para o usuário do programa pois o retorno (valor 2) será perdido O compilador C não faz distinção para otimizar o tempo de processamento ATENÇÃO: Isto é verdade para a linguagem de programação C, mas não é assim em outras linguagens. Portanto, deve-se prestar atenção nestes detalhes… Composição de funções Em C podemos compor funções através de expressões, tal qual na Matemática. Isto é ◦ Resultado = f(g(x)); Ou através dos comandos imperativos usando memória (variável): ◦ ResG = g(x); ◦ Resultado = f(ResG); Ambas as formas são funcionalmente equivalentes Apesar de 2a solução usar memória de forma mais prolongada que a 1a solução Variáveis e escopo Escopo em programação é um termo associado ao local onde o uso de uma variável é válido Por exemplo, compare: ◦ for(int i=0; i<4; i++) printf(“%d\n”, i); Com ◦ for(int i=0; i<4; i++) printf(“%d\n”, i); ◦ printf(“%d\n”, i); O 2o grupo dá erro de compilação porque a variável i não é conhecida fora do comando for Variáveis e escopo Fala-se em escopo local e escopo global O escopo é definido pelo comando onde uma variável é declarada Deve-se evitar o escopo global pois todos podem usar uma variável e, na ocorrência de um problema, torna-se difícil saber quem provocou Tentar usar escopo local (funções) ao máximo. Escopo global apenas para constantes! Escopo e variáveis Assim, ◦ int main() { int i; … } É preferível ao código ◦ int i; int main() { … } No 1o código, o i só é conhecido dentro de main No 2o código, o i é conhecido por main e todas as outras funções que, por ventura, sejam criadas Escopo global Quando se declara uma variável antes de qualquer elemento de um programa Exemplo: #include<stdio.h> int y; int ident(int x) { return x; } int main() { printf(“%d\n”, ident(2)); } Esta variável é válida em ident e em main. Perigoso!!! Escopo global (perigo…) Veja este novo código: #include<stdio.h> int y; int ident(int x) { return x + y; } int main() { y = 1; printf(“%d\n”, ident(2)); } Note que por y ser global, a função ident não gera problemas de compilação. Mas está incorreta… Se tivéssemos y = 0 não conseguiríamos detectar o problema… Visível por tudo que vem abaixo. Com este código, perde-se o princípio da localidade! Criando comandos Dissemos anteriormente que funções podem ser usadas como comandos, perdendo seus valores Mas C também provê facilidade para criar comandos mesmo Usa-se o tipo especial void quando uma função não deve retornar valor. Ou seja, atua como comando mesmo Exemplo Suponha que queiramos criar um comando que faz cursor avançar N linhas Teríamos: void avanca(int n) { for(int i = 0; i < n; i++) printf(“\n”); } Note que por usar void, não usamos return no corpo da função. Exercício Crie uma função (mostra) que simplifica o comando printf para mostrar dados inteiros. Esta função só aceita um parâmetro inteiro Modificando parâmetros Suponha que tenhamos o seguinte código: Quais valores aparecerão na tela? 10 e 20, ou 20 e 10? int x, y, temp; x = 10; y = 20; temp = x; x = y; y = temp; printf(“%d e %d\n”, x, y); Modificando parâmetros Suponha que tenhamos uma função chamada troca, tal que Como seria esta função? int x, y; x = 10; y = 20; troca(x, y); printf(“%d e %d\n”, x, y); Modificando parâmetros Seria simplesmente? Infelizmente não. Precisamos de umaforma de modificar o conteúdo de uma variável através de outra (indireção) void troca(int x, int y) { int temp; temp = x; x = y; y = temp; } Revisitando a função troca Agora que temos ponteiros e indireção, podemos construir a função troca corretamente Esta função deve ser chamada assim: ◦ troca(&var1, &var2); void troca(int *x, int *y) { int temp; temp = *x; *x = *y; *y = temp; } *x consegue acessar o valor de var1. O mesmo para *y com var2 *x consegue acessar o valor de var1. O mesmo para *y com var2 Isto é chamado “passagem por referência”, pois passa endereços de memória ao invés de valores… Memória e funções A memória ocupada por um programa é usualmente representada da seguinte forma: Código do Programa Variáveis globais e estáticas Pilha de execução de funções Memória livre Armazena variáveis locais (inclusive parâmetros) Exemplificando a pilha de memória Suponha o seguinte código Nossa pilha começa vazia em main()… int main() { int x, y; x = 10; y = 20; troca(&x, &y); printf(“%d e %d\n”, x, y); } void troca(int *x, int *y) { int temp; temp = *x; *x = *y; *y = temp; } Exemplificando a pilha de memória Ao executar A pilha passa para int main() { int x, y; x = 10; y = 20; troca(&x, &y); printf(“%d e %d\n”, x, y); } x y ? ? Exemplificando a pilha de memória Após as atribuições A pilha passa para int main() { int x, y; x = 10; y = 20; troca(&x, &y); printf(“%d e %d\n”, x, y); } x y 10 20 Exemplificando a pilha de memória A função troca foi chamada A pilha passa para x y 10 20 void troca(int *x, int *y) { int temp; temp = *x; *x = *y; *y = temp; } x y end. x end. y Exemplificando a pilha de memória Usando a variável temporária A pilha passa para x y 10 20 void troca(int *x, int *y) { int temp; temp = *x; *x = *y; *y = temp; } x y end. x end. y temp 10 Exemplificando a pilha de memória Acessando x e y indiretamente (ponteiros) A pilha passa para x y 20 10 void troca(int *x, int *y) { int temp; temp = *x; *x = *y; *y = temp; } x y end. x end. y temp 10 Exemplificando a pilha de memória Voltando ao programa principal… A pilha passa para x y 20 10 int main() { int x, y; x = 10; y = 20; troca(&x, &y); printf(“%d e %d\n”, x, y); } E quando o programa termina, a pilha volta a ficar totalmente vazia novamente… Funções e main() Há dois formatos para escrever seu programa: int maior(int x, int y) { return (x > y) ? x : y; } int main() { … } int maior(int x, int y); int main() { … } int maior(int x, int y) { return (x > y) ? x : y; } O código à direita está relacionado a como o #include funciona Funções Alexandre Mota acm@cin.ufpe.br
Compartilhar