Mudanças entre as edições de "Funções - Programação 1 - Engenharia"

De MediaWiki do Campus São José
Ir para navegação Ir para pesquisar
Linha 731: Linha 731:
 
<math>\mathrm{e}^{x} = \sum^{\infin}_{n=0} \frac{x^n}{n!}</math>
 
<math>\mathrm{e}^{x} = \sum^{\infin}_{n=0} \frac{x^n}{n!}</math>
 
</br>
 
</br>
 +
 
Exemplo: Caso se deseje computar com N=5 teremos:
 
Exemplo: Caso se deseje computar com N=5 teremos:
 +
 +
<br>
 
<math>\mathrm{e}^{x}  \approx \sum^{\5}_{n=0} \frac{x^n}{n!}</math> que corresponde a somar:
 
<math>\mathrm{e}^{x}  \approx \sum^{\5}_{n=0} \frac{x^n}{n!}</math> que corresponde a somar:
 +
 
<math>\frac{x^0}{0!} + \frac{x^1}{1!} + \frac{x^2}{2!} + \frac{x^3}{3!} + \frac{x^4}{4!}</math>
 
<math>\frac{x^0}{0!} + \frac{x^1}{1!} + \frac{x^2}{2!} + \frac{x^3}{3!} + \frac{x^4}{4!}</math>
 +
</br>
 +
 
A  função deve ter a estrutura do esqueleto abaixo e DEVE se utilizar de uma função que calcula o fatorial (''fat''()) e uma função que computa a exponenciação (''expon''()).
 
A  função deve ter a estrutura do esqueleto abaixo e DEVE se utilizar de uma função que calcula o fatorial (''fat''()) e uma função que computa a exponenciação (''expon''()).
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>

Edição das 08h30min de 28 de agosto de 2020

FUNÇÕES no C

Programa C: Um conjunto de funções

Um programa em C basicamente é um conjunto de funções. 
Uma função pode ser vista como um subprograma para o qual podemos repassar 
dados de entrada através de parâmetros e receber os resultados através 
do retorno da função.

Normalmente, um programa bem comportado em C possui pelo menos uma função: a função main(). Esta função é chamada no início da execução do programa. A primeira instrução da função main() é a primeira instrução executada pelo programa (pelo menos do ponto de vista do programador). Da mesma forma, a última instrução desta função é a última instrução a ser chamada.

Um programa normalmente vai apresentar um conjunto de funções. Por exemplo:

#include <stdio.h>

void func4()
{
    printf("Esta é a função func4()\n");
}

void func3()
{
    printf("Esta é a função func3()\n");
    func4();
}

void func2()
{
    printf("Esta é a função func2()\n");
}

void func1()
{
    printf("Esta é a função func1()\n");
    func2();
    func3();
}

int main()
{
    printf("Esta é a primeira instrução da função main()\n");
    func1();
    printf("Esta é a última instrução da função main()\n");
    return 0;
}

A sequência de chamada de funções pode ser ilustrada da forma:

FuncChamadaFuncoes.png

EXERCÍCIO: Compile e execute o programa acima. Verifique a ordem de impressão das mensagens e compare com as chamadas das funções.

NOTA: Uma função pode ser chamada várias vezes no programa. 
É O REAPROVEITAMENTO DE CÓDIGO...

Definição, declaração e chamada de Funções

