123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- <!--
- Introducao `a Programacao - desde 2017 - Prof. Leo^nidas de Oliveira Branda~o
- Introducao ao apontadores em C
- LInE (Laboratory of Informatics in Education) - http://www.usp.br/line
- IME - USP
- Material dida'tico
- Pode usar livrevemente este material para fins nao comerciais, devendo sempre fazer referencia `a autoria.
- Sugestoes/apontamento são bem vindos: leo@ime.usp.br (favor indicar no assunto "Material de introducao 'a programacao")
- Autoria: Prof. Leo^nidas de Oliveira Branda~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">
- <!--
- <center><p>[
- <a href="memoria" title="sobre memoria, bits e bytese">Memória</a> |
- <a href="#variaveis" title="variaveis inteiras e reais">Variáveis</a> |
- <a href="#expressoes" title="expressoes aritmetics">Expressões</a> |
- <a href="#contexto" title="resultado de uma expressao depende do contexto">Contexto</a>
- ]</p>
- </center>
- -->
- <p class="secao">Introdução aos apontadores em C</p>
- <p>
- Nesta seção apresentaremos o conceito de apontadores na linguagem <i>C</i>.
- </p>
- <a name="padrao">
- <p class="subsecao">Lembrete do tratamento padrão para variáveis em praticamente todas as linguagens de programação</p>
- </a>
- <p>
- 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.
- Assim, um trecho com <tt>10*b</tt>, implica em pegar o valor corrente armazenado na variável <tt>b</tt> e
- multiplicá-lo por <tt>10</tt>.
- </p>
- <p>
- <a name="porque">
- <p class="subsecao">Por que precisamos de apontadores</p>
- </a>
- <p>
- 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).
- Então imagina precisarmos de uma função que computa dois ou mais valores, sem apontador seria impossível devolver de alguma forma
- ambos os valores!
- 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
- <i>referências</i>) e, dentro da função registrar as alterações na variável apontada.
- </p>
- <p>
- Um exemplo onipresente de apontadores na linguagem <i>C</i> é a função de leitura <tt>scanf</tt> usada para providenciar leitura de dados.
- Isso é feito via apontadores, por exemplo, o comando para ler e guardar em uma variável inteira é:
- <tt>scanf("%d", &a);</tt>.
- <br/>
- Cujo significado é: pegar <i>bytes</i> até encontrar um separador ou finalizador <tt>ENTER</tt>, interpretar os <i>bytes</i> como
- um número inteiro e guardá-lo no endereço indicado da variável <tt>a</tt>.
- <br/>
- <b>O operador <tt>&</tt> devolve o endereço de uma variável.</b>
- </p>
- <a name="como">
- <p class="subsecao">Ideia de apontadores</p>
- </a>
- <p>
- 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,
- uma <i>indireção</i>.
- Para ficar mais claro, comparemos com as variáveis "usuais":
- invocar o nome desta variável "usual" pegamos diretamente seu conteúdo,
- mas ao tentar pegar o conteúdo apontado por uma variável apontadora,
- precisamos <u>primeiro</u> pegar o que ele guarda (um endereço) e <u>depois</u>
- ir para este endereço e dali pegar efetivamente o conteúdo (seria o <i>conteúdo apontado</i>).
- Existe um operador especial para esta indireção, que é o operador <tt>*</tt>.
- <br/>
- <b>O <b style="color:#00aa00">operador <tt>*</tt></b> só pode ser aplicado à <i>variáveis apontadoras</i> (ou para declarar uma) e
- devolve o conteúdo guardado na variável apontada.</b>
- <br/>
- 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>.
- <br/>
- <b>O <b style="color:#00aa00">operador <tt>&</tt></b> indica que deve-se pegar o endereço da variável</b>.
- <br/>
- 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:
- <tt>a</tt> receberá o endereço de <tt>c</tt> e depois,
- a variável apontada por <tt>a</tt> (no caso <tt>c</tt>) receberá o valor guardado em <tt>b</tt>.
- Ou seja, ao final <tt>b</tt> e <tt>c</tt> estarão com o mesmo valor <tt>-2</tt>.
- 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>.
- </p>
- <p>
- <center><!-- style="float: left; margin-right: 1%; margin-bottom: 0.5em;justify-content: center;" -->
- <img src="img/img_introd_apontador.png"
- title="memoria com variaveis a, b, p1 e p2"/><br/>
- <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>.
- </center>
- </p>
- <p>
- Por exemplo, vamos imaginar outra situação que a figura 1 poderia ilustrar.
- Suponha que precisemos alterar os valores das variáveis inteiras <tt>b</tt> e de <tt>c</tt>
- dentro de uma função <tt>f</tt> (que nada devolve, logo <tt><verm>void</verm></tt>).
- <br/>
- <bloco1>
- 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>.
- <br/>
- 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
- <tt>&b</tt> e <tt>&c</tt>.
- <br/>
- 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,
- deverão <i style="color:#0000aa">guardar os endereços de variáveis inteiras</i> e não valores inteiros!
- <br/>
- Suponha ainda que os resultados que <tt>b</tt> e <tt>c</tt> deveriam receber dentro da função <tt>f</tt> sejam,
- respectivamente, os valores guardados nas variáveis <tt>v1</tt> e <tt>v2</tt> (variáveis locais declaradas na função <tt>f</tt>).
- <br/>
- Então a função <tt>f</tt> deveria ter como últimas duas linhas os comandos
- <tt>*p1 = v1;</tt> e <tt>*p2 = v2;</tt>.
- <br/>
- Como o operador <b><tt>*</tt></b> indica
- <i style="color:#0000aa">pegar o valor no endereço que é apontado</i>, então <tt>*p1 = v1; *p2 = v2;</tt> resulta em:
- <i style="color:#0000aa">guarde em <tt>b</tt> o valor que encontra-se em <tt>v1</tt></i> e
- <i style="color:#0000aa">guarde em <tt>c</tt> o valor que encontra-se em <tt>v2</tt></i>
- (pois <i style="color:#0000aa"><tt>p1</tt> <b>aponta para <tt>b</tt></b> (guarda o endereço de <tt>b</tt>) e
- <tt>p2</tt> <b>aponta para <tt>c</tt></b></i>).
- </bloco1>
- </p>
- <a name="exemploA">
- <p class="subsecao">Exemplo inicial de apontadores</p>
- </a>
- <p>
- Vamos examinar um exemplo simples: usar um apontador para alterar o valor guardado em outra variável.
- </p>
- <p>
- <pre><incl1>#include</incl1> <stdio.h>
- <verm>int</verm> main (<verm>void</verm>) {
- <verm>int</verm> *a, b, c; <cyan>// a deve guardar endereco de variavel inteira (nao e' o mesmo que guardar inteiro)</cyan>
- b = 5; c = 11;
- <verd>printf</verd>("b=%d, c=%d\n", b, c); <cyan>// deve resultar na tela: b=5, c=11</cyan>
- a = &c; <cyan>// a recebe endereco de c</cyan>
- *a = b; <cyan>// que e' apontado por a recebe valor guardado em b</cyan>
- <verd>printf</verd>("b=%d, c=%d\n", b, c); <cyan>// deve resultar na tela: b=5, c=5 (por que?)</cyan>
- }</pre>
- <i>Algoritmo 1. A variável apontadora <tt>a</tt> receberá o endereço da variável <tt>c</tt>.
- </p>
- <p>
- Antes de continuar a leitura, copie o código acima, cole em seu editor preferido, grave, compile e rode.
- Verá que o resultado é o indicado nos comentários. Por que?
- </p>
- <p>
- Resposta: vamos examinar as linhas chaves e traduzir o que elas fazem.<br/>
- 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>;
- <br/>
- 2. <tt>*a = b;</tt>: nesta linha existe a "indireção", o conteúdo guardado em <tt>b</tt> será atribuido à
- <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>.
- </p>
- <p>
- Portanto, poderíamos trocar os comandos 1 e 2 acima, por um equivalente mais simples: <tt>c = b;</tt>
- </p>
- <a name="exemploB">
- <p class="subsecao">Exemplo de apontadores como parâmetros (parâmetros por referência)</p>
- </a>
- <p>
- 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.
- </p>
- <pre><incl1>#include</incl1> <stdio.h>
- <verm>void</verm> troca (<verm>int</verm> *p1, <verm>int</verm> *p2) {
- <verm>int</verm> aux; <cyan>// para trocar o conteudo e' obrigatorio termos mais uma variavel (senao perdemos um dos valores)</cyan>
- aux = *p1; <cyan>// aux recebe o conteudo da variavel apontada por p1</cyan>
- *p1 = *p2; <cyan>// a variavel apontada por p1 recebe o conteudo da variavel apontada por p2</cyan>
- *p2 = aux; <cyan>// a variavel apontada por p2 recebe o conteudo em aux</cyan>
- }
- <verm>int</verm> main (<verm>void</verm>) {
- <verm>int</verm> a = 5, b = 11;
- <verd>printf</verd>("a=%d, b=%d\n", a, b); <cyan>// deve resultar na tela: a=5, b=11</cyan>
- troca(&a, &b); <cyan>// passa os parametros por referencia (ou endereco)</cyan>
- <verd>printf</verd>("a=%d, b=%d\n", a, b); <cyan>// deve resultar na tela: a=11, b=5</cyan>
- }</pre>
- <i>Algoritmo 2. Uso de passagem de parâmetro por referência (ou endereço).
- </p>
- <a name="exemploB">
- <p class="subsecao">Exemplo da recursividade do conceito e implementação em <i>C</i> de apontadores</p>
- </a>
- <p>
- Da mesma forma que pode-se declarar <i>vetor de vetor de vetor...</i> (<tt>int vet[D0][D1][D2]...</tt>),
- pode-se fazer a mesma coisa com <i>apontador de apontador de apontador...</i>.
- Claro que isso só é recomendável em situações muito especiais. Só implemente apontadores níveis mais altos se estiver muito
- seguro de que isso é necessário.
- <br/>
- 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
- e a figura 2 ilusta esse algoritmo.
- </p>
- <p>
- <center><!-- style="float: left; margin-right: 1%; margin-bottom: 0.5em;justify-content: center;" -->
- <img src="img/img_introd_apontador_apontador_apontador.png"
- title="memoria com variaveis apontador para apontador para apontador"/><br/>
- <i>Fig. 2: Representação da memória RAM com variáveis apontadoras de até 3 níveis.
- </center>
- </p>
- <pre><incl1>#include</incl1> <stdio.h>
- <verm>int</verm> main (<verm>void</verm>) {
- <verm>int</verm> ***a, **b, *c, d = 11, e = 3; <cyan>// declara apontadores de varios niveis - tem que ser compativeis!</cyan>
- c = &d; <cyan>// c aponta para d</cyan>
- b = &c; <cyan>// b aponta para c = b aponta para quem aponta para c</cyan>
- a = &b; <cyan>// a aponta para b = b aponta para quem aponta para quem aponta para c</cyan>
- <verd>printf</verd>("***a=%d, **b=%d, *c=%d, d=%d, e=%d\n", ***a, **b, *c, d, e);
- <cyan>// printf acima deve resultar em: ***a=11, **b=11, *c=11, d=11, e=3</cyan>
- ***a = e; <cyan>// apontado por apontado por apontado por a recebe e => d recebe valor em e</cyan>
- <verd>printf</verd>("***a=%d, **b=%d, *c=%d, d=%d, e=%d\n", ***a, **b, *c, d, e);
- <cyan>// printf acima deve resultar em: ***a=3, **b=3, *c=3, d=3, e=3</cyan>
- }</pre>
- <i>Algoritmo 3. Exemplo da recursividade da definição de apontador.
- </p>
- <p>
- Novamente, experimente o código do algoritmo 2 até estar seguro de compreendê-lo.
- </p>
- <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/10/16: explicações adicionais e novo exemplo de apontador em diversos níveis<br/>
- 2020/08/20: acerto no formato<br/>
- 2020/05/05: primeira versão do texto<br/>
- </p>
- </div>
|