Вы находитесь на странице: 1из 14

9

Funes

Como j dissemos, a linguagem C permite modularizar uma seqncia de


comandos de forma que possamos execut-la sempre que necessrio, sem a
necessidade de repeti-la fisicamente.
De fato, as funes e
so muito parecidas com as sub-rotinas encontra-
das em outras linguagens como BASIC e Assembly.
Neste captulo, vamos estudar em maiores detalhes o funcionamento, ca-
ractersticas e detalhes das funes na linguagem C.

9.1. Forma Geral


O formato geral (ANSI) de uma funo :

{tipo da funao} nome_da_funao ( {parimetro>>


{
//bl.oco de cCJIDAndoa
copendol;
coaando:21

}

O tipo da funo usado para especificar o tipo de dado do resultado


que a funo vai devolver para o local onde foi chamada. A especificao do
tipo de dado de retorno opcional e caso seja omitida , o compilador assume
que o dado retornado do tipo inteiro.
O nome_da_funo um identificador utilizado para especificar o nome
pelo qual ser conhecida a funo pelo resto do programa . Esse nome deve
ser um identificador vlido da linguagem C e no possvel utilizar um identifi-
cador j utilizado previamente.
Os parmetros da funo so utilizados para passagem de valores para
que a funo possa utiliz-los e efetuar os procedimentos para os quais foi
escrta. Eles consistem em uma lista de tipos e variveis separados por vrg\Jo-
las. A especificao de parmetros opcional , podendo o programador escre-
ver funes que no utilizam qualquer parmetro no seu funcionamento. \
entanto, mesmo que no existam parmetros, os parnteses devem ainda
assim ser utilizados.
Um exemplo de declarao de funo:

inc quadrado ( nt a l
(
reLurn a *a;
l

Note que muitos complladores (o CCS no um deles) suportam a foITTa


original adotada por Kemighan & Ritchie para a declarao de uma funo que
:

{tipo da funo) nome .da funo ( (vari,vea) )


tipoa1
(
//bloco de comando
comandol1
comando21

}

Nesta forma de declarao , somente os nomes das variveis so coloca-


dos na lista de parametros da funo. Os seus tipos so definidos logo abai-
xo, no corpo da funo.
Veja um exemplo da mesma funo "quadrado" anterior, declarada pela
forma clssica da linguagem:

n t quadrao ( a l
int a;
{
return aa ;
}

Lembre-se que esta forma de declarao no suportada pelo compilador


CCS, mesmo porque ela j no mais utilizada e o seu uso no incentivado
pela norma ANSI.
.
A seguir, veja um exemplo da utllizao de uma funao definida pelo pro-
gramador:

' ~--~----------~M-1c_rocoo
~_1ro1__ado__re_s_P1_c_-_Pr_og~rama--~_o_e_m_c______~~~~---.
'
Exemplo 9.1
linclude <16F628 . h>
luse delay(clock=4000000)
tfuses INTRC_IO,NOWDT,PUT,NOBROWNOUT,NOMCLR,NOLVP
luse rs232(baud=19200 ,parityzN,xrnit=PIN_B2.rc v=PI N_Bll

int soma (int a , i nt b )


{
ro t urn a~b;
}

main ()
{
printf ( '1 ~ 1 = %d ' , soma(l,11 ) ;
}

Neste programa. foi definida uma funo chamada soma , que ir retomar
o valor da soma dos seus parmetros "a" e "b". Observe que a palavra lnt
colocada frente do nome da funo especifica que ela vai retornar um valor
do tipo inteiro de 8 bits.
No corpo da funo encontramos apenas o cdigo retum a+b, que utili-
zado para provocar o retorno da funo com o valor especificado, o que no
caso ser a soma do valor dos parmetros "a" e "b" .
No corpo principal do programa ( maln() ) encontramos a funo prlntf
que em um dos seus argumentos realiza uma chamada funo soma com os
parmetros iguais a 1.
Veja que a funo soma recebe os valores 1 e 1 como argumentos e du-
rante a execuo da funo teremos "a =1" e "b =1".
A execuo do programa resulta na seguinte sada:

1 + 1 : 2

Nos prximos tpicos vamos estudar em maiores detalhes os diversos


aspectos da estrutura de uma funo.

9.2. Regras de Escopo


No tpico 5.3 vimos que as variveis podem ser declaradas em diversos
pontos do programa , mas quando so declaradas dentro de uma funo, en-
contramos uma situao especial.
Existem duas possibilidades distintas de declarao de variveis dentro
de uma funo: na sua lista de parmetros e no corpo da funo.
Como j vimos no tpico 5.3.1, uma varivel declarada no corpo de uma
funo chamada de varivel local, pois possuir apenas uma abrangncia
(ou escopo) local, dentro da prpria funo em que foi declarada.
As variveis locais no podem ser acessadas de fora da funo, sendo
acessveis apenas no interior da funo. Alm disso, as variveis locais so
normalmente destrudas aps o retorno da funo (a destruio ou no do
contedo da varivel depende da otimizao e consumo de memria avaliados
pelo compilador durante a compilao do programa).
Outra possibilidade de declarao de uma varivel na seo de parme-
tros da funo.
Como j vimos, uma funo pode conter parmetros de chamada, sendo
definidos na declarao da funo.
Os parmetros da funo possuem um comportamento diferenciado, uma
vez que se comportam como variveis locais em relao visibilidade, e ao
mesmo tempo comportam-se como variveis globais em relao sua aces-
sibilidade.
Isso quer dizer que uma varivel declarada como parmetro formal de
uma funo ter escopo global no sentido de poder ser acessada de qualquer
ponto do programa, para que possa ser utilizada para efetuar a passagem de
parmetros para a funo.

9.3. Passagem de Parmetros


Uma funo pode receber argumentos para a sua execuo de duas for-
mas distintas:
Por valor: copiando o valor de um argumento diretamente para a vari-
vel encarregada de receber o parmetro formal da funo. Este foi o
mtodo utilizado at agora nos nossos programas.
Por referncia: na passagem por referncia, no o valor do argumen-
to que copiado para o parmetro formal da funo e sim o seu ende-
reo. Desta forma, a funo, recebendo o endereo do argumento,
pode utiliz-lo internamente e inclusive alterar o seu valor. Daf resulta
a principal caracterstica da passagem por referncia: a funo pode
alterar o valor do argumento passado a ela.
Possivelmente esta exposio no tenha esclarecido muita coisa ao lei-
tor. Por isso, vejamos o exemplo seguinte:
Exemplo 9.2
tincl ude <16F628.h>
#use de lay (clock=4000000)
#fuses HS , NOWDT,PUT , NOBROWNOUT,NOMCLR , NOLVP
#use rs232( baud=l9200 , parity=N,x:rnit=PIN_B2,rcv=PIN_Bll

f l oat divide(float a, floa t b)


l i Divid e o parmetro a pelo parmetro b
{
i f (!b) re turn O; l i verifica s e b O, se verdadeiro retorna O
a/=b; l i caso b>O a = a / b
return a; // retorna o val or de a
}

rnain ()
{
float a,b,c ;
a = 7;
b = 3;
e = divide (a,b) ;
printf('a= %f , b = %f, e= %f,a,b, c);
l

O resultado impresso pela funo printf ser:

a= 7.000000 , b= 3 . 000000 , e= 2 . 333333

O funcionamento do programa pode ser descrito sucintamente da seguin-


te forma: as variveis "a" , "b" e "e" da funo maln so declaradas e
atribudo o valor 7 a "a" e 3 a "b". Em seguida, chamada a funo divide,
sendo passados como argumentos os valores das variveis "a" e "b" respec-
tivamente.

Durante a execuo da funo divide, o valor recebido pelo parmetro "a"


alterado {" a/=b ") e a funo retoma o valor do parmetro "a" alterado.
Em seguida, o valor retornado pela funo atribudo varivel "e" e
chamada a funo printf que imprime o resultado na sada padro.
Esta uma legtima descrio de uma passagem por valor, j que pode-
mos constatar que o valor armazenado nas variveis locais da funo main,
passados como argumentos, no foram alterados pela funo divide.
Mas como podemos realizar uma chamada por referncia?
Simples: pela utilizao de ponteiros no lugar dos parmetros da funo.
Isto permite que em vez do valor do argumento seja passado o endereo dele.
De posse do endereo do argumento, a funo pode ento, no somente ler o
valor armazenado nele, como tambm alterar esse valor.
Vejamos ento a realizao de uma chamada de funo com passagem
de valores por referncia:

Exemplo 9.3
#include <16F628 . h>
tuse delay {clock=4000000)
#fuses HS,NOWDT,PUT,NOBROWNOUT,NOMCLR,NOLVP
l use r s232 (baud=19200, parity,.N, xmi t=PIN_B2, rcv,.,PIN_Bl)

void di v ide(float *a, float *b)

li Divide o parmetro a pelo parmetro b


{
i f {*b) a l =*b; li caso b>O a = a / b
}

main ( l
(
float a, b;
a "' 7 ;
b "' 3;
divi de (&a,&b) ;
printf ( a= %f , b = %f,a,b);
}

Este programa imprime o seguinte resultado :

a = 2 . 333333 , b= 3 .000000

Observe que agora o valor da varivel "a", que local funo maln , foi
alterado: o resultado da operao de diviso realizada na funo divide foi
armazenado diretamente na varivel "a" citada .
Por que a varivel "a" foi alterada?
Simples: o valor passado para a funo divide no foi o valor de "a" e sim
o endereo da varivel local "a" da funo maln . Isto permite que a funo
divide altere o valor apontado pelo endereo localizado no seu parmetro "a".

9.4. Matrizes com Argumentos de uma Funo


Em C, o compilador normalmente realiza chamadas de funo com par~
metro, utilizando o mtodo de passagem por valor, no entanto, no caso das
matrizes, isto no ocorre.
A utilizao de matrizes como argumentos de uma funo baseia-se no
princpio de que em C, a referncia do nome da matriz, sem a indicao do(s)

Microcontroladores PIC - Programao em C


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--,
elemento(s) interno(s), traduz-se num ponteiro para o primeiro elemento da
matriz.
Assim, quando utlizamos uma matriz como elemento da lista de parme-
tros de uma funo, o compilador ir efetivamente realizar uma passagem por
referncia, mesmo que no especificado pelo programador.
Isto significa que quando utlizamos uma matriz como argumento de uma
funo, apenas um ponteiro para ela passado para a funo e no a matriz
inteira .
Existem trs formas de declarar uma funo para receber matrizes como
argumento:
1.. Declarando a matriz na lista de parmetros da funo.
Neste caso, o programador deve declarar normalmente a matriz e suas
dimenses na lista de parmetros da funo. Veja o exemplo:

Exemplo 9 .4

void s orna ( int matriz [20) , i nt valor)


l i soma o parametro valor a todos os elementos da ma triz
{
int conta ;
for ( conta~O; conta<20 ; conta++) matriz(conta)+e valor;
}
main ()
{
i nt valores[20);
soma (valores , l) ;
}

2. Declarando a matriz sem elementos na lista de parmetros da funo:


Neste caso, o programador deve declarar a matriz sem os seus elemen-
tos , ou seja, uma matriz adimensional. Veja o mesmo exemplo anterior
reescrito:

Exemplo 9.5
void sorna ( int matr iz[] , int valor)
/ / sorna o parametro valor a todos os elementos da matriz
{
int conta;
for (conta=O; conta<20; conta++) matriz[contaJ+= valor;
}
main ()
{
int valores ( 20 J ;
soma (valores, lJ;
)
3. Declarando um ponteiro para o mesmo tipo da matriz na lista de parme.
tros da funo.
Neste caso, em vez de utilizar a matriz como elemento da lista de par-
metros da funo, o programador utiliza apenas um ponteiro do mesmo
tipo de dado da matriz e que ir receber o endereo dela. Veja o mesmo
exemplo anterior reescrito:

Exemplo 9.6
void s orna ( int *matriz , i nt valor)
l i soma o parametro valor a todos os e l ementos da matriz
{
i nt conta;
for (conta=O; conta<20; conta++) matriz[conta]+= valor;
)
main ( )
{
int valores [20J;
soma (valores , l);
}

Um aspecto importante deste exemplo a demonstrao de que a lingua-


gem e permite indexar ponteiros, como acontece com as matrizes, con-
forme j estudado no captulo anterior.

9.5. Estruturas como Argumentos de uma Funo


Tambm possvel a passagem de elementos de uma estrutura ou mes-
mo estruturas inteiras para uma funo.
A passagem de elementos de uma estrutura feita da mesma forma que
a passagem de variveis comuns.
No caso da passagem de estruturas inteiras, apesar de ser possvel a
passagem da estrutura por valor (caso em que a estrutura inteira ser dupl-
cada na memria, j que os PICs no podem utilizar a pilha para armazenar
argumentos de funes), prefervel a passagem por referncia, utilizando um
ponteiro para a estrutura, conforme j discutido no captulo anterior.

9.6. Funes com Nmero de Parmetros Varivel


O padro ANSI e prev ainda a possibilidade de criar funes com nmero
indefinido de parmetros .

~~~~~~~~~M_ic_ro~
con_t~_a_d~~~-P_IC_-_P_rog~ra_ma__o_e_m_C~~~~~~~--.\
A forma bsica de declarao de uma funo com mltiplos parmetros :

tipo_de_re t orno Nome_da_funao (tipo parl, ... )


(
comandos;
}

Um exemplo tpico de funo com mltiplos parmetros a funo prtntf


da linguagem e.
Observe que os compiladores CCS no suportam a criao de funes
com nmero varivel de parmetros .

9. 7. Retorno de Valores
Existem duas formas bsicas de uma funo retornar a execuo ao local
de onde foi chamada:
Pelo trmino da execuo de todos os comandos da funo;
Pela execuo da declarao return .
O retomo da funo pelo trmino da execuo uma forma bastante evi-
dente.
Sempre que a execuo de uma funo alcana o seu ltimo comando,
ocorre o retorno da funo, ou seja, o endereo de retorno retirado da pilha
e carregado no contador de programa do processador, desviando a execuo
para o ponto seguinte chamada da funo.
Abaixo temos um exemplo de retorno de uma funo pelo trmino da exe-
cuo dos seus comandos:

Exemplo 9.7
#include <16F628 . h>
t use delay(clock=4000000)
tfuses lNTRC_IO,NOWDT,PUT,NOBROWNOUT.NOMCLR,NOLVP

void muda_pino(int pino)


l i Inverte o es tado lgico de um pino do microcontrolador
{
ouput_bit (pino , linput(p ino)) ;
)

main {)
(
muda_pino{pin_b3); li inverte o nvel 16gico do pino RB3
)
Foi definida a funo muda_plno, que possui como parmetro o inteiro
pino, que ser utilizado para especificar o pino a ser Invertido pela funo.
Observe que o tipo de dado retomado pela funo vold, ou seja, ne-
nhum valor retornado pela funo.
No corpo da funo encontramos uma chamada a outra funo interna do
compilador: output_blt, que utilizada para colocar um determinado pino do
microcontrolador (o primeiro argumento) em um determinado nvel lgico (o
seu segundo argumento).
Ao trmino da execuo da funo output_blt, a funo muda_pino efetua
o retorno, j que no existe mais cdigo a ser executado.
Um aspecto importante sobre o retorno pelo trmino da execuo da fun
o que neste tipo de retorno de funo, ela normalmente ir retomar urr
valor O. Para que ela retorne outros valores, necessrio utilizar a declarao
return.
Como j foi dito, por padro, uma funo C que no possua declarao de
tipo considerada como sendo do tipo inteira, o que significa que o valor a ser
retornado (return) pela funo ser do tipo inteiro.
,
E possvel retornar outros tipos de valores, bastando selecionar adequa
damente o tipo de dado na especificao de tipo da funo.
Em seguida temos um exemplo de um programa que utiliza mltiplas de-
claraes retum em uma funo cujo tipo de dado de retorno float:

Exemplo 9.8
finc l ude <16F620.h>
#use delay(clock;4000000)
f!uses I!l.'TRC_IO,NOWDT,PU1,NOBROWNOUT,NOMCLR,NOLVP
~ use rs232(baud=l9200,parity=N,xrnit=PIN_B2,rcvcPIN_Bl)

float divide(Eloat a, float b)


l i Divide o parirGtro a PGlo par!retro b
(
if ('b) ret~rn O; /verifica se b O, se vexdadeiro re~orna O
return a/b; // caso b>O re~orna a/b
}

main ()
(
f loat a ,b;
a= 7;
b = 3;
a= divide (a,b);
printf(a= tf , b= \f',a,b);
}
Alm de demonstrar a utilizao de uma funo com tipo de retomo diver-
so do inteiro, o exemplo anterior demonstra tambm que:
possvel utilizar o mesmo nome para variveis locais (como "a" e
"b" na funo maln) e para as variveis utilizadas como parmetros
formais de outra funo ("a" e "b" na funo divide);
possvel utilizar diversas declaraes retum numa mesma funo;
Caso no seja explicitamente declarado o valor de retorno (atravs do
uso da declarao retum, ou equivalente), o valor retornado pela fun-
o ser indefinido.
Um outro aspecto importante a ser destacado que, caso no seja dese-
j ado o retorno de valores da funo , ela deve ser declarada preferencialmente
como vold, o que acarreta, na maioria das vezes, uma maior eficincia e redu-
o do cdigo gerado.

9.8. Retorno de Valores em Funes Assembly


Quando utilizamos linguagem Assembly dentro do bloco de cdigo de uma
funo, pode ser necessrio efetuar o retorno da funo a part.ir do prprio
cdigo Assembly.
Nestes caso, devemos utillzar a varivel interna _RETURN_, definida pelo
compilador, para o armazenamento do valor de retorno da funo.
Vejamos um exemplo de uma funo que soma seus dois parmetros e
devolve o resultado:

Exemplo 9.9

int soma(int a, int b)


{
#asm
movf a,w
addwf b,w
movwf _RETURN_
#endasm
)

mai n()
{
int a,b;
a= 7;
b = 3;
a = soma ( a , b ) ;
}
9.9. Prottipos de Funo
Muitas vezes, quando um programa fica muito complexo e so utilizadas
muitas funes, pode acontecer de ocorrer uma chamada a uma funo que
somente definida mais adicante no programa.

Neste caso, o compilador sinaliza um erro, informando ao programador


que a funo desconhecida (no compilador CCS ser exibida a mensagem:
Undefined /dentifler").

Para solucionar estes problemas, existe em ANSI e (assim como em


Pascal ), a chamada prototipagem de funo.
Prottipo de uma funo uma declarao prvia com o intuito de infor-
mar previamente ao compilador (antes da declarao real da funo) o formato
de declarao da funo, incluindo o t ipo de dado de retomo e os tipos e
quantidade de parmetros.
Um prottipo de funo deve sempre ser declarado obedecendo aos
mesmos tipos e parmetros da declarao da funo.
Normalmente, os prottipos de funes so declarados logo no incio do
programa e sempre antes de fazer referncia s funes prototipadas.
Vejamos ento um exemplo de prototipagem de funo no compilador
CCS:

Exemplo 9.10
#include <16F628 . h>
#use delay(clock=4000000)
#fus e s rNTRc_rO , NOWDT , PUT, NOBROWNOUT , NOMCLR,NOLVP
#use r s 232(baud=19200,pa rity=N,xmit=PIN_B2,rcv=PrN_Bl)

void divide(fl oat *a, f loat *b); l i prottipo de fun~o divide


main ()
(
float a,b;
a= 7 ;
b = 3;
divide (&a, &b) ;
pri ntf("a= %f , b- %f " , a,b);
}

li declaraao da fun o

void divide{float *a, float *b)

li Divide o pa rmetro a p e lo parmetro b


(
if ( *b) a/ =b; // cas o b>O a = a I b
}

Microcontroladores PIC - Programao em C


9.10. Recursividade
Uma funo recursiva aquela que possui dentro do seu cdigo uma
chamada para ela mesma.
Funes recursivas so muito teis em diversos algoritmos de organiza-
o e pesquisa de dados, no entanto, devido ao pequeno tamanho da pilha de
memria dos PICs, incapacidade deles de armazenar dados na pilha e tam-
bm ao uso intensivo da pilha nestes tipos de funo, o compilador CCS no
permite chamadas recursivas a funes .

9.11.. Exerccios
.
1) possvel ter duas variveis com o mesmo nome, estando uma sr-
tuada em uma funo e outra fora de todas as funes?

2) Como podemos declarar uma funo que no retorne valores?

3) Qual a diferena entre passagem de parmetros por valor e por refern-


cia?

4) Na funo seguinte, quantos e quais erros podem ser observados?

void teste (int a, bl;


{
if (a) return (a ); olse return (a+b);
}

5) No fragmento de programa apresentado em seguida, qual o valor arma-


zenado na varivel "v1 "?

void func(int a, int *b)


{
i nt X;
X "' ( * a + *b) / 2;
*a = X;
}
main ( l
{
int vl,v2;
vl = 7;
v2 = 3;
func (&vl, &v2l;
printf ("%u\r\n",vl);
}
6) Assinale Verdadeiro ou Falso:
( } Em C possvel termos uma quantidade varivel de parmetros de
funo.
( } As funes recursivas so admitidas em e, mas no so permitidas
nos compiladores CCS.
( ) Por definio, as funes e retornam tipos char.
( ) Os parmetros formais de uma funo so, para todos os efeitos, va-
riveis locais.
( ) Na passagem de parmetros por referncia, o valor da varivel
passado para a funo.
( ) Por padro, todos os tipos de dados, incluindo-se matrizes e estrutu-
ras, so passados por valor.
( ) Uma funo do tipo VOID no retorna qualquer valor.

Вам также может понравиться