Programação com a Linguagem C

 

Funções e Estrutura de Programas

 

1 - Funções

 

·       Funções são uma das características mais importantes da linguagem C; o local onde toda a atividade do programa ocorre.

·       As funções dividem grandes tarefas de computação em tarefas menores.

·       Permitem às pessoas trabalharem sobre o que outras já fizeram.

·       Podem esconder detalhes de operação que não necessitam ser conhecidos.

·       Facilitam mudanças.

·       C utiliza várias pequenas funções eficientes e fáceis de usar (biblioteca).

·       O programa pode residir em vários arquivos-fonte.

·       Os arquivos-fonte podem ser compilados separadamente e carregados juntos, com funções de bibliotecas previamente compiladas.

·       Com o padrão ANSI, é possível declarar os tipos dos argumentos quando uma função é declarada.

·       A nova sintaxe possibilita às declarações casarem com as definições das funções, tornando possível uma melhor detecção de erros.

 

Conceitos Básicos

 

·       Observe o programa abaixo para cálculo do fatorial de um número inteiro.

 

     #include <stdio.h>

 

     void main() {

 

         int num, fat, i;

 

         clrscr();

         printf("\nValor para o fatorial:");

         scanf("%d", &num);

 

         fat = 1;

         for(i = num; i > 1; i--) {

              fat = fat * i;

         }

 

         printf("\nFatorial de %d = %d", num, fat);

         getch();

 

     }

·       Podemos representar o processamento para cálculo do fatorial através de uma função. Nesse caso, o programa acima seria:

 

#include <stdio.h>

 

void main() {

 

  int num;

 

  clrscr();

 

  printf("\nValor para o fatorial:");

  scanf("%d", &num);

 

  printf("\nFatorial de %d = %d", num, fatorial(num));

  getch();

 

}

 

int fatorial(int num) {

 

  int i, fat;

  fat = 1;

  for(i = num; i > 1; i--) {

       fat = fat * i;

  }

  return (fat);

}

 

·       Forma geral de definição de uma função:

 

     tipo-retorno nome(declarações de parâmetros) {

         declarações e comandos - corpo da função

     }

 

·       A lista de declaração de parâmetros para uma função tem esta forma geral:

 

     Func(tipo nomeVar1, tipo nomeVar2, …, tipo nomeVar3)

 

·       A menor função que poderíamos escrever:

 

     vazia() {}

 

·       Quando o tipo de retorno não é especificado considera-se o tipo int.

·       A comunicação entre funções é feita essencialmente através de: argumentos e retorno.

·       Os argumentos de uma função são passados na sua chamada.

 

. . .

     Func(valor1, valor2);

     . . .

 

·       O retorno do valor de uma função é realizado pelo comando return.

 

     return expressão;

 

·       O comando return tem duas funcionalidades: provocar uma saída imediata da função que o contém, devolver um valor ao programa chamador.

·       A função chamada pode ignorar o valor retornado.

·       As funções podem retornar valores numéricos ou literais, além de apontadores.

·       O controle retorna sempre que o fluxo atinge o último comando da função mesmo sem return.

 

Regras de escopo de funções

·       Regras de escopo de uma linguagem são as regras que determinam se um trecho de código conhece ou tem acesso a outro trecho de código ou dados.

·       Em C, cada função é um bloco de código.

·       O código e os dados que são definidos internamente em uma função não podem interagir com o código ou dados definidos em uma outra função porque as duas têm escopos diferentes.

·       Variáveis internas a uma função são chamadas variáveis locais.

·       Variáveis locais não são reconhecidas fora de seu próprio bloco de código.

·       O ciclo de vida de uma variável local está associado à execução da função – variáveis existem enquanto o bloco de código em que foram declaradas está sendo executado.

·       Ao contrário das variáveis locais, variáveis globais são reconhecidas pelo programa inteiro e podem ser usadas por qualquer trecho de código.

. . .

void func1(void);

void func2(void);

 

int contador; /* contador é uma variável global */

 

void main() {

  . . .

  contador = 100;

  func1();

  . . .

}

 

void func1(void) {

  int temp;

  temp = contador;

  . . .

  func2();

  printf(“O valor de contador eh %d”, contador);

}

 

void func2(void) {

  int i, contador; /* contador é uma variável local */

 

  contador = 0;

  for(i = 0; i < 10; i++) {

       if(…) {

            contador++;

       }

  }

  printf(“O valor de contador eh %d”, contador);

}

 

·       Em C, todas as funções estão no mesmo nível de escopo, isto é, não é possível definir uma função internamente a uma outra função.

·       Especificadores de tipo de classe de armazenamento (aplicados a variáveis locais/globais): extern, static, register. Tais especificadores servem para indicar ao compilador como uma variável deve ser armazenada.

·       Exemplo:

 

void serie (void) {

  static int num_serie = 0; /*variaveis locais estaticas*/

  num_serie = num_serie++;

  return (num_serie);

}

 

Argumentos de funções

·       Se uma função usa argumentos, ela deve declarar variáveis que receberão os valores dos argumentos; tais variáveis são denominadas parâmetros formais.

·       Os parâmetros formais se comportam como qualquer outra variável local dentro da função.

·       Aproveitando a definição da função fatorial(), já mostrada:

 

     int fatorial(int num){

         . . .

     }

 

·       A função fatorial() tem um único parâmetro: num. É preciso assegurar-se de que os argumentos usados para chamar a função sejam compatíveis com o tipo de seus parâmetros.

·       Incompatibilidade de tipos não são detectadas pelo compilador mas podem gerar resultados inesperados – o uso de protótipos de funções pode ajudar a achar esses tipos de erros, como veremos mais adiante.

·       Por exemplo: experimente chamar a função fatorial() da seguinte maneira:

 

#include <stdio.h>

 

void main() {

 

  int num;

 

  clrscr();

  printf("\nValor para o fatorial:");

  scanf("%d", &num);

 

  printf("\nFatorial de %d = %d", num, fatorial(“erro”));

  getch();

 

}

 

·       Ocorreram erros de compilação? Qual o valor retornado?

·       Agora inclua no programa acima o protótipo da função fatorial:

 

#include <stdio.h>

 

int fatorial(int);

. . .

 

·       Quais as mudanças em relação ao comportamento do compilador?

 

Chamada por valor, chamada por referência

·       Os argumentos para funções são passados de duas maneiras: chamadas por valor ou por referência.

·       Na chamada por valor, copia-se o valor de um argumento no parâmetro formal da função. Nesse caso, alterações feitas no parâmetro da função não têm nenhum efeito sobre as variáveis usadas para chamá-la.

·       Na chamada por referência, o endereço é usado para acessar o argumento real passado na chamada da função. Nesse caso, as alterações feitas no parâmetro afetam a variável usada para chamar a função.

·       No uso da função fatorial(), verifica-se a chamada por valor.

 

int fatorial(int);

     . . .

  printf("\nFatorial de %d = %d", num, fatorial(num));

     . . .

 

·       Observe este outro exemplo

 

#include <stdio.h>

#include <conio.h>

 

#define NUM_ELEM 10  /* numero de elementos do arranjo */

void leInteiros(int[], int);

void imprimeInvertido(int*, int);

 

void main() {

  int arranjo[NUM_ELEM], i;

 

  clrscr();

  leInteiros(arranjo, NUM_ELEM); /*chamada por referencia*/

  imprimeInvertido(arranjo, NUM_ELEM); /*chamada por referencia*/

}

 

void leInteiros(int vetor[], int lim) {

  int i;

  printf("\nPreenchendo um vetor de %d elementos...\n", lim);

  for (i = 0; i < lim; i++) {

       scanf("%d", &vetor[i]);

  }

}

 

void imprimeInvertido(int *ap, int lim) {

  int i;

  ap = ap + (lim -1);

  printf("\nElementos na ordem inversa a de leitura:\n");

  for (i = lim; i > 0; i--) {

       printf("%5d", *ap);

       ap--;

  }

 

}

 

 

Argumentos para a função main() – argc e argv

·       É possível passar informações para a função main() através de argumentos da linha de comandos.

·       Um argumento da linha de comandos é a informação que segue o nome do programa na linha de comandos do sistema operacional. Por exemplo: (no DOS)

 

     C:\>cd turbo_C

 

·       Há dois argumentos internos especiais: argc e argv, que são usados para receber os argumentos da linha de comandos.

·       O argc contém o número de argumentos da linha de comandos e é um inteiro (seu valor é, no mínimo, 1, porque o nome do programa é qualificado como primeiro argumento).

·       O argv é um apontador para um vetor de apontadores para caracteres. Cada elemento nesse vetor aponta para um argumento da linha de comandos (no caso, strings).

·       Um exemplo simples:

     . . .

     /* programa para mostrar mensagem de boas-vindas */

     void main(int argc, char *argv[]) {

 

         clrscr();

         if(argc != 2) {

              printf(“\nERRO: voce esqueceu de digitar seu nome!”);

              exit(1); /* execucao do programa eh finalizada */

         }

         printf(“Bem-vindo %s!!!”, argv[1]);

     }

 

·       Um outro exemplo: escreva um programa para imprimir a quantidade de linhas da entrada que contêm um padrão especificado.

·       Estrutura do programa:

 

     enquanto (há outra linha)

         se (a linha contém o padrão)

              incrementa contador

     imprima contador

 

-         "enquanto há outra linha" é a função leLinha()

-         "imprima contador" é a função printf()

-         "se a linha contém o padrão" será indiceCad(string, padrao)

 

#include <stdio.h>

#include <conio.h>

 

#define MAXLINHA 85

#define ENTER ‘\n’

#define NULO ‘\0’

 

int leLinha(char linha[], int max);

int indiceCad(char fonte[], char procurado[]);

 

/*

 * encontra todas as linhas que casam com o padrão

 */

void main() {

 

     char linha[MAXLINHA];

     char padrao[] = "an"; /* padrão a procurar */

     int encontradas = 0;

 

     clrscr();

     while (leLinha(linha, MAXLINHA) > 0) {

          if (indiceCad(linha, padrao) >= 0) {

              encontradas++;

          }

     }

     printf(“O padrao %s foi encontrado em %d linhas”, padrao, encontradas);

}

 

/*

 * leLinha: le a linha em string, retorna o tamanho da mesma

 */

int leLinha(char string[], int lim) {

 

     char ch;

     int i;

 

     i = 0;

     while (--lim > 0 && (ch = getchar()) != EOF && ch != ENTER) {

          string[i++] = ch;

     }

     if (ch == ENTER) {

          string[i++] = ch;

     }

     string[i] = NULO;

     return i;

}

 

/*

 * indiceCad: retorna indice de padrao em string, -1 se ausente

 */

int indiceCad(char string[], char padrao[])    {

     int i, j, k;

 

     for (i = 0; string[i] != NULO; i++) {

   for (j = i, k = 0;padrao[k] != NULO &&

   string[j] == padrao[k]; j++, k++);

          if (k > 0 && padrao[k] == NULO) {

              return i;

          }

     }

     return -1;

}

 

·       Exercício: modifique a função indiceCadeia(), e posteriormente a função main() para que esta retorne o número de ocorrências de um padrão em cada linha da entrada.