NOTAR que existem 3 momentos diferentes de uso de funções:

  • Definição de função: é quando se constrói a função explicitando o seu nome, tipo de retorno, parâmetros e o seu corpo (instruções e variáveis locais;
  • Declaração da Função: é somente uma declaração contendo o nome da função, tipo de retorno e parâmetros. O corpo da função NÂO é explicitado pois supõe-se que ela foi definida em algum outro lugar;
  • Chamada (invocação) da Função: quando se usa a função, chamando-a pelo seu nome e passando os parâmetros (ATUAIS) que se deseja.

Por que usar Funções

  • Quebrar grandes problemas em pequenos problemas. Criar código para resolver cada pequeno problema e confiná-lo em funções:
    • uma função cujo objetivo é calcular o seno de um ângulo passado como parâmetro e retornar o seno calculado.
    • uma função que desenha um quadrado dado o tamanho do lado. Neste caso ela nem precisa retornar valor.
  • Facilita a divisão de tarefas em um grande projeto. O desenvolvedor X desenvolve a função seno, o desenvolvedor Y desenvolve a função coseno;
  • Facilita o teste de software: a função pode ser testada exaustivamente antes de ser disponibilizada para uso;
  • Deixa o código mais claro e mais otimizado: se eu quero computar o seno eu não preciso reproduzir o algoritmo que computa o seno, simplesmente chamo a função.

CONSELHO: SEMPRE que receber um problema a ser resolvido, tente quebrá-lo em subproblemas com "funcionalidades" bem definidas. Pense seriamente rm tratar cada subproblema como uma ou mais funções. Liste os possíveis parâmetros e o retorno destas funções. Agrupe as funções em módulos (serão arquivos furutamente).

Exemplo: Implementar uma função que imprimi uma linha com 10 caracteres X lado a lado. Na sequência, melhorar a função e imprimir 5 linhas com 10 caracteres Y cada.

Exercício: Implementar uma função que simula a jogada de dois dados e retorna a soma destes dois dados. Esta função poderia ser usada num jogo de adivinhação contra o computador.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/* definição da função soma de dados
   Sinopse: Esta função ....
   Parâmetros  de Entrada: não tem
   Saída: soma da jogada randômica de 2 dados
   Autor: ....
   Versão:
*/

int somar_dois_dados()
{
    int dado1, dado2, soma;

    dado1 =  rand() % 6 + 1;
    dado2 =  rand() % 6 + 1;
    soma = dado1 + dado2;
    return soma;
}

int main ()
{
      int teste;

      /* iniciar semente: */
      srand ( time(NULL) );

      teste = somar_2_dados();
      printf("Valor da jogada de dados é %d\n", teste);
      return 0;
}

Passando parâmetros e recebendo valores de retorno

Uma função normalmente resolve um determinado problema para um determinado conjunto de dados e produz uma saída. Estes dados podem ser passados como parâmetros e a saída pode ser retornada pela função.

Exemplo: Uma função media_nums() que retorna a média de 3 números reais passados como parâmetros

#include <stdio.h>

float media_nums(float num1, float num2, float num3)
{
  float media_local;

  media_local = (num1 + num2 + num3)/3;
  return media_local;
}

int main()
{
  float media, aux1, aux2, aux3;

  printf("\nEntre com numero 1: ");  
  scanf ("%f",&aux1);

  printf("\nEntre com numero 2: ");  
  scanf ("%f",&aux2);

  printf("\nEntre com numero 3: ");  
  scanf ("%f",&aux3);

  media = media_nums(aux1, aux2, aux3);
  printf ("\nmedia dos 3 numeros é %f\n", media);
  return 0;
}

ADS29002-PassagemParametrosC.png

Deve ser observado que:

  • após o nome da função, entre parênteses, são fornecidos os três parâmetros com os seus respectivos tipos. Os valores (conteúdos das variáveis) aux1, aux2 e aux3 são copiados para as variáveis num1, num2 e num3 da função media_nums().
  • a função media_nums() retorna um valor do tipo float (informado antes do nome da função) que é o valor da variável media_local. Este valor é copiado para a variável media da função main()
  • as variáveis num1, num2 e num3 bem como a variável media_local possuem escopo LOCAL, ou seja, são "vistas" somente pela função media_nums();
  • as variáveis media, aux1, aux2 e aux3 também possuem escopo LOCAL, ou seja são "vistas" somente pela função main();

NOTE que o formato de declaração de uma função é

 tipo_retorno nome_funcao( lista_de_parametros )
 {
   declaracao_variaveis_locais

   instruções
 }

Por enquanto, assumiremos que variáveis devem ser somente declaradas no início da função. Existem situações que poderemos relaxar esta afirmação.

Um pouco mais sobre parâmetros

O termo argumento ou parâmetro real (atual) é usado para referenciar os valores que estão sendo passados na CHAMADA da função. Os parâmetros formais referem-se aos parâmetros listados na função. É comum, no entanto, usar os termos argumentos e parâmetros como sinônimos e identificados pelo contexto em que estão sendo usados.

A passagem de parâmetros POR VALOR diz respeito a copiar o valor do argumento na CHAMADA da função para a variável associada ao parâmetro na função. Mais tarde falaremos na passagem de parâmetro POR REFERÊNCIA. Por ora, usaremos a passagem POR VALOR.

Os parâmetros passados na CHAMADA de uma função não são necessariamente variáveis. Eles podem ser uma expressão qualquer (uma expressão SEMPRE resulta em um VALOR).

Exemplo de chamadas para a função media_nums():

main()
{
  float media, x,y,z;

  x = 5.7;
  y = 9.8;

  /* exemplo de chamada 1 */  
  media = media_nums(4.8,x,y*3);

  /* exemplo de chamada 2 */ 
  media = media_nums (x+y,y*y+5,(x+y)/2);
}

Deve ser observado que:

  • na chamada 1 do exemplo, os parâmetros são uma CONSTANTE, o valor de x, e o valor de y*3. Note que o compilador deve gerar código para resolver estas expressões antes de INVOCAR a função!.
  • na chamada 2 aparecem expressões ainda mais complexas. Todas elas devem ser resolvidas antes da função ser INVOCADA.

Funções Definidas pelo Usuário X Funções da Biblioteca

  • Existem funções que já foram implementadas e disponibilizadas através de uma biblioteca.
    • Exemplo: printf(), rand() etc;
    • Para usar corretamente estas funções devemos incluir a sua declaração no ínicio do programa (arquivo). Uma forma simples é incluir um arquivo cabeçalho contendo estas declarações (ex: #include <stdio.h>)
  • As funções implementadas pelo usuário são àquelas voltadas a aplicação específica que ele está trabalhando.
    • Exemplo: uma função para sortear a soma de dois dados. Toda vez que precisar de uma soma de dois dados lançados randomicamente, bastará chamar esta função.


EXERCÍCIO EM SALA: IMPLEMENTAR uma função para retornar a soma de dois dados lançados randomicamente.

Perguntas a serem respondidas antes de começar a implementação:

  • Qual é a funcionalidade desta função? Qual o objetivo dela?
  • Esta função tem parâmetros? Em que situação ela poderia ter parâmetros?
  • A função retorna valor? Que tipo?
  • Liste possíveis variações desta função.
    • somar o lançamento de N dados;
    • criar um dado de N faces...

Variáveis GLOBAIS e variáveis LOCAIS

Se variáveis são declaradas dentro de uma função, então a visibilidade (ESCOPO) destas variáveis é LOCAL. Nenhuma outra função tem acesso a estas variáveis. Na realidade, o ESCOPO da variáivel LOCAL é o bloco (entre chaves) em que ela foi declarada.

Uma variável pode ser GLOBAL, ou seja, declarada FORA das funções. Neste caso a variável é VISTA por todas as funções. Seja o exemplo anterior modiificado:

#include <stdio.h>

float media; /* Variável GLOBAL */

void media_nums(float num1, float num2, float num3)
{
  media = (num1 + num2 + num3)/3;
  return;
}

main()
{
  float aux1, aux2, aux3; /* Variáveis LOCAIS */

  printf("\nEntre com numero 1: ");  
  scanf ("%f",&aux1);

  printf("\nEntre com numero 2: ");  
  scanf ("%f",&aux2);

  printf("\nEntre com numero 3: ");  
  scanf ("%f",&aux3);

  media_nums(aux1, aux2, aux3);
  printf ("\nmedia dos 3 numeros é %f\n", media);
}

Neste exemplo, a variável media é declarada como GLOBAL. Ela é MODIFICADA diretamente pela função media_nums() e impressa pela função main()

NOTE que como a função media_nums() não retorna valor então declaramos 
seu tipo de retorno como void que significa aqui NADA ou VAZIO. 
NOTE também que MESMO que a função retorne um valor, não é obrigatório colocá-la
no lado direito do sinal de atribuição.

Na realidade, uma função pode ser chamada dentro de qualquer expressão. Por exemplo, para o caso em que a função media_nums() retorna um valor, ela poderia ser usada como:

#include <stdio.h>

float media_nums(float num1, float num2, float num3)
{
  float media_local;

  media_local = (num1 + num2 + num3)/3;
  return media_local;
}

main()
{
  float media, aux1, aux2, aux3;
  printf("\nEntre com numero 1: ");  
  scanf ("%f",&aux1);

  printf("\nEntre com numero 2: ");  
  scanf ("%f",&aux2);

  printf("\nEntre com numero 3: ");  
  scanf ("%f",&aux3);

  media = media_nums(aux1, aux2, aux3);
  printf ("\nmedia dos 3 numeros multiplicada por 10 é %f\n", 10*media_nums(aux1, aux2, aux3));
}

Um parênteses sobre nome de variáveis

Um nome de variável pode conter letras, dígitos e o underscore(sublinhado). Ela DEVE iniciar com um underscore ou uma letra. Letras maúsculas e minúsculas podem ser usadas e são distinguidas (o C é CASE SENSITIVE)

Variáveis LOCAIS e GLOBAIS podem ter o mesmo nome. A variável LOCAL terá preferência no uso.

Exercício: Execute o programa abaixo e verifique as saídas.

#include <stdio.h>

int i=1;          /* GLOBAL  */

func()
{
    int i=100;     /* LOCAL */
    i=i+1;          /* incrementa LOCAL */
    printf( "Valor de i = %d na função func()\n", i );
}
   
main()
{
    i=i+1;          /* incrementa GLOBAL  */
    func();
    printf( "Valor de i = %d \n", i );
}


 NOTA: não é recomendado o uso de variáveis com o mesmo nome.

Iniciando variáveis na declaração

Tanto as variáveis LOCAIS como as GLOBAIS podem ser inicializadas na declaração.

Exemplo:

int alfa=1;

main()
{
  float beta=1.5;

  printf("Valor de alfa = %d e valor de beta = %f\n", alfa, beta);
}
 NOTA: variáveis LOCAIS não são iniciadas automaticamente: cabe ao programador iniciá-la corretamente.
 NOTA: variáveis GLOBAIS são iniciadas automaticamente com zero. 
       Mas mantenha-se informado sobre o sistema que está trabalhando...
       Em sistemas embarcados pode não ser verdade!

Exercícios

  1. Elaborar uma função que imprime uma mensagem:
    ATENÇÃO: CUIDADO COM O CORONA!!!
    Customize a função para que ela repita a mensagem N vezes, onde N é passado como parâmetro. Será que seri importante a função retornar um valor?
  2. Elaborar uma função "linha_vazada()" para desenhar uma linha da forma:
     XXbbbbbbbbbbbXX

    O caracter b apenas indica espaço em branco. Qual seria o tipo de retorno desta função?

    Quais parâmetros ela tem? Propor uma versão customizada que proporcione receber como parâmetros a largura
    total da linha (número de espaços em branco) mas mantendo sempre os lados com XX.

  3. Elaborar um programa contendo uma função da forma:
      float area_retang (float lado1 , float lado2)
    

    A função recebe os lados de um retângulo e retorna a área do mesmo. Crie uma VARIÁVEL LOCAL para armazenamento temporário da área.

    Solução
    #include <stdio.h>
    
    /* Função de Cálculo da Aŕea de um retângulo */
    /* Parâmetros de entrada: lados do retângulo */
    /* retorno: valor da área */
    float area_retang(float lado1, float lado2)
    {
      float area;
    
      area = lado1 * lado2;
      return area;
    }
    
    int main()
    {
      float area;
      float l1, l2;
    
      /* entrada de dados */
      printf("entre com lado 1\n");
      scanf ("%f", &l1);
    
      printf("entre com lado 2\n");
      scanf ("%f", &l2);
    
      /* chamada da função */
      area = area_retang(l1,l2);
      
      printf("valor da area do retanguloa = %0.2f\n", area);
      return 0;
    }
    
  4. Elaborar um programa contendo uma função da forma:
    float area_circ (float raio)
    

    A função recebe o raio de um círculo e retorna a área da mesma.


    Solução
    #include <stdio.h>
      
    float area_circ(float raio)
    { 
      float area;/* Variáveis LOCAIS */
      area = (raio*raio)*3.1415;
      return area;
    }
     
    int main()
    {
      float aux1, area; /* Variáveis LOCAIS */
     
      printf("Entre com o raio\n");  
      scanf ("%f",&aux1);
     
      area = area_circ(aux1);
      printf ("A area do circulo é %f\n", area);
      return 0;
    }
    
  5. Elaborar um programa contendo uma função da forma: float dif_area_circ (float raio1, float raio2) A função recebe o raio de dois círculos e retorna a diferença da área entre eles. Reaproveitar o exercício anterior.
    Solução
    #include <stdio.h>
    
    #define PI 3.1415
    
    float area_circ (float raio)
    {
      float area;
      area = PI*raio*raio;
    
      return (area);
    }
    
    float dif_area_circ (float raio1, float raio2)
    {
      float a1, a2, area;
    
      a1 = area_circ(raio1);
      a2 = area_circ(raio2);
    
      return (a1-a2);
    }
    
    int main()
    {
      float dif;
      float r1, r2, area;
    
      /* entrada de raio 1 */
      printf("entre com raio 1\n");
      scanf ("%f", &r1);
    
      printf("entre com raio2 \n");
      scanf ("%f", &r2);
      
      area = dif_area_circ(r1, r2);
    
      printf("valor da diferença = %0.2f\n", area);
      return 0;
    }
    
  6. Elaborar uma função que recebe dois números inteiros. A função retorna a soma de todos os números entre eles (inclusive).
    Solução
    #include <stdio.h>
     
    /* a função calcula a soma de todos inteiros entre num1 e num2 inclusive */
    
    int soma_int(int num1, int num2)
    {
      int i, soma_acumulada = 0;
      if (num1>num2) {
         i=num2;
         num2=num1;
         num1=i;
      }      
      for (i=num1;i<=num2;i++) 
         soma_acumulada = soma_acumulada + i;
    
      return soma_acumulada;
    }
    
    int main()
    {
      int i,x,y, soma_acumulada;
    
      printf("Entre com o primeiro número da faixa \n");
      scanf ("%d", &x);
    
      printf("Entre com o segundo número da faixa \n");
      scanf ("%d", &y);
    
      soma_acumulada = soma_int(x,y);
      printf("soma acumulada entre %d e %d é %d\n", x,y,soma_acumulada);     
      return 0;
    }
    
  7. Elaborar um programa em C correspondente aos fluxogramas abaixo. Trata-se de um procedimento para calcular o fatorial de um número inteiro passado como parâmetro para uma função. A função fatorial é definida por: FluxogramaFatorial.jpg Neste fluxograma, o subprograma denominado CalcFatorial recebe um valor no parâmetro N (implicitamente inteiro) e retorna o valor calculado do fatorial. O fluxograma principal invoca duas vezes o subrograma. O retorno é armazenado nas variáveis NUM1 e NUM3. Quando um subprograma retorna um valor, ele é chamado de função. Para manter coerência com o C chamaremos qualquer subrprograma de função (independente de retornar valor).
    Solução
    #include <stdio.h>
     
    float calc_fatorial(int num)
    { 
      int i=1;
      float fat=1;/* Variaveis LOCAIS */
      while(i<=num){
      	fat = fat*i;
    	i++;
      }
      return fat;
    }
     
    int main()
    {
      float num1, num2, num3; /* Variáveis LOCAIS */
     
      num1 = calc_fatorial(5);
      num2 = 4;
      num3 = calc_fatorial(num2*3);
     
      printf ("num1: %f\n", num1);
      printf ("num2: %f\n", num2);
      printf ("num3: %f\n", num3);
      return 0;
    }
    
  8. Faça uma função para entrar com dois números inteiros pelo teclado e retornar a soma dos fatoriais destes números. Reutilize a função anterior.
    Solução
  9. Faça uma função para calcular o módulo de um número complexo passado na forma retangular. O protótipo da função tem o seguinte aspecto:
    float mod_complexo(float x, float y)
    
    Solução
    #include <stdio.h>
    #include <math.h>
     
    float mod_complexo(float x,float y)
    { 
      float modulo;/* Variaveis LOCAIS */
      modulo = sqrt( ((x*x) + (y*y)) ); // Funcao sqrt lembrar de utilizar math.h
      return modulo;
    }
     
    main()
    {
      float num1, num2, result;/* Variaveis LOCAIS */
      printf("Entre com a parte real:\n");
      scanf("%f",&num1);
      printf("Entre com a parte imaginaria:\n");
      scanf("%f",&num2);
      result = mod_complexo(num1,num2);
      printf("Modulo do numero complexo: %f\n",result);
    
    }
    
  10. Considere o exercício para desenho de uma figura retangular tal como repassado em aula aula anterior. Confine a solução do problema em uma função onde são repassados a margem, o número de colunas e o número de linhas a ser impresso. A função deve ter o seguinte protótipo: int desenhar_retang(int margem, int linha, coluna); Observe um exemplo de como poderia ser chamada esta função:
    include <stdio.h>
    int desenhar_retang(int margem, int linha, coluna)
    {
     /* implementar aqui */
    }
    
    
    main()
    {
      desenhar_retang(5,10,30);
    }
    
  11. Faça uma função para computar o número usando série da seguinte forma: . Observe que onde está infinito deve ser usado um parâmetro que permita limitar a computação a alguns termos. Esta função deve se UTILIZAR de uma outra função (a ser implementada) que faz a computação do fatorial de um número. Demonstre o funcionamento na função main(). O esqueleto da função é dado:
    float calc_e(int max_n)
    {
      // esta função deve chamar a função fatorial...
    }
    
  12. Faça uma função para computar a combinação simples de 'n' elementos agrupados 'p' a 'p' dada pela fórmula (extraído de Wikipedia:

    A função deve ter a estrutura do esqueleto abaixo e DEVE se utilizar de uma função que calcula o fatorial (fat()). O programa main deve testar o funcionamento da função, imprimindo a combinação de 4 elementos 2 a 2.
    int fat(int n)
    {
    }
    
    int combinacao (int n, int p)
    {
    
    }
    
    int main()
    {
     /* testar aqui */
    
      return 0;
    }
    
  13. Faça uma função para computar uma aproximação da função exponencial através dos N primeiros termos de: Wikipedia:

    Exemplo: Caso se deseje computar com N=5 teremos:
    Falhou ao verificar gramática (MathML com retorno SVG ou PNG (recomendado para navegadores modernos e ferramentas de acessibilidade): Resposta inválida ("Math extension cannot connect to Restbase.") do servidor "https://en.wikipedia.org/api/rest_v1/":): {\displaystyle \mathrm{e}^{x} \approx \sum^{\5}_{n=0} \frac{x^n}{n!}} que corresponde a somar:
    A função deve ter a estrutura do esqueleto abaixo e DEVE se utilizar de uma função que calcula o fatorial (fat()) e uma função que computa a exponenciação (expon()).
    float expon(float x, int n)
    {
        /* implementar */
    }
    
    int fat(int n)
    {
        /* implementar */
    }
    
    float func_exp (int x, int N)
    {
        int n;
        float soma_ac = 0;
    
        /* esqueleto da solução */
        for(n = 0; n < N; n++)
            soma_ac = soma_ac + expon(x,n)/fat(n);
    
        /* retornar: a fazer */
    }
    
    int main()
    {
        /*  exemplo de uso*/
        float valor;
    
        /* calcula os 5 primeiros termos da série correspondente a e^4.3 */
        valor = func_exp(4.5,5);
    
    
        return 0;
    }