Você está na página 1de 16

LÓGICA DE PROGRAMAÇÃO – LINGUAGEM C

MODULARIZAÇÃO DE ALGORITMOS

1 INTRODUÇÃO:

Definição : Conjunto de comandos agrupados em um bloco que recebe um nome e através deste pode ser ativado.
Objetivo: Reduzir a complexidade dos problemas através da modularização de algoritmos, ou seja, da decomposição
de problemas em partes menores, simplificando as soluções.
Vantagens
Permitem o reaproveitamento de código já construído;
Evitam que um trecho de código que seja repetido várias vezes dentro de um mesmo programa;
Permitem a alteração de um trecho de código de uma forma mais rápida. Com o uso de uma função é
preciso alterar apenas dentro da função que se deseja;
Simplificam os blocos do programa para que não fiquem grandes demais e, por conseqüência, mais difíceis
de entender;
Facilitam a leitura do programa-fonte;
Decompõem o programa em partes(blocos) que possam ser logicamente compreendidos de forma isolada.

Na modularização é utilizada a técnica de Refinamentos Sucessivos, conhecida também como Top-Down, que parte
da decomposição dos problemas, sucessivamente, até conseguir o nível de detalhamento desejado e, então,
desenvolver um subalgoritmo ou módulo para cada subproblema.
Subalgoritmos são trechos de algoritmos que efetuam um ou mais cálculos determinados que, não isoladamente,
mas em conjunto, resolvem o problema proposto. É conveniente utilizá-los quando uma determinada tarefa é
efetuada em diversos lugares no mesmo algoritmo. Podem ser funções que retornam algum valor ou procedimentos
(sub-rotinas) que não retornam nada. Devem ser declarados no início do algoritmo e podem ser chamados em
qualquer ponto após sua declaração.

2 PROCEDIMENTO
Um procedimento (procedure), também chamado de sub-rotina, é um conjunto de instruções que realiza uma
determinada tarefa. È identificado por um nome, por meio do qual é referenciado em qualquer parte do programa
que o chamou. Quando um procedimento é chamado por um programa, ele é executado e, quando termina, o
controle do processamento retorna automaticamente para a primeira linha de instrução após a linha que fez a
chamada do procedimento. A sintaxe em português estruturado é:

procedimento <identificador> (lista de parâmetros)


var
<declaração das variáveis locais do procedimento>
inicio
<código do procediemto>
fim procedimento

Na linguagem C não existem definições para sub-rotinas ou procedimentos. A funções em C são equivalentes a essas
construções.

1
3 FUNÇÕES
A linguagem C é baseada no conceito de blocos de construção chamados de funções. Um programa em C é uma
coleção de uma ou mais funções, onde cada função deve realizar uma tarefa bem específica.
Uma função é uma sub-rotina que contém uma ou mais declarações e realiza uma ou mais tarefas, podendo atuar
sobre dados e retornar um valor. Cada função tem seu próprio nome. Quando um nome é encontrado, a execução
do programa é desviada para a função e, quando retorna, a execução recomeça na próxima linha da função que tiver
feito a chamada.

Exemplo:
funcao_1
Programa {
instrucao;
main( ) :
{instrucao_1; instrucao;
return;
instrucao; }
funcao_1 ( ); funcao_2 funcao_3
instrucao; { {
funcao_2 ( ); funcao_3 ( ); instrucao;
instrucao; :
instrucao; instrucao; instrucao;
instrucao; funcao_4 return; return;
{ } }
funcao_4 ( );
instrucao;
instrucao; :
} instrucao;
return;
}

Figura 1 – Esquema de execução programas com diversas funções

A chamada de uma função é feita através da citação do seu nome no programa seguido opcionalmente de seus
argumentos iniciais entre parênteses, separados por vírgulas. Toda função inicia com abre chaves “{“ e é encerrada
com fecha chaves “}”e, então, a execução retorna para a instrução seguinte à chamada da função.

Exemplo
Considerando um programa que necessita imprimir a mensagem "Pressione a tecla ENTER" várias vezes durante sua
execução e, a cada vez, tenha que esperar o usuário teclar ENTER. Caso o usuário tecle algo diferente o programa
deve emitir um BEEP.
Neste caso, pode ser feito um laço de WHILE sempre que isto seja necessário e, uma alternativa é criar uma função.
Com o uso de funções, este processo de repetição fica simplificado.
Exemplo
#include <conio.h>
#include <dos.h>
#include <stdio.h>

void EsperaEnter() // Definição da função "EsperaEnter"


