<!-- Introdução à Programação - 2017 - Prof. Leoônidas de Oliveira Brandão Introdução ao uso de funções em C: variáveis locais, globais e aninhamento de funções LInE (Laboratory of Informatics in Education) - http://www.usp.br/line IME - USP Material didático Pode usar livrevemente este material para fins não comerciais, devendo sempre fazer referência à autoria. Sugestões/apontamento são bem vindos: leo@ime.usp.br (favor indicar no assunto "material de introducao 'a programacao") Prof. Leônidas de Oliveira Brandão http://www.ime.usp.br/~leo http://line.ime.usp.br http://www.matemtica.br --> <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'> <meta name='keywords' content='mac0122, material, professores, leonidas de oliveira brandao'> <link rel='stylesheet' type='text/css' href='css_img_js_conf/all.css'> <link rel='stylesheet' type='text/css' href='css_img_js_conf/line_introducao_programacao.css'> <script src="css_img_js_conf/defineLInE.js"></script> <!-- para referencias 'a documentos internos --> <div class="pagina"> <p class="secao">Introdução ao uso de funções em C: variáveis locais, globais e aninhamento de funções</p> <p class="subsecao">Sobre o aninhamento de funções na linguagem <i>C</i></p> <p> Nesta seção apresento um conceito mais "sutil" de programação, para entendê-lo é essencial que você copie os códigos apresentados, "cole" em seu editor preferido e os teste até que esteja tudo claro. Experimente alterar os exemplos e criar outros: grave-os, compile-os e os <i>rode</i>. <b style="color:#0000aa">Para prender programação é preciso programar!</b> </p> <p> Em várias linguagens de programação, <i>C</i> em particular, é possível declarar função dentro de função (<b>aninhar</b>). Por exemplo, dentro de uma função de nome <i>funcao2</i>, pode-se declarar outra função de nome <i>funcao3</i>. O resultado é que, dentro da primeira função, pode-se invocar a segunda, mas em nenhum outro local do código seria possível invocar esssa função <i>funcao3</i>. </p> <p> Mas vale destacar que esse recurso só é efetivamente útil se a função interna (<i>funcao3</i>) só fizer sentido dentro da função que a contém. Pois em caso contrário, você poderia invocar a função interna em outros contextos. Um exemplo dessa situação é ilustrado no parágrafo seguinte, a função <i>fatorial</i> poderia ficar melhor se declarada fora da função <i>combinacao</i>. </p> <p> Um exemplo simples de função contendo outra função, poderia ser o caso do cálculo da combinação de <i>n</i>, tomados <i>k</i>-a-<i>k</i>: <i>C<sub>n,k</sub> = n!/(k!(n-k)!)</i>. Como para o calculo de <i>C<sub>n,k</sub></i> é necessário invocar o cálculo do fatorial 3 vezes, podemos desejar fazer um implementação que deixe essa dependência clara, definindo uma função de nome <i>combinacao</i> e, dentro dela, declarar a função <i>fatorial</i>. Vide exemplo 1. </p> <p> Porém, como anteriormente comentado, como a função <i>fatorial</i> tem potencial de aplicação mais amplo, ela ficaria melhor se declarada <b style="color:#0000aa">fora</b> da função <i>combinacao</i> (vide a crítica a esse organização de código logo após o exemplo 1). </p> <div class="exemplo"><i>Exemplo 1</i>. Declaração aninhada de funções. </div><!-- class="exemplo" --> <div class="codigo"><pre><verm>int</verm> combinacao (<verm>int</verm> n, <verm>int</verm> k) { <cyan>// combinacao de n, k-a-k</cyan> <verm>int</verm> fatorial (<verm>int</verm> n) { <cyan>// fatorial de n (supondo n natural)</cyan> <verm>int</verm> i, fat = 1; for (i=2; i≶=n; i++) fat *= i; return fat; } return fatorial(n) / (fatorial(k)*fatorial(n-k)); } <verm>void</verm> main (<verm>void</verm>) : <verm>int</verm> n = 5, k = 3; <verd>printf</verd>("Combinacao %d, %d-a-%d = %d\n", n, k, k, combinacao(n,k)); <cyan>// Problema de declarar 'fatorial()' dentro de 'combinacao()': a linha abaixo daria erro!</cyan> <cyan>// Erro: exemplo_funcao_aninhada.c:(.text+0xd3): undefined reference to fatorial'</cyan> <cyan>// <verd>printf</verd>("fat(0)=%d, fat(1)=%d, fat(2)=%d, fat(3)=%d", fatorial(0), fatorial(1), fatorial(2), fatorial(3));</cyan> }</pre> </div> <p> Note que o código acima apresenta a linha comentada <tt><verd>printf</verd>("fat(0)=%d, fat(1)=%d, fat(2)=%d, fat(3)=%d", fatorial(0), fatorial(1), fatorial(2), fatorial(3));</tt>. A razão é que ela resultaria erro, pois a função <i>fatorial</i> foi declarada dentro da função <i>combinacao</i> e portanto só pode ser usando (invocada) dentro dessa segunda! </p> <p> Uma vez que o uso do cálculo de fatorial é muito comum e pensando que o código acima seria parte de um maior, então provavelmente o desenho de código usado não é bom, pois implicaria em precisarmos definir outra função <i>fatorial</i>, essa fora do contexto da função <i>combinacao</i>. Nesse sentido, poderia ser melhor usar o código (pensando que ele seja o início de um sistema maior) do exemplo a seguir. </p> <div class="exemplo"><i>Exemplo 2</i>. Declaração de funções sem aninhamento. </div><!-- class="exemplo" --> <div class="codigo"><pre><verm>int</verm> fatorial (<verm>int</verm> n) { <cyan>// fatorial de n (supondo n natural)</cyan> <verm>int</verm> i, fat = 1; for (i=2; i≶=n; i++) fat *= i; return fat; } <verm>int</verm> combinacao (<verm>int</verm> n, <verm>int</verm> k) { <cyan>// combinacao de n, k-a-k</cyan> return fatorial(n) / (fatorial(k)*fatorial(n-k)); } <verm>void</verm> main (<verm>void</verm>) : <verm>int</verm> n = 5, k = 3; <verd>printf</verd>("Combinacao %d, %d-a-%d = %d\n", n, k, k, combinacao(n,k)); <cyan>// Note que agora pode-se invocar 'fatorial()' dentro da 'main'</cyan> <verd>printf</verd>("fat(0)=%d, fat(1)=%d, fat(2)=%d, fat(3)=%d", fatorial(0), fatorial(1), fatorial(2), fatorial(3)); }</pre> </div> <a name="variaveis"> <p class="subsecao">Variáveis globais e locais</p> </a> <p> Outro conceito importante relacionado com funções é o de declaração de variáveis. Assim, uma variável é <b>local</b> se declarada dentro de uma função qualquer, quer dizer, essa variável será "conhecida" (poderá ser usada) apenas dentro dessa função. Por exemplo, no <i>exemplo 2</i> acima, a variável <i>fat</i> é conhecida apenas dentro da função <i>fatorial</i>, portanto seu uso deve-se restringir a esse contexto. </p> <p> Por outro lado, pode-se usar o mesmo nome para variáveis que estão em contextos distintos. Por exemplo, no código do <i>exemplo 1</i>, se fosse necessário poderíamos usar o nome de variável local <i>i</i> dentro da função <i>combinacao</i> e não haveria qualquer colisão com a variável <i>i</i> de <i>fatorial</i>. </p> <p> Entretanto é necessário um critério que elimine <i>ambiguidades</i>, isto é, que permita identificar unicamente cada uma das variáries. Para isso usa-se o <b>princípio da proximidade</b> (ou da <b>localidade</b>), quer dizer, ao usar uma variável considera-se como sua declarção aquela mais próxima. Por isso, que poderiamos usar <i>i</i> tanto em <i>combinacao</i>, quanto em <i>fatorial</i>, </p> <center> <img src="img/funcoes_var_locais.png" title="imagem ilustrando principio da localidade"/> <br/> <i>Fig. 1. Imagem de código ilustrando o princípio da localidade, variável <tt>var1</tt> dentro de <tt>funcao2</tt> é a declarada localmente. </center> <p> Já uma variável <b>global</b> pode ser acessada em qualquer ponto do código. Mas por essa mesma razão deve evitada, sendo necessária apenas para casos excepcionais, sempre com um nome significativo, que evite qualquer confusão. </p> <p> Em <i>C</i>, toda variável declarar fora do contexto de qualquer função, é uma variável <b>global</b>, ou seja, uma variável que pode ser acessada em qualquer ponto do código (em qualquer função). A ideia de variável global é ilustrado no próximo exemplo. </p> <div class="exemplo"><i>Exemplo 3</i>. Declaração de variável global. </div><!-- class="exemplo" --> <div class="codigo"><pre><verm>char</verm> var_global1[] = "essa variavel e' uma variavel global"; <verm>int</verm> funcao () { <verd>printf</verd>("funcao1: var_global1 = %s\n", var_global1); } <verm>void</verm> main (void) { <verd>printf</verd>("main : var_global1 = %s\n", var_global1); <cyan>// uso da global</cyan> funcao(); }</pre> </div> <p class="subsecao">Aninhando funções e variáveis locais e globais</p> <p> Deve-se notar que é possível aninhar tantas funções quantas forem necessárias e o mesmo para variáveis locais. O exemplo abaixo ilustra esses aninhamentos e o princípio da proximidade. Examine-o com atenção, copie-o em seu computador, utilizando um compilador <i>C</i> e altere seu código, compile-o e rode-o até que esteja certo de ter assimilado os conceitos. </p> <div class="exemplo"><i>Exemplo 4</i>. Declaração de variáveis locais, usando globais e aninhamento de funções. </div><!-- class="exemplo" --> <div class="codigo"><pre style="font-size:0.8em;">#include <stdio.h> <verm>char</verm> glob1[] = "glob1: variavel global, conhecida em qualquer local"; <verm>void</verm> funcao1 () { <verm>char</verm> loc1[] = "loc1: conhecida apenas dentro da funcao 'funcao1'"; <verm>char</verm> glob1[] = "funcao1.glob1: declarada dentro de 'funcao1' => uma variavel local `a funcao 'funcao1'"; <verd>printf</verd>("funcao1: loc1 = %s\n", loc1); <cyan>// note que: pode haver m</cyan> <verd>printf</verd>("funcao1: glob1 = %s\n\n", glob1); } <verm>void</verm> funcao2 (param1) { <verm>char</verm> loc1[] = "loc1: conhecida apenas dentro da funcao 'funcao2 - NAO sou a 'funcao1.loc1'!'"; <verm>char</verm> loc2[] = "loc2: conhecida apenas dentro da funcao 'funcao2'"; <verm>void</verm> funcao3 () { <cyan>// essa funcao so' e' conhecida dentro de 'funcao2'!</cyan> <verm>char</verm> loc1[] = "loc1: conhecida apenas dentro da funcao 'funcao3' - NAO sou a 'funcao1.loc1' e nem a 'funcao2.loc1'!"; <verm>char</verm> glob1[] = "funcao3.glob1: declarada dentro de 'funcao3' => var. local `a funcao 'funcao3' - NAO e' global 'glob1' e nem 'funcao1.glob1'!";</cyan> <!-- " --> <verd>printf</verd>("funcao3: loc1 = %s\n", loc1); <cyan>// note que: e' local com outra com esse nome; "principio do mais proximo" em acao!</cyan> <verd>printf</verd>("funcao3: loc2 = %s\n", loc2); <cyan>// note que: e' local `a funcao 'funcao2' - variavel declarada fora de funcao3!</cyan> <verd>printf</verd>("funcao3: glob1 = %s\n", glob1); } <verd>printf</verd>("funcao2: param1 = %s\n", param1); <verd>printf</verd>("funcao2: loc1 = %s\n", loc1); <cyan>// note que: e' local com outra com esse nome; "principio do mais proximo" em acao!</cyan> <verd>printf</verd>("funcao2: loc2 = %s\n", loc2); <cyan>// note que: e' local</cyan> <verd>printf</verd>("funcao2: glob1 = %s\n", glob1); <verd>printf</verd>("funcao2: chama funcao3\n\n"); funcao3(); <verd>printf</verd>("funcao2: chama funcao1\n\n"); funcao1(); } <verm>void</verm> main () { <verm>char</verm> loc1[] = "loc1: conhecida apenas dentro da funcao 'main'"; <verd>printf</verd>("main : loc1 = %s\n", loc1); <cyan>// note que: e' local com outra com esse nome; "principio do mais proximo" em acao!</cyan> <verd>printf</verd>("main : glob1 = %s\n", glob1); <verd>printf</verd>("main : chama funcao1\n\n"); funcao1(); <verd>printf</verd>("main : chama funcao2\n\n"); funcao2("parametro efetivo passado para a funcao 'funcao2'"); <cyan>// Atencao: como a funcao 'funcao3' foi declarada dentro da funcao 'funcao2', entao ela so' pode se invocada em 'funcao2'</cyan> <cyan>// Por exemplo, a linha abaixo resultaria no erro: sobre_variaveis_global_local.c:(.text+0x4d2): undefined reference to funcao3'</cyan> <cyan>// funcao3();</cyan> }</pre> </div> <p class="autoria"> <a href="https://www.ime.usp.br/~leo" target="_blank" title="seguir para a pagina do prof. Leônidas">Leônidas de Oliveira Brandão</a><br/> <a href="http://www.ime.usp.br/~leo" target="_blank" title="seguir para a página do LInE">http://line.ime.usp.br</a> </p> <p class="rodape"> <b>Alterações</b>:<br/> 2020/08/15: novo formato, pequenas revisões<br/> 2020/08/09: formato, revisão geral;<br/> 2018/05/22: pequenas alterações<br/> 2018/05/21: pequenas alterações </p> </div>