c_introducao_apontadores.html 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. <!--
  2. Introducao `a Programacao - desde 2017 - Prof. Leo^nidas de Oliveira Branda~o
  3. Introducao ao apontadores em C
  4. LInE (Laboratory of Informatics in Education) - http://www.usp.br/line
  5. IME - USP
  6. Material dida'tico
  7. Pode usar livrevemente este material para fins nao comerciais, devendo sempre fazer referencia `a autoria.
  8. Sugestoes/apontamento são bem vindos: leo@ime.usp.br (favor indicar no assunto "Material de introducao 'a programacao")
  9. Autoria: Prof. Leo^nidas de Oliveira Branda~o
  10. http://www.ime.usp.br/~leo
  11. http://line.ime.usp.br
  12. http://www.matemtica.br
  13. -->
  14. <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
  15. <meta name='keywords' content='mac0122, material, professores, leonidas de oliveira brandao'>
  16. <link rel='stylesheet' type='text/css' href='css_img_js_conf/all.css'>
  17. <link rel='stylesheet' type='text/css' href='css_img_js_conf/line_introducao_programacao.css'>
  18. <script src="css_img_js_conf/defineLInE.js"></script> <!-- para referencias 'a documentos internos -->
  19. <div class="pagina">
  20. <!--
  21. <center><p>[
  22. <a href="memoria" title="sobre memoria, bits e bytese">Memória</a> &nbsp; | &nbsp;
  23. <a href="#variaveis" title="variaveis inteiras e reais">Variáveis</a> &nbsp; | &nbsp;
  24. <a href="#expressoes" title="expressoes aritmetics">Expressões</a> &nbsp; | &nbsp;
  25. <a href="#contexto" title="resultado de uma expressao depende do contexto">Contexto</a> &nbsp; &nbsp;
  26. ]</p>
  27. </center>
  28. -->
  29. <p class="secao">Introdução aos apontadores em C</p>
  30. <p>
  31. Nesta seção apresentaremos o conceito de apontadores na linguagem <i>C</i>.
  32. </p>
  33. <a name="padrao">
  34. <p class="subsecao">Lembrete do tratamento padrão para variáveis em praticamente todas as linguagens de programação</p>
  35. </a>
  36. <p>
  37. Usualmente, em qualquer linguagem de programação, sempre que usamos em um código uma variável, significa que deve-se pegar o valor armazenado nesta variável.
  38. Assim, um trecho com <tt>10*b</tt>, implica em pegar o valor corrente armazenado na variável <tt>b</tt> e
  39. multiplicá-lo por <tt>10</tt>.
  40. </p>
  41. <p>
  42. <a name="porque">
  43. <p class="subsecao">Por que precisamos de apontadores</p>
  44. </a>
  45. <p>
  46. Uma boa razão é que na linguagem <i>C</i> as funções devolvem apenas valores simples (como inteiro ou real, mas nunca uma estrutura).
  47. Então imagina precisarmos de uma função que computa dois ou mais valores, sem apontador seria impossível devolver de alguma forma
  48. ambos os valores!
  49. Mas com apontadores podemos passar para esta função os apontadores para duas variáveis locais (i.e., seus <i>endereços</i> ou suas
  50. <i>referências</i>) e, dentro da função registrar as alterações na variável apontada.
  51. </p>
  52. <p>
  53. Um exemplo onipresente de apontadores na linguagem <i>C</i> é a função de leitura <tt>scanf</tt> usada para providenciar leitura de dados.
  54. Isso é feito via apontadores, por exemplo, o comando para ler e guardar em uma variável inteira é:
  55. <tt>scanf("%d", &a);</tt>.
  56. <br/>
  57. Cujo significado é: pegar <i>bytes</i> até encontrar um separador ou finalizador <tt>ENTER</tt>, interpretar os <i>bytes</i> como
  58. um número inteiro e guardá-lo no endereço indicado da variável <tt>a</tt>.
  59. <br/>
  60. <b>O operador <tt>&</tt> devolve o endereço de uma variável.</b>
  61. </p>
  62. <a name="como">
  63. <p class="subsecao">Ideia de apontadores</p>
  64. </a>
  65. <p>
  66. Assim a ideia básica de um <b>apontador</b> é que a linguagem deve dispor de um recurso de dois passos para pegar um conteúdo,
  67. uma <i>indireção</i>.
  68. Para ficar mais claro, comparemos com as variáveis "usuais":
  69. invocar o nome desta variável "usual" pegamos diretamente seu conteúdo,
  70. mas ao tentar pegar o conteúdo apontado por uma variável apontadora,
  71. precisamos <u>primeiro</u> pegar o que ele guarda (um endereço) e <u>depois</u>
  72. ir para este endereço e dali pegar efetivamente o conteúdo (seria o <i>conteúdo apontado</i>).
  73. Existe um operador especial para esta indireção, que é o operador <tt>*</tt>.
  74. <br/>
  75. <b>O <b style="color:#00aa00">operador <tt>*</tt></b> só pode ser aplicado à <i>variáveis apontadoras</i> (ou para declarar uma) e
  76. devolve o conteúdo guardado na variável apontada.</b>
  77. <br/>
  78. Da mesma forma, existe um operador "inverso" ao <tt>*</tt>, o operador <tt>&</tt> que devolve o <i>endereço ocupado por uma variável</i>.
  79. <br/>
  80. <b>O <b style="color:#00aa00">operador <tt>&</tt></b> indica que deve-se pegar o endereço da variável</b>.
  81. <br/>
  82. Logo, usando a declaração <tt><verm>int</verm> *a, b=-2, c = 3;</tt>, o código <tt>a = &c; *a = b;</tt> indica, respectivamente, que:
  83. <tt>a</tt> receberá o endereço de <tt>c</tt> e depois,
  84. a variável apontada por <tt>a</tt> (no caso <tt>c</tt>) receberá o valor guardado em <tt>b</tt>.
  85. Ou seja, ao final <tt>b</tt> e <tt>c</tt> estarão com o mesmo valor <tt>-2</tt>.
  86. Na fig. 1 está a situação após executar a instrução <tt>a = &c;</tt> e antes de executar a instrução <tt>*a = b;</tt>.
  87. </p>
  88. <p>
  89. <center><!-- style="float: left; margin-right: 1%; margin-bottom: 0.5em;justify-content: center;" -->
  90. <img src="img/img_introd_apontador.png"
  91. title="memoria com variaveis a, b, p1 e p2"/><br/>
  92. <i>Fig. 1: Representação da memória RAM com espaços para as variáveia <tt>a</tt>, <tt>b</tt>, <tt>c</tt>, <tt>p1</tt> e <tt>p2</tt>.
  93. </center>
  94. </p>
  95. <p>
  96. Por exemplo, vamos imaginar outra situação que a figura 1 poderia ilustrar.
  97. Suponha que precisemos alterar os valores das variáveis inteiras <tt>b</tt> e de <tt>c</tt>
  98. dentro de uma função <tt>f</tt> (que nada devolve, logo <tt><verm>void</verm></tt>).
  99. <br/>
  100. <bloco1>
  101. Desse modo, a declaração da função deveria ser <tt><verm>void</verm> f (<verm>int</verm> *p1, <verm>int</verm> *p2)</tt> e sua chamada deveria ser <tt>f(&b, &c)</tt>.
  102. <br/>
  103. Lembrando: <tt>p1</tt> e <tt>p2</tt> são parâmetros formais, que receberão valores vindo dos <i>parâmetros efetivos</i>, que são
  104. <tt>&b</tt> e <tt>&c</tt>.
  105. <br/>
  106. Isso quer dizer que <tt>p1</tt> e <tt>p2</tt> serão <b>variáveis apontadoras para <tt>b</tt> e <tt>c</tt></b>, ou seja,
  107. deverão <i style="color:#0000aa">guardar os endereços de variáveis inteiras</i> e não valores inteiros!
  108. <br/>
  109. Suponha ainda que os resultados que <tt>b</tt> e <tt>c</tt> deveriam receber dentro da função <tt>f</tt> sejam,
  110. respectivamente, os valores guardados nas variáveis <tt>v1</tt> e <tt>v2</tt> (variáveis locais declaradas na função <tt>f</tt>).
  111. <br/>
  112. Então a função <tt>f</tt> deveria ter como últimas duas linhas os comandos
  113. <tt>*p1 = v1;</tt> e <tt>*p2 = v2;</tt>.
  114. <br/>
  115. Como o operador <b><tt>*</tt></b> indica
  116. <i style="color:#0000aa">pegar o valor no endereço que é apontado</i>, então <tt>*p1 = v1; *p2 = v2;</tt> resulta em:
  117. <i style="color:#0000aa">guarde em <tt>b</tt> o valor que encontra-se em <tt>v1</tt></i> e
  118. <i style="color:#0000aa">guarde em <tt>c</tt> o valor que encontra-se em <tt>v2</tt></i>
  119. (pois <i style="color:#0000aa"><tt>p1</tt> <b>aponta para <tt>b</tt></b> (guarda o endereço de <tt>b</tt>) e
  120. <tt>p2</tt> <b>aponta para <tt>c</tt></b></i>).
  121. </bloco1>
  122. </p>
  123. <a name="exemploA">
  124. <p class="subsecao">Exemplo inicial de apontadores</p>
  125. </a>
  126. <p>
  127. Vamos examinar um exemplo simples: usar um apontador para alterar o valor guardado em outra variável.
  128. </p>
  129. <p>
  130. <pre><incl1>#include</incl1> &lt;stdio.h&gt;
  131. <verm>int</verm> main (<verm>void</verm>) {
  132. <verm>int</verm> *a, b, c; <cyan>// a deve guardar endereco de variavel inteira (nao e' o mesmo que guardar inteiro)</cyan>
  133. b = 5; c = 11;
  134. <verd>printf</verd>("b=%d, c=%d\n", b, c); <cyan>// deve resultar na tela: b=5, c=11</cyan>
  135. a = &c; <cyan>// a recebe endereco de c</cyan>
  136. *a = b; <cyan>// que e' apontado por a recebe valor guardado em b</cyan>
  137. <verd>printf</verd>("b=%d, c=%d\n", b, c); <cyan>// deve resultar na tela: b=5, c=5 (por que?)</cyan>
  138. }</pre>
  139. <i>Algoritmo 1. A variável apontadora <tt>a</tt> receberá o endereço da variável <tt>c</tt>.
  140. </p>
  141. <p>
  142. Antes de continuar a leitura, copie o código acima, cole em seu editor preferido, grave, compile e rode.
  143. Verá que o resultado é o indicado nos comentários. Por que?
  144. </p>
  145. <p>
  146. Resposta: vamos examinar as linhas chaves e traduzir o que elas fazem.<br/>
  147. 1. <tt>a = &c;</tt>: nesta linha a <u>variável apontadora</u> <tt>a</tt> recebe <b>o endereço da variável inteira <tt>c</tt></b>;
  148. <br/>
  149. 2. <tt>*a = b;</tt>: nesta linha existe a "indireção", o conteúdo guardado em <tt>b</tt> será atribuido à
  150. <u>variável apontada</u> por <tt>a</tt>, que é a variável <tt>c</tt>, logo <tt>c</tt> receberá o que está em <tt>b</tt>.
  151. </p>
  152. <p>
  153. Portanto, poderíamos trocar os comandos 1 e 2 acima, por um equivalente mais simples: <tt>c = b;</tt>
  154. </p>
  155. <a name="exemploB">
  156. <p class="subsecao">Exemplo de apontadores como parâmetros (parâmetros por referência)</p>
  157. </a>
  158. <p>
  159. Vamos retomar a figura 1 com um exemplo que se adequa bem a ela: uma função para trocar os conteúdos de duas variáveis.
  160. </p>
  161. <pre><incl1>#include</incl1> &lt;stdio.h&gt;
  162. <verm>void</verm> troca (<verm>int</verm> *p1, <verm>int</verm> *p2) {
  163. <verm>int</verm> aux; <cyan>// para trocar o conteudo e' obrigatorio termos mais uma variavel (senao perdemos um dos valores)</cyan>
  164. aux = *p1; <cyan>// aux recebe o conteudo da variavel apontada por p1</cyan>
  165. *p1 = *p2; <cyan>// a variavel apontada por p1 recebe o conteudo da variavel apontada por p2</cyan>
  166. *p2 = aux; <cyan>// a variavel apontada por p2 recebe o conteudo em aux</cyan>
  167. }
  168. <verm>int</verm> main (<verm>void</verm>) {
  169. <verm>int</verm> a = 5, b = 11;
  170. <verd>printf</verd>("a=%d, b=%d\n", a, b); <cyan>// deve resultar na tela: a=5, b=11</cyan>
  171. troca(&a, &b); <cyan>// passa os parametros por referencia (ou endereco)</cyan>
  172. <verd>printf</verd>("a=%d, b=%d\n", a, b); <cyan>// deve resultar na tela: a=11, b=5</cyan>
  173. }</pre>
  174. <i>Algoritmo 2. Uso de passagem de parâmetro por referência (ou endereço).
  175. </p>
  176. <a name="exemploB">
  177. <p class="subsecao">Exemplo da recursividade do conceito e implementação em <i>C</i> de apontadores</p>
  178. </a>
  179. <p>
  180. Da mesma forma que pode-se declarar <i>vetor de vetor de vetor...</i> (<tt>int vet[D0][D1][D2]...</tt>),
  181. pode-se fazer a mesma coisa com <i>apontador de apontador de apontador...</i>.
  182. Claro que isso só é recomendável em situações muito especiais. Só implemente apontadores níveis mais altos se estiver muito
  183. seguro de que isso é necessário.
  184. <br/>
  185. O código no <i>algoritmo 3</i> apresenta a possibilidade de um número arbitrário de indireções com 3 níveis de indireção
  186. e a figura 2 ilusta esse algoritmo.
  187. </p>
  188. <p>
  189. <center><!-- style="float: left; margin-right: 1%; margin-bottom: 0.5em;justify-content: center;" -->
  190. <img src="img/img_introd_apontador_apontador_apontador.png"
  191. title="memoria com variaveis apontador para apontador para apontador"/><br/>
  192. <i>Fig. 2: Representação da memória RAM com variáveis apontadoras de até 3 níveis.
  193. </center>
  194. </p>
  195. <pre><incl1>#include</incl1> &lt;stdio.h&gt;
  196. <verm>int</verm> main (<verm>void</verm>) {
  197. <verm>int</verm> ***a, **b, *c, d = 11, e = 3; <cyan>// declara apontadores de varios niveis - tem que ser compativeis!</cyan>
  198. c = &d; <cyan>// c aponta para d</cyan>
  199. b = &c; <cyan>// b aponta para c = b aponta para quem aponta para c</cyan>
  200. a = &b; <cyan>// a aponta para b = b aponta para quem aponta para quem aponta para c</cyan>
  201. <verd>printf</verd>("***a=%d, **b=%d, *c=%d, d=%d, e=%d\n", ***a, **b, *c, d, e);
  202. <cyan>// printf acima deve resultar em: ***a=11, **b=11, *c=11, d=11, e=3</cyan>
  203. ***a = e; <cyan>// apontado por apontado por apontado por a recebe e => d recebe valor em e</cyan>
  204. <verd>printf</verd>("***a=%d, **b=%d, *c=%d, d=%d, e=%d\n", ***a, **b, *c, d, e);
  205. <cyan>// printf acima deve resultar em: ***a=3, **b=3, *c=3, d=3, e=3</cyan>
  206. }</pre>
  207. <i>Algoritmo 3. Exemplo da recursividade da definição de apontador.
  208. </p>
  209. <p>
  210. Novamente, experimente o código do algoritmo 2 até estar seguro de compreendê-lo.
  211. </p>
  212. <p class="autoria">
  213. <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/>
  214. <a href="http://www.ime.usp.br/~leo" target="_blank" title="seguir para a página do LInE">http://line.ime.usp.br</a>
  215. </p>
  216. <p class="rodape">
  217. <b>Alterações</b>:<br/>
  218. 2020/10/16: explicações adicionais e novo exemplo de apontador em diversos níveis<br/>
  219. 2020/08/20: acerto no formato<br/>
  220. 2020/05/05: primeira versão do texto<br/>
  221. </p>
  222. </div>