{
int tecla; // variável local da função “EsperaEnter”
printf("Pressione ENTER\n");
2
do // início do laço
{
tecla = getch(); //função predefinida
if (tecla !=13) // Se não for ENTER
{
sound(700); // Ativa a emissão de um BEEP
delay(10); // Mantém a emissão do som por 10 ms
nosound(); // Para de emitir o som
}
} while(tecla != 13); // 13 é o código ASCII do ENTER
}

void main()
{
EsperaEnter(); // Chamada da função
EsperaEnter();
EsperaEnter();
}

3.1 DECLARANDO E DEFININDO FUNÇÕES

A definição da função informa ao compilador o nome da função, o tipo de retorno, os parâmetros e como ela
trabalha.
A única exigência é que a função seja definida antes de ser utilizada, que pode causar problemas, uma vez que a
maioria dos usuários prefere que o programa principal main( ) esteja no início do programa. A solução é declarar a
função separadamente no início do programa.
Portanto, inicialmente a função deve ser declarada e depois definida no local desejado.

3.1.1 DEFINIÇÃO DE FUNÇÕES

Conforme mencionado anteriormente, a definição da função informa ao compilador o nome da função, o tipo de
retorno, os parâmetros e como ela trabalha. Sua forma geral é:

<tipo de retorno> <identificador> (lista de parâmetros)


{
<código da função>
}

Identificador: nome da função sempre seguido pro parêntesis


Tipo de retorno: (opcional) determina o tipo de valor que a função retorna através da declaração return.
Pode ser qualquer tio válido. Se um tipo for especificado a função retorna um resultado inteiro, que é o tipo
padrão do C.
Lista de parâmetros: lista de todos s tipos e nomes das variáveis que recebem valores dos argumentos
quando a função for chamada.
Na definição da função está implícita sua declaração
Exemplo:
#include <stdio.h>
int Mensagem()
{
printf(“Olá!” );
return (0)
3
}
int main()
(
printf(“Comovai?!”);
}

Este programa define uma função Mensagem( ) que coloca uma string na tela e retorna 0.Esta função é chamada a
partir da função main( ), que é a primeira função a ser executada em um programa C.
Este programa imprime na tela do monitor a frase: Olá! Como vai?!

3.1.2 DECLARAÇÃO DE FUNÇÕES

Alguns programadores preferem que o início do programa seja a primeira parte de seu programa. Para isto a
linguagem C permite que se declare uma função, antes de defini-la. Esta declaração é feita através do protótipo da
função. O protótipo da função é o trecho de código que especifica o o tipo de retorno, o nome e os parâmetros da
função. Por ser uma instrução, deve ser encerrada com ponto e vírgula.
Em geral, pode-se dar qualquer nome a uma função, com exceção de main(). Os tipos de retorno podem ser os tipos
básicos, com modificadores de tipos da linguagem C, ou ainda, tipos definidos pelo usuário.
Sintaxe do protótipo de uma função:

<tipo de retorno> nome da função ( <lista de parâmetros>);

Tipos de parâmetros Nomes dos parâmetros

Exemplo: u n s i g n ed l o n g i n t Ca l c u l a Ar ea ( i n t p a ra m _1 , i n t p ar am _2 ) ;

Tipo de retorno Nome da função Lista de parâmetros

No exemplo a seguir a função SOMA( ) é prototipada antes de ser usada e assim pôde ser chamada antes de ser
definida.

Exemplo:
#include <stdio.h>
void SOMA(float a, int b); // Protótipo da função SOMA( )

void main()
{
SOMA(16.7,15); // Chamada da função SOMA antes de sua definição, mas após sua
prototipação
}

void SOMA(float a, int b) // Definição da função SOMA( )


