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.
· 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 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);
}
· 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?
· 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--;
}
}
· É 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.