Baixe o app para aproveitar ainda mais
Prévia do material em texto
4. Funções: Definição e Utilização • Já utilizamos algumas funções. Quais? • O próprio programa é uma função: int main(). • Algumas funções disponíveis na biblioteca math.h: r = sqrt(35); // r = 5.916 p = pow(x,3); // p = x3 s = sin(1.5); // s = seno(1.5) = 0.9975 • Para utilizar uma função devemos fornecer os valores corretos para os parâmetros da função. • O que uma função produz? Produz um valor, que depende do(s) valor(es) do(s) parâmetro(s)! • Qual é o tipo do valor produzido? • Como definir (e utilizar) nossas próprias funções? © ELFS 111 • A definição de uma função deve especificar: • O tipo de valor que a função irá produzir; • O nome da função; • Os parâmetros (e seus tipos) que a função receberá; • O cálculo que precisa ser feito para produzir o valor que a função deverá produzir; • O retorno do valor produzido pela função. • Como utilizar uma função? • Para ser utilizada, a função deve ser chamada em um comando de atribuição e devem ser fornecidos os valores corretos para os parâmetros. x = func(p1, p2); © ELFS 112 Chama a função func com os parâmetros p1 e p2 e atribui o valor produzido pela função à variável x. Qual deve ser o tipo de x? • Exemplo: Definir uma função para calcular a somatória dos números inteiros de a até b com incremento c. • Decisões: • Qual o tipo do valor que a função produz? • Qual o nome da função? • Quantos parâmetros a função precisa? • Qual é o tipo de cada um dos parâmetros? © ELFS 113 int somatoria(int a, int b, int c) { int t,soma; soma = 0; t = a; while (t <= b) { soma = soma + t; t = t + c; } return soma; } Observe que não existe printf() na função! A função deve apenas calcular o valor desejado e retornar esse valor! • E como utilizar essa função somatoria? • Exercício. Utilizar a função somatoria para: • Calcular a soma dos múltiplos de 5 menores que 500; • Calcular a soma dos N pares maiores do que 50; • Calcular a diferença entre a soma dos pares e a soma dos ímpares existentes no intervalo [10, 5000]. © ELFS 114 int main() { int S,N; printf("Valor de N: "); scanf("%d",&N); S = somatoria(1,N,1); // Soma dos inteiros de 1 a N printf("Soma = %d\n",S); S = somatoria(1,N,2); // Soma dos ímpares de 1 a N printf("Soma = %d\n",S); return 0; } • Para que servem os parâmetros de uma função? • Os parâmetros representam valores que a função precisa para calcular o valor que a função irá produzir. • Exemplo. Quais são os parâmetros necessários para uma função que calcula: a) a área de um retângulo? b) uma raiz real de uma equação do 2º grau? • A função deve ser definida considerando que os valores dos parâmetros são conhecidos (portanto, não precisam ser lidos na função). • Os valores dos parâmetros serão fornecidos quando se usa a função. a) a = area(4.5, 2.3); b) r = raizReal(2, 3, -1); © ELFS 115 • Parâmetros formais: usados na definição da função. • Parâmetros reais: usados na chamada da função. • Um parâmetro formal e seu correspondente parâmetro real não precisam ter nomes iguais, mas precisam ser do mesmo tipo. © ELFS 116 int maior(int x, int y) { if (x > y) return x; else return y; } int main() { int a,b,c; scanf("%d %d",&a,&b); c = maior(a,b); printf("c = %d\n",c); return 0; } Definição da função Uso da função float area(float b, float h) { return b*h; } a = area(4.5, 2.3); • Na definição de uma função, o nome dos parâmetros serve apenas para "guardar o lugar" do parâmetro que será usado na chamada da função. • Exemplo: Função para determinar o maior valor dentre dois inteiros. © ELFS 117 int maior(int x, int y) { if (x > y) return x; else return y; } Qual é a importância dos parâmetros serem x e y? Eles poderiam ser p1 e p2? int maior(int p1, int p2) { if (p1 > p2) return p1; else return p2; } int main() { int a,b,c; scanf("%d %d",&a,&b); c = maior(a,b); printf("c = %d\n",c); return 0; }\ Usando a função: Ao usar a função, não interessa se os parâmetros usados na definição da função foram x e y ou p1 e p2. O que interessa é o tipo dos parâmetros. • Exercício: Definir uma função para calcular o valor de considerando n termos da série: • Neste caso: • Qual é o tipo do valor que a função irá retornar? • Qual é o nome da função? • Quais são os parâmetros da função (e seus tipos)? © ELFS 118 € π € 4 1 − 4 3 + 4 5 − 4 7 + 4 9 − ... int main() { float pi; int n; printf("Valor de n: "); scanf("%d",&n); pi = calculaPI(n); printf("Pi = %f (calculado com %d termos)\n",pi,n); return 0; } • Quando uma função é executada? • Quando for chamada! No momento da chamada, os parâmetros formais recebem os valores dos parâmetros reais e a função é executada com esses valores. © ELFS 119 int maior(int x, int y) { if (x > y) return x; else return y; } int main() { int x, aa = 7, b30 = 15; x = maior(aa,b30); printf("Maior = %d\n",x); ... } No momento da chamada, o primeiro parâmetro formal (x) recebe o valor de aa (7) e o segundo parâmetro formal (y) recebe o valor de b30 (15). Em seguida, a função é executada e retorna o valor 15. O valor retornado pela função (15) é atribuído à variável x da função main(), que não tem nada a ver com a variável x da função maior(). Exercícios 1. Escrever uma função somaFatores, que retorna a soma dos fatores de um dado inteiro N. 2. Escrever uma função perfeito, que retorna 1 se um dado N (int) é um número perfeito. Caso contrário, retorna 0. 3. Escrever um programa que usa a função perfeito para mostrar qual é o primeiro número perfeito maior do que um dado valor K (int). 4. Escrever uma função fatorial, que retorna N! para um dado valor N (int). 5. Escrever uma função numComb, que retorna o número de combinações de N termos, tomados K a K, para inteiros N e K dados. Lembrar que: © ELFS 120 € N K ⎛ ⎝ ⎜ ⎞ ⎠ ⎟ = K!(N−K)! N! • Em geral, uma função deve produzir e retornar um valor. • Mas, às vezes, queremos escrever funções, não para produzir um valor, e sim para mostrar resultados. • Exemplo: Escrever uma função que mostra todos os fatores de um dado inteiro N. © ELFS 121 void fatores(int N) { int i; printf("Fatores de %d: 1",N); for (i = 2; i < N; i++) { if (N % i == 0) printf(", %d",i); } return; } Observar que a função deve ser do tipo void e que o return é vazio. int main() { int N; printf("Valor de N: "); scanf("%d",&N); fatores(N); return 0; } Fatores de 28: 1, 2, 4, 7, 14 Observar que, como a função não retorna valor algum, não tem sentido chamar a função em um comando de atribuição. • Num programa que contém várias funções, a função main() deve ser a última. Por que? © ELFS 122 int perfeito(int N) { ... } void fatores(int N) { ... } int main() { int K; ... if (perfeito(K) == 1) { fatores(K); return 0; } ... } • A linguagem C exige que todos os identificadores (nomes de variáveis e de funções) sejam declarados antes de serem usados. • A função main() sendo a última função declarada, pode usar (chamar) qualquer outra função do programa. • Quando uma função é chamada, o compilador verifica pela declaração da função se o número de parâmetros e seus tipos foram respeitados. Exercícios 1. Escrever a função void raizes(float a, float b, float c) que calcula e mostra as raízes reais da equação ax2 + bx + c = 0. A função deve mostrar mensagens adequadas caso as raízes forem iguais ou caso a equação não tenha raízes reais. Escrever um programa que, dados os valores dos coeficientes de uma equação do 2o grau, chama a função raizes para mostrar as raízes dessa equação.2. O Triângulo de Pascal é um arranjo de números da forma: 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 . . . Escrever uma função void tPascal(int k) que mostra a k-ésima linha do Triângulo de Pascal. Escrever um programa que, dado um inteiro N, mostra um Triângulo de Pascal com N linhas. © ELFS 123 Notar que a 1a linha tem 1 elemento; a 2a linha tem 2 elementos; e assim por diante. O elemento que aparece na linha I, coluna J é calculado por: € (I−1)! (J−1)!(I− J)! (I≥1; J≥1) Escopo de Variáveis • Na Linguagem C, as variáveis podem ser declaradas em diversas partes de um programa. • A região do programa onde o nome de uma variável é visível, ou seja, onde uma variável pode ser usada, é conhecida como escopo da variável. • Em geral, o escopo de uma variável é um bloco. • Bloco é uma região do programa delimitada por { e }. • Um exemplo comum de bloco é o corpo de uma função. © ELFS 124 void alarme(int n) { int i; for (i = 0; i < n; i++) printf("\a"); return; } Normalmente, as variáveis são declaradas no início do bloco. O escopo é o bloco inteiro. • Uma variável com escopo de bloco é denominada variável local e tem visibilidade apenas dentro do bloco em que foi declarada. • Declarar variáveis no início do bloco não é obrigatório, desde que a regra "definir antes de usar" seja atendida. © ELFS 125 int main() { int n,d; scanf("%d %d",&n,&d); while (d != 0) { int r; r = n % d; n = d; d = r; } printf("mdc = %d,n); return 0; } As variáveis n e d podem ser usadas em todo o corpo da função. A variável r pode ser usada apenas no corpo do while. Ou seja, cada variável pode ser usada apenas dentro do seu próprio escopo. Algoritmo do mdc: "máximo divisor comum". Exercício. Rescrever esse programa com uma função int mdc(int n, int d) e a função principal. • Mesmo dentro de um bloco, uma variável local pode não ser visível, caso exista um bloco mais interno que declara uma outra variável de mesmo nome. • As variáveis têm mesmo nome, mas têm escopos diferentes, ou seja, são variáveis diferentes (diferentes endereços de memória). © ELFS 126 void qualValor() { int v; v = 100; if (v > 10) { int v; v = 10; printf("v = %d",v); } printf("v = %d",v); return; } O que escreve essa função? v = 10 v = 100 • O escopo de variáveis declaradas como parâmetros na definição de uma função é o corpo da função. • As variáveis a e b também são variáveis locais da função simplifica, mas podem ser usadas somente após terem sido declaradas. Ou seja, o escopo local das variáveis x, y e m é diferente do escopo local das variáveis a e b. © ELFS 127 void simplifica(int x, int y) { int m; m = mdc(x,y); int a,b; a = x/m; b = y/m; printf("%d / %d\n",a,b); printf("%d / %d\n",x,y); return; } As variáveis x, y e m são variáveis locais da função simplifica e podem ser usadas somente no corpo da função. Passagem de Parâmetros • Toda função define um processamento a ser realizado. Esse processamento depende dos valores dos parâmetros da função. • Assim, para usar uma função, um programa precisa fornecer a ela os parâmetros adequados. Exemplos: • Calcular o seno de 30º: sin(pi/6); • Calcular o valor absoluto de a-b: abs(a-b); • Calcular o mdc de 12 e 8: mdc(12,8); • O mecanismo de fornecer a uma função os parâmetros adequados chama-se passagem de parâmetros. • Temos visto até agora a passagem por valor. Como esse tipo de passagem de parâmetros funciona? © ELFS 128 • Seja o seguinte exemplo: • O que este programa irá mostrar? © ELFS 129 void alterar(int x, int y) { printf("Valores recebidos: %d e %d\n",x,y); x++; y++; printf("Valores alterados: %d e %d\n",x,y); return; } int main() { int a = 1, b = 2; alterar(a,b); printf("Valores finais: %d e %d\n",a,b); return 0; } Valores recebidos: 1 e 2 Valores alterados: 2 e 3 Valores finais: 1 e 2 • Na passagem de parâmetros por valor, os parâmetros formais da função recebem cópias dos valores dos parâmetros reais. main() alterar() • A função alterar() modifica os valores de x e y: • Os valores de a e b permanecem os mesmos porque a e b são variáveis locais da função main(). © ELFS 130 2 3 x++ y++ 1 2 x y 1 2 a b Observar que x e y são variáveis locais da função alterar(). • Observe que mesmo usando as variáveis x e y (em vez de a e b) na função main(), o resultado será o mesmo! © ELFS 131 void alterar(int x, int y) { printf("Valores recebidos: %d e %d\n",x,y); x++; y++; printf("Valores alterados: %d e %d\n",x,y); return; } int main() { int x = 1, y = 2; alterar(x,y); printf("Valores finais: %d e %d\n",x,y); return 0; } Aqui: x e y são variáveis locais à função alterar(). Aqui: x e y são variáveis locais à função main(). Exercícios 1. Escrever uma função float graus(float x), onde x é um valor em radianos, e retorna o equivalente valor em graus. Fazer uma função radianos, que recebe um valor x (em graus) e retorna o equivalente em radianos. Lembrar que 3.14159 radianos = 180 graus. 2. Escrever uma função float angulo(float x, float y), que recebe um ponto de coordenadas cartesianas (x, y), com x > 0 e y > 0, e retorna o ângulo (em graus) formado pelo vetor (x, y) e o eixo horizontal. Por exemplo: (0,1) 90 graus (2,2) 45 graus (1,4) 75.9 graus (5,1) 11.3 graus © ELFS 132 (0,1) (1,4) (2,2) (5,1) Fazer também a função main(), para testar a função. 3. Escrever um programa que lê um inteiro não-negativo n e imprime a soma dos n primeiros números primos. O programa deve definir e usar a função int primo(int m), que retorna 1 se m é primo e retorna 0, caso contrário. 4. Escrever um programa que lê dois números inteiros positivos em notação binária de m bits, sendo o primeiro bit igual a 1, e mostra o valor da soma em notação binária dos dois números dados. O programa deve definir e usar as seguintes funções: int bit(int n, int k): retorna o k-ésimo bit (da direita para a esquerda) do número binário n. int vaium(int b1, int b2, int v1): retorna o "vai-um" da soma do bit b1 com o bit b2, considerando o "vai-um" v1. int somabits(int b1, int b2, int v1): retorna a soma do bit b1 com o bit b2, considerando o "vai-um" v1. © ELFS 133 Lembrar que escrever um programa significa escrever a função main(). Passagem de Parâmetros por Referência • Como vimos, na passagem de parâmetros por valor, no momento da chamada da função, cada parâmetro formal da função recebe uma cópia dos valores do parâmetro real correspondente. • Na passagem por referência, em vez de ser passado o valor do parâmetro real é passado o seu endereço. • Considere, por exemplo, que as variáveis a e b correspondem, respectivamente, aos endereços (hexadecimais) F010 e F122. © ELFS 134 1 Variável a b Endereço F010 F122 2 Então: &a = F010 (endereço de a); &b = F122 (endereço de b); a = 1 (valor de a) b = 2 (valor de b) • Considere uma variável declarada como: • Como já discutido anteriormente, x é um ponteiro para int, ou seja, x é uma variável que armazena o endereço de uma variável do tipo int. • Considere agora que: • Neste caso, x armazena o valor F010. • A notação *x corresponde ao valor contido na posição de memória apontada por x. Ou seja, *x vale 1. • Resumindo: © ELFS 135 int *x; x = &a; Endereço Variável Valor F010 a 1 F122 b 2 F530 x F010 Variável Valor a 1 b 2 x • O que acontece, se em vez do valor, é passado o endereço? • O que este programa irá mostrar? © ELFS 136 void alterar(int *x, int *y) { printf("Valores recebidos: %d e %d\n",*x,*y); (*x)++; (*y)++;printf("Valores alterados: %d e %d\n",*x,*y); return; } int main() { int a = 1, b = 2; alterar(&a,&b); printf("Valores finais: %d e %d\n",a,b); return 0; } Valores recebidos: 1 e 2 Valores alterados: 2 e 3 Valores finais: 2 e 3 • Observe que os valores das variáveis a e b, da função main(), foram modificados na função alterar(). Por quê? • Na passagem de parâmetros por referência são passados os endereços das variáveis a e b para os ponteiros x e y. © ELFS 137 2 3 (*x)++ (*y)++ 1 2 a b main() F010 F122 F010 F122 x y alterar() Altera o conteúdo do endereço F010, ou seja, altera o valor da variável a. Altera o conteúdo do endereço F122, ou seja, altera o valor da variável b. Observação Importante: • Os parâmetros passados por valor podem ser entendidos como uma forma de fornecer informação para a função. Neste caso, a única informação que a função produz é o valor retornado. • No caso dos parâmetros passados por referência, qualquer modificação feita pela função no valor do parâmetro será preservada e, portanto, um parâmetro passado por referência pode ser entendido como uma outra forma de receber informação da função (além do valor retornado). • Quantas informações uma função pode produzir? Várias informações: o valor retornado e o valor de cada um dos parâmetros passados por endereço. • Exemplo: Escrever uma função retangulo, que recebe a base e a altura de um retângulo e fornece o perímetro e a área deste retângulo. © ELFS 138 © ELFS 139 float retangulo(float B, float H, float *P) { (*P) = 2*(B + H); return B*H; } • Essa é a única maneira de escrever a função retangulo? • Exercício: Mostrar duas outras formas de definir a função retangulo. Mostrar também como a função será chamada. int main() { float B,H,A,P; printf("Base e Altura: "); scanf("%f %f",&B,&H); A = retangulo(B,H,&P); printf("Area = %f\n",A); printf("Perimetro = %f\n",P); return 0; } Exercícios 1. Para simplificar uma fração de inteiros, basta dividir o numerador e o denominador pelo máximo divisor comum de ambos. Escrever uma função simplifica, que recebe como parâmetros o numerador e o denominador de uma fração e retorna nestes parâmetros a fração simplificada. Considere a função mdc() como já disponível. Qual deve ser o tipo da função simplifica? 2. Modificar a função retangulo de modo a receber as coordenadas cartesianas do vértice superior esquerdo e do vértice inferior direito e a fornecer: a largura, a altura, o perímetro e a área do retângulo. 3. Escrever uma função relogio, que recebe os valores de hora, minuto e segundo e retorna esses valores atualizados, após acrescentar 1 segundo. 4. Escrever uma função data, que recebe os valores de dia, mes e ano e retorna esses valores atualizados, após acrescentar 1 dia. Considere como já disponível a função diasMes(). © ELFS 140 Fazer também a função main(), para testar a função.
Compartilhar