{
float result; // a declaração de variáveis é igual ao que se faz na função main( )
result = a+b;
printf("A soma de %d com %6.3f é %6.3f\n”, a,b,result);
}

4
3.2 PARÂMETROS E ARGUMENTOS DAS FUNÇÕES

O uso de parâmetros torna mais amplo o uso de uma função,pois possibilitam que seja definida sobre quais dados a
função deve operar. A função sound(freq), por exemplo, recebe como parâmetro a freqüência do som a ser gerado,
permitindo que se defina seu comportamento a partir deste valor.
Para definir os parâmetros de uma função o programador deve explicitá-los como se estive declarando uma variável,
entre os parênteses do cabeçalho da função. Caso precise declarar mais de um parâmetro, basta separá-los por
vírgulas. Um parâmetro, portanto, é uma declaração de que tipo de valor será passado para função, o verdadeiro
valor passado pela função que fez a chamada é denominado argumento. No exemplo abaixo, a função SOMA( )
possui dois parâmetros, sendo o primeiro um float e o segundo um int.
Exemplo:
void SOMA(float a, int b) // basta separar cada parâmetro por vírgulas
{
float result; // a declaração de variáveis é igual ao que se faz na função main( )
result = a+b;
printf("A soma de %d com %6.3f é %6.3f\n”, a,b,result);
}

Os parâmetros da função na sua declaração são chamados parâmetros formais. Através dos argumentos na
chamada da função são passados valores para os parâmetros da função, de acordo com a posição de cada
parâmetro. Ou seja, o primeiro argumento (da chamada) define o valor o primeiro parâmetro (na definição da
função), o segundo argumento define o valor do segundo parâmetro e assim por diante. Os nomes dos argumentos
na chamada não têm relação com os nomes dos parâmetros na definição da função.

Exemplo: Cálculo do Produto

Neste exemplo, é a função produto( ) é definida com três parâmetros inteiros A, B e C, e retorna um inteiro. Na
função main( ) é feita uma chamada para a função produto ( ), sendo passado os argumentos X=12, Y=3 e 7. Esses
valores são copiados nas variáveis A, B e C, respectivamente. Uma vez efetuado o produto, o valor é retornado para
a variável SAIDA. O argumento de função é simplesmente um valor que é passado para a função no momento em
que ela é chamada (12, 3 e 7).
5
Resumindo, argumento refere-se ao valor que é usado para chamar uma função. A variável que recebe o valor dos
argumentos usados na chamada da função é um parâmetro da função. As funções que levam argumentos são
chamadas de funções parametrizadas, como por exemplo as funções printf( ) e scanf( ).
O tipo do argumento usado para chamar uma função deve ser igual ao tipo do parâmetro que está recebendo o
dado argumento. Por exemplo, não se deve chamar a função quadrado( ) a seguir, com um argumento de ponto
flutuante.
Exemplo:
1. #include <stdio.h>
// Definição da função quadrado()
2. int quadrado (int B) //parâmetro B declarado dentro dos parênteses
3. {
4. printf (“%d elevado ao quadrado é %d\n”, B, B * B);
5. }
// Programa Principal
6. void main()
7. {
8. int num; // declaração da variável local da função main()
9. num = 30;
10. quadrado(num); //chama a função quadrado() com o argumento num
11. }

Na linha 2 do algoritmo acima, é definida a função quadrado( ) que retornar um dado do tipo inteiro. A variável B é o
parâmetro desta função e está declarada dentro dos parênteses. No programa principal, função main( ), a variável
num é declarada na linha 10, inicializada com o valor 30 na linha 11 e, este valor, é passado como argumento da
função quadrado( ) quando esta é chamada na linha 12 (o valor 30 é copiado na variável B). A seguir, a função
quadrado( ) imprime o resultado da expressão B*B calculada na linha 5. Assim, a mensagem “30 elevado ao
quadrado é 900” é apresentada na tela do monitor. Depois da impressão, o programa retorna para a função main() e
executa a instrução seguinte à chamada da função, ou seja, a instrução da linha 13 que encerra o programa.

3.3 DECLARAÇÃO RETURN

Existem duas maneiras de uma função terminar a execução e retornar ao seu chamado.
A primeira forma é quando a última instrução na função é executada e o finalizador da função } é encontrado. Por
exemplo, a função a seguir imprime o inverso de um número na tela e é encerrada com o fecha chaves }.
Exemplo:
void pr_inverso(float num)
{
float inverso;
Inverso = 1/num
printf (“o inverso de %6.3f é %6.3f\n”, num, inverso);
}
Uma vez que a mensagem é exibida, não há nada para a função fazer a seguir, então ela retorna ao lugar de onde foi
chamada.
A segunda maneira é quando a declaração return é encontrada. Esta declaração tem dois importantes usos:
provoca uma saída imediata da função corrente, isto é, faz com que a execução do programa retorne para o
código chamador assim que a declaração return é encontrada.
pode ser usada para retornar um valor.

6
A declaração return pode ser usada sem qualquer valor associado a ela. Por exemplo, a função a seguir imprime o
resultado de um número elevado a uma potência positiva. Se o expoente é negativo, a declaração return faz com
que a função termine antes que a chave final seja encontrada, mas nenhum valor é retornado.
Exemplo:
1 #include <stdio.h>
2 void potencia(int base, int exp)
3 {
4 int i;
5 if (exp<0) return; /* nao pode calcular com expoente negativo */
6 i = 1;
7 for ( ; exp; exp--) i = base * i;
8 printf ("A resposta é: %d\n ", i);
9 }
10 main()
11 {
12 int b,e;
13 printf("Digite a base e expoente x,y : ");
14 scanf("%d,%d",&b,&e);
15 potencia(b,e);
16 }

3.3.1 FUNÇÕES QUE RETORNAM VALORES

Em C, uma função pode retornar um valor para uma rotina chamadora usando a palavra reservada return. Para uma
função com retorno, o tipo de retorno da função deve ser declarado. Muitas das funções de biblioteca pré-definidas
retornam valores.
Por exemplo, o programa anterior que imprime a potenciação de número, pode ser reescrito como o programa a
seguir, onde o valor de retorno é associado a uma variável, colocando-se a função do lado direito de uma declaração
de atribuição (linha 15)
Exemplo:
1 #include <stdio.h>
2 int potencia (int base, int exp)
3 {
4 int i;
5 if (exp<0) return; /* nao pode calcular com expoente negativo */
6 i = 1;
7 for ( ; exp; exp--) i = base * i;
8 return i;
9 }
10 main()
11 {
12 int b,e, resposta;
13 printf("Digite a base e expoente x,y : ");
14 scanf("%d,%d",&b,&e);
15 resposta = potencia(b,e);
16 printf ("A resposta é: %d\n ", resposta);
17 }
Na linha 8 a função potencia( ) retorna o valor de “ i “ usando a declaração return. Esse valor é atribuído à variável
resposta. Isto é, o valor retornado pela declaração return torna-se o valor da função potencia ( ) na chamada de
rotina na linha 15.
A variável que recebe o valor de retorno de uma função tem que ser do mesmo tipo que o valor retornado pela
função. O tipo retornado pela rotina potencia( ) é int
7
O valor a ser retornado deve ser colocado após a declaração return. A função max() retorna o maior valor entre dois
argumentos:
Exemplo:
max (int a, int b)
{
int temp;
if (a > b) temp = a;
else temp = b;
return temp;
}

O tipo de retorno padrão de uma função se nenhum outro é explicitamente especificado na definição da função é o
inteiro. É possível uma função conter duas ou mais declarações return. Por exemplo, a função max( ) é melhor
escrita como mostrado aqui:
Exemplo:
max (int a, int b)
{
if (a > b) return a;
else return b;
}
Todas as funções, exceto aquelas declaradas para serem do tipo void, retornam um valor. Isso significa que uma
função pode ser usada como um operando em qualquer expressão válida em C e, portanto, cada uma das seguintes
expressões é válida em C:
x = abs(y);
if (max(x, y) > 100) printf(“excelente”);
for (ch = getchar(); digito(ch); ) ...;
Porém, uma função não pode ser alvo de uma atribuição e, a declaração abaixo é incorreta.
soma(x, y) = 50 ; /* declaração incorreta */
Não é necessário usar o valor de retorno de uma função. Se não há atribuição especificada, o valor retornado é
descartado. Considere o seguinte programa que usa a função mul( ).
Exemplo:
1 #include <stdio.h>
2 int mul (int a, int b)
3 {
4 return (a * b)
5 }
6 void main() {
7 int x, y, z;
8 x = 10;
9 y = 20;
10 z = mul(x, y);
11 printf (“%d”, mul (x, y));
12 mul (x, y);
13 }

Na linha 10, o valor de retorno de mul( ) é associado a variável z. Na linha 11, o valor de retorno é usado pela função
printf( ) e, na linha 12, o valor de retorno é perdido porque não é associado a outra variável nem usado em uma
expressão.

8
3.3.2 FUNÇÕES RETORNANDO VALORES NÃO-INTEIROS

O programa abaixo calcula a área de um círculo, dado o seu raio. A função retorna um valor do tipo float, dado o
argumento do tipo float. Neste caso, é declarado o protótipo da função área( ),permitindo que esta seja definida
após a função mai( ).

Exemplo:
1. #include <stdio.h>
2. float area(float raio); //protótipo da função area( )
3. void main()
4. {
5. float r,res;
6. printf(“Informe o raio: ”);
7. scanf(“%f”, &r);
8. res = area(r);
9. printf(“A área é: %f\n”, res);
10. }
11. float area(float raio) // definição da função area( )
12. {
13. return 3.1416 * raio*raio;
14. }

3.3.3 FUNÇÕES DO TIPO VOID

Funções que não retornam valor podem sem declaradas como void, que impede o seu uso em qualquer expressão e
ajuda a evitar um uso incorreto acidental. Funções do tipo void são puramente procedimentos.
Por exemplo, as funções func1( ) e func2( ) imprimem em cem linhas, uma seqüência de nove caracteres. Uma vez
que não retornam valores, as funções são declaradas como void.

Exemplo:
1. #include <stdio.h>
2. void func1(void), func2(void); /* protótipos das funções func1( ) e func2( )
3. void main()
4. {
5. func1();
6. getch();
7. }
8. void func1(void){
9. int count;
10. for (count = 1; count < 100; count++) func2();
11. }
12. void func2(void){
13. int count;
14. for (count = 1; count < 10; count++) printf ("*");
15. printf("\n");
16. }

9
3.4 ESCOPO DE VARIÁVEIS

Escopo: conjunto de regras que determinam o uso e a validade das variáveis nas diversas partes do problema.
Na linguagem C, cada função é um bloco de código que pode ser acessado por qualquer declaração. Uma função
pode chamar outras funções, mas o código que compreende o corpo de uma função (bloco entre {}) está escondido
do resto do programa, ele não pode afetar nem ser afetado por outras partes do programa, a não ser que o código
use variáveis globais.
Tipos básicos:
Variáveis Locais
Parâmetros -
Variáveis Globais

3.4.1 VARIÁVEIS LOCAIS

São variáveis declaradas dentro de uma função ou de um bloco de código e seus escopos estão restritos ao bloco
onde foram declaradas. Toda variável declarada entre um bloco { } só podem ser referenciadas dentro deste bloco e
existem apenas durante a execução deste bloco.
As variáveis locais são criadas na entrada e destruídas na saída de um código.
Exemplo1: void func1() {
int X;
X=10;
}
void func1() {
int X;
X=-15;
}
A variável X foi declarada duas vezes, uma em cada função e O X em func1() não tem relação com o X em func2(),
pois cada X conhecido somente dentro do código que foi declarado.

Exemplo2: void func(void) {


int X;
void main() {
int X=10;
printf(“O valor de X na função main ( ) e %d \n”, X); // imprime :“O valor de X na main ( ) e 10
func();
printf(“O valor de X na função main ( ) e %d \n”, X); // imprime :“O valor de X na main ( ) e 10
}
void func() {
int X;
X=-15;
printf(“O valor de X na função func( ) e %d \n”, X); // imprime :“O valor de X na func ( ) e -15
}

Exemplo3: void linha( ); //protótipo da função linha


main() {
int tamanho; // variável local da função main()
printf("Digite o tamanho: ");
scanf("%d",&tamanho);
linha(tamanho); // chamada da função linha()
}
void linha(x) // definição da função linha()
int x; // variável

10
{
int i; // variável local da função linha()
for(i=0;i<=x;i++) putchar(95); // imprime o caracter “_” X vezes
}
A variável i na função linha não é reconhecida pela função main()
Não é obrigatório declarar as variáveis no início do código de uma função. Elas podem ser declaradas em qualquer
bloco de código.
Exemplo4: void f1(void ) { //definição da função f1
char ch; // variável local da função f1()
printf(“Continua? (s/n)”);
ch=getche();
if(ch=’s’) {
char str[80]; // variável local neste bloco
printf(“Informe o nome”);
gets(str);
processa(str); // função que realizará algum processo
}
A variável local str será criada na entrada do bloco de código if e destruída na saída, sendo conhecida somente
dentro deste bloco e não pode ser referenciada em outro lugar – mesmo em outras partes da função que a contém.
Como as variáveis locais são criadas e destruídas a cada entrada e saída de um bloco, os seus conteúdos são
perdidos quando o bloco é deixado. Isso significa que variáveis locais não podem reter os seus valores entre
chamadas.

3.4.2 PARÂMETROS FORMAIS

Se uma função usa argumentos, ela deve declarar as variáveis que receberão valores daqueles argumentos. Estas
variáveis são chamadas de parâmetros formais das funções e comportam-se como quaisquer outras variáveis locais
dentro da função

3.4.3 VARIÁVEIS GLOBAIS

São conhecidas por todo programa e podem ser usadas em qualquer parte do código. Permanecem com seu valor
durante toda execução do programa. São armazenadas em uma região fixa da memória própria para esse fim.
Normalmente, são criadas declarando-as fora de qualquer função e podem ser acessadas por qualquer expressão,
não importando em qual função esteja. Porém, as variáveis globais podem ser declaradas em qualquer lugar do
código, contando que sua primeira utilização ocorra após ter sido declarada.
Exemplo: void func1(),func2(); // protótipo das funções func1() e func2()
int cont; // declaração da variável global cont
main() {
cont=100; // atribuição do valor 100 para a variável cont
func1(); // chamada da função func1()
}
void func1() { // definição da função func1()
int temp; //declaração da variável local temp da função func1()
temp=cont;
func2(); // chamada da função func2()
printf("cont é = %d",cont); // imprime: cont é = 100
}
void func2() { // definição da função func2()
int cont; // declaração da variável local cont da função func2()
for(cont=1;cont<10;cont++) printf("*"); // imprime: *********
}

11
Neste programa, a variável global cont foi declarada antes da função main( ) e, embora não tenha sido declarada
dentro da main() nem da func1( ), estas duas funções podem usá-la. Entretanto, func2( ) tem declarado uma variável
local chamada cont. Quando se referencia a variável cont, func2( ) está se referenciando somente a sua variável
local, não a global.
Se uma variável global e uma local tiverem o mesmo nome, todas as referências àquele nome de variável dentro da
função onde a variável local é declarada se referirão somente a ela e não terão efeito na variável global
Variáveis globais são muito úteis quando o mesmo dados é usado em muitas funções no seu programa. Entretanto,
deve-se evitar a utilização de variáveis globais desnecessárias por três razões:
ocupam a memória durante todo o tempo em que o programa está executando, não somente quando
necessário;
torna uma função menos geral, porque ela vai depender de algo que deve ser definido fora dela;
a utilização de um grande número de variáveis globais pode levar o projeto a erros,pois pode se tornar difícil
gerenciá-las

Exemplo1: GERAL ESPECÍFICO


mul (int x, int y) int x, y;
{ {
return (x * y); return (x * y);
} }

As duas funções retornam o produto das duas variáveis x e y. A versão geral, ou parametrizada, pode ser usada para
retornar o produto de dois números quaisquer. A versão específica pode ser usada somente para encontrar somente
o produto das variáveis globais x e y.

3.5 PASSAGEM DE PARÂMETROS ENTRE FUNÇÕES

Os argumentos podem ser passados para as funções de duas maneiras. A primeira é chamada de passagem por
valor que copia o valor de um argumento para um parâmetro formal de uma função. A segunda é chamada por
referência (ou passagem de parâmetro por referência), onde o endereço de um argumento é copiado no seu
parâmetro.

3.5.1 PASSAGEM POR VALOR

A passagem por valor (ou passagem de parâmetro por cópia) copia o valor do argumento na chamada da função
para um parâmetro declarado na função. Portanto, é uma cópia do valor do argumento que é passado para uma
dada função. O que ocorre dentro da função não terá efeito na variável usada na chamada. O valor de um
argumento é copiado para o parâmetro formal da função e as alterações no processamento não alteram as variáveis
usadas para chamar a função.

Exemplo1: int quad(int x); // protótipo da função quad( )


main()
{
int t=10; // declaração e inicialização da variável local t
printf("%d %d",quad(t),t); // chama a função quad( ) e imprime 100 10
}
int quad(int x) // definição da função quad( ) e declaração do parâmetro x
{
x=x*x;
return(x)
}

12
O valor do argumento passado para a função quad( ), 10, é copiado no parâmetro x. Quando a atribuição x = x * x é
executada, modifica somente a variável local x. A variável t, usada para chamar quad( ), ainda terá o valor 10. Por
isso o resultado será 100 10.

3.5.2 PONTEIROS

Para alterar as variáveis que são passadas para uma função, seus parâmetros formais devem ser declarados como
ponteiros.
Ponteiro é uma variável que contém um endereço de memória, ou seja, é uma variável que guarda um endereço de
memória de outro objeto.
Endereço de memória é cada uma das posições de memória, seqüencialmente numeradas, que pode ser dividida a
memória do computador. Qualquer variável, de qualquer tipo, é posicionada em uma única posição ( 1 byte por
posição).
São usados para o gerenciamento de dados na memória disponível, para acessar dados e funções membros de classe
e para a passagem de variáveis por referência para funções

Uma variável que vai armazenar um ponteiro deve ser declarada como tal. Uma declaração de ponteiro consiste em
um tipo base, um * e o nome da variável. A forma geral para declaração de uma variável ponteiro é:
tipo *<nome da variável>;
onde o tipo pode ser qualquer tipo válido da linguagem C e o nome da variável é o nome da variável ponteiro. O tipo
base do ponteiro define qual tipo de variáveis o ponteiro pode apontar.
As declarações abaixo criam um ponteiro caractere e dois ponteiros inteiros.
char *p;
int *temp, *início;
Existem dois operadores especiais de ponteiro: o & e o *.
O & é um operador unário que retorna o endereço de memória do seu operando. Por exemplo,
end_cont = &cont;
coloca em end_cont o endereço de memória da variável cont. Esse endereço é a localização interna da variável no
computador. Ele não faz nada com o valor de cont.
Para entender melhor essa atribuição, assuma que a variável cont esteja localizada no endereço 2000. Então, após a
atribuição, end_cont terá o valor de 2000.
O operador * é o complemento do operador &. Ele é um operador unário que retorna o valor da variável localizada
no endereço que o segue. Por exemplo, se end_cont contém o endereço de memória da variável cont, então
val = *end_cont;
colocará o valor de cont em val. Supondo que a variável cont originalmente tem o valor 100, então val terá o valor
de 100, porque este é o valor armazenado no endereço 2000, que é o endereço de memória que foi atribuído a
end_cont. Neste caso, então, a declaração pode ser lida como “val recebe o valor que está no endereço end_cont”.
Exemplo: /* imprime 100 na tela */
1. #include <stdio.h>
2. void main() {
3. int *end_cont, cont, val;
13
4. cont = 100;
5. end_cont= &cont; //pega o endereço de cont
6. val = *end_cont; //pega o valor armazenado no endereço end_cont
7. printf(“%d”, val); //exibe 100
8. }
Infelizmente, o sinal de multiplicação e o sinal “valor no endereço” são os mesmos, porém, esses operadores não se
relacionam um com o outro. Tanto & como * têm precedência maior que todos os outros operadores aritméticos,
exceto o menos unário, com o qual eles se igualam.

3.5.3 CRIANDO UMA CHAMADA POR REFERÊNCIA

Como o C só faz chamadas por valor, é possível usar os parâmetros formais à vontade dentro da função, sem a
preocupação de estar alterando os valores dos argumentos que foram passados para a função. Porém, pode-se
querer mudar os valores fora da função. Ainda que a convenção de passagem de parâmetros da linguagem C seja a
chamada por valor, é possível criar uma chamada por referência passando-se um ponteiro para o argumento. Esta
maneira é chamada por referência (passagem de parâmetro por referência), onde o endereço de um argumento é
copiado no seu parâmetro.
Este tipo de passagem de parâmetros para uma função ocorre quando alterações nos parâmetros formais, dentro da
função, alteram os valores dos parâmetros que foram passados para a função; neste tipo de chamada, não se passa
para a função os valores das variáveis, mas sim suas referências (a função usa as referências para alterar os valores
das variáveis fora da função). Para usar a função é necessário colocar um & na frente das variáveis que estiverem
sendo passadas para a função.
Exemplo:
1. #include <stdio.h>
2. void troca (int *a,int *b); //declaração dos ponteiros a e b
3. void main (void)
4. {
5. int num1,num2; // declaração das variáveis locais num1 e num2 da main( )
6. num1=100;
7. num2=200;
8. troca (&num1,&num2); /* chamada da função troca passando os endereços das variáveis
num1 e num2 */
9. printf ("\n\nEles agora valem %d %d\n",num1,num2);
10. }
11. void troca (int *a,int *b) // definição da função troca( )
12. {
13. int temp; // declaração da variável local temp da função troca( )
14. temp=*a; //
15. *a=*b;
16. *b=temp;
17. }
Na linha 2 são declarados o protótipo da função troca( ) e duas variáveis do tipo inteiro a e b. Na linha 8, a função
troca é chamada e os endereços das variáveis num1 e num2 são passados (copiados) para os ponteiros a e b, ou
seja, a variável a recebe o endereço da variável num1 e a variável b o endereço de num2.
Na linha 14, o valor da variável num1, armazenada no endereço contido no ponteiro a (&num1) é atribuído à
variável temp, na linha 15, o valor da variável apontada pelo ponteiro b (num2) é atribuído à variável apontada pelo
ponteiro a e, finalmente, o valor da variável apontada pelo ponteiro b recebe o valor de temp
Através do operador * o conteúdo apontado pelos ponteiros é acessado e modificado e, estes conteúdos nada mais
são que os valores armazenados em num1 e num2, que, portanto, estão sendo modificados!

14
Na função scanf() as variáveis chamadas também são precedidas pelo operador de endereços &. Esta função usa
chamada por referência porque ela precisa alterar as variáveis que são passadas para ela! Ela lê uma variável e,
portanto, precisa alterar seu valor. Assim, o endereço da variável a ser modificada é passado para a função com o
operador & sendo colocado na frente dos argumentos da função scanf( ), que recebem valores.

3.6 FUNÇÕES RECURSIVAS

Uma função é recursiva se um comando no corpo da função chama ela mesma. Um algoritmo que para resolver um
problema divide-o em sub-rotinas mais simples, cujas soluções requerem a aplicação dele mesmo, é chamado
recursivo, seja de forma direta ou indireta.
A forma direta ocorre quando uma rotina recursiva R, que é composta por um conjunto de comandos C (que não
chamam a R) mais uma chamada (recursiva) à rotina R. Na forma indireta, as rotinas são conectadas através de uma
cadeia de chamadas recursivas que acaba retornando a primeira que foi chamada.
Para que esteja bem definida, uma função recursiva deve possuir as seguintes propriedades:
(1) Deve haver certos argumentos, chamando valores básicos, para os quais a função não se refere a ela mesma.
(2) Sempre que a função se refere a ela mesma o argumento deve estar relacionado a um valor básico e/ou a um
valor anterior.
Os algoritmos recursivos têm em geral a forma seguinte:
caso de base (base de recursão), onde o problema é resolvido diretamente (sem chamada recursiva)
caso geral, onde o problema é resolvido com uma chamada recursiva
caso geral onde o tamanho do problema é menor a cada chamada
Esquematicamente, os algoritmos recursivos têm a seguinte forma:
se "condição para o caso de base"
então resolução direta para o caso de base
senão
uma ou mais chamadas recursivas
Exemplo1: - Para definir um número natural por meio de recursividade basta utilizar os dígitos 0, 1, 2, 3, 4, 5, 6, 7, 8,
9 mais ou menos outro número inteiro. Por exemplo, o número 15 é o número 7 mais o número 8; 21 é 9 mais 12 e
12 é 9 mais 3.
Exemplo2 - exemplo clássico de recursividade: cálculo do fatorial de um número, onde o fatorial de um número N é
o produto de todos os números inteiros entre 1 e N, ou seja:
F(n) = 1 se n = 0 ou n = 1;
F(n) = n.F(n-1), se n>1.
onde n é um numero inteiro positivo. Uma propriedade (facilmente verificável) dos fatoriais é que:
n! = n . (n-1)!
Esta propriedade é chamada de propriedade recursiva: o fatorial de um número pode ser calculado através do
fatorial de seu antecessor. Logo, pode-se, por exemplo, escrever uma rotina recursiva para o cálculo do fatorial de 4
F(0) = 1 F(0) = 1 caso base
F(1) = 1*1 F(1) = 1*F(1-1)
F(2) = 2*1 F(2) = 2*F(2-1)
F(3) = 3*2 F(3) = 3*F(3-1)
F(4) = 4*6 F(4) = 4*F(4-1)
Assim, F(4) = 4*3*2*1 = 24
Se fosse nº 6, F(6) = 6*5*4*3*2*1=720
Para que o algoritmo termine, as chamadas recursivas devem convergir em direção ao caso de base, senão o
algoritmo não terminará jamais. Convergir significa ter uma parte menor do problema para ser resolvido. No
exemplo acima, as chamadas recursivas ( F(n)=n*F(n-1) convergem em direção ao caso base (F(0) = 1)

15
Exemplo3 : função fatorial_recursivo() e sua equivalente iterativa:

Cálculo iterativo do fatorial de um número


1. #include <stdio.h>
2. unsigned long int fatorial (int n) { // definição da função fatorial( )
3. unsigned long int t, resposta; // declaração da variável local resposta
4. resposta = 1; // inicialização da variável resposta
5. for (t = 1; t < n; t++) resposta = resposta * t; // cálculo do fatorial
6. return(resposta);
7. } // encerra função fatorial( )
8. void main(){ // definição da função main( )
9. unsigned long f; // declaração da variável local f
10. int n; // declaração da variável local n
11. printf(“Digite um número: ”);
12. scanf(“%d”,&n);
13. f = fatorial(n); // chamada da função fatorial( )
14. printf(“O fatorial de %d é %ld\n”, n, f);
15. }

Cálculo recursivo do fatorial de um número


1. #include <stdio.h>
2. unsigned long int fatorial_recursivo (int n){ //definição da função fatorial_recursivo( )
3. unsigned long int resposta; // declaração da variável local resposta
4. if ((n == 1) || (n == 0)) return(1);
5. resposta = n * fatorial_recursivo(n – 1); // chamada da função fatorial_recursivo( )
6. return (resposta);
7.
8. void main(){ // definição da função main( )
9. unsigned long f; // declaração da variável local f
10. int n; // declaração da variável local n
11. printf(“Digite um número: ”);
12. scanf(“%d”,&n);
13. f = fatorial_recursivo(n); // chamada da função fatorial_recursivo( )
14. printf(“O fatorial de %d é %ld\n”, n, f);
15. } // encerra função main( )

Quando uma função chama a si própria, as novas variáveis locais e os argumentos são alocado,, e o código da função
é executado com esses novos valores a partir do início. Uma chamada recursiva não faz uma nova cópia da função.
Somente os argumentos e as variáveis são novos. Quando cada chamada recursiva retorna, as antigas variáveis locais
e os parâmetros são removidos e a execução recomeça no ponto de chamada da função dentro da função.
Vantagens da Recursão
Simplifica a solução de alguns problemas;
Geralmente, um código com recursão é mais conciso;
Caso não seja usada, em alguns problemas, é necessário manter o controle das variáveis manualmente.
Desvantagens da Recursão
Funções recursivas são mais lentas que funções iterativas, pois muitas chamadas consecutivas a funções são
feitas;
Erros de implementação podem levar a estouro de pilha. Isto é, caso não seja indicada uma condição de
parada, ou se esta condição nunca for satisfeita, entre outros.

16