\documentclass[apostila.tex]{subfiles} \begin{document} \chapter{Ponteiros} Ponteiros são ferramentas extremamente versáteis e flexíveis disponibilizadas pela linguagem C para manipulação da memória. Através de ponteiros o programador tem controle praticamente total sobre o armazenamento de dados na memória. \section{O que são ponteiros?} Para entender o que é um ponteiro é necessário visualizar a memória como se fosse um grande e contínuo vetor. Vamos supor que a memória do computador é um vetor {\it M[n]}, como representado abaixo, {\bf N+1} é o tamanho da memória: \begin{tabular}{cllllllllllll} & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & ... & N \\ \cline{2-13} {\bf M} & \multicolumn{1}{|l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf 12}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} \\ \cline{2-13} \multicolumn{1}{l}{} & & & & & & & {\bf A} & & & & & \end{tabular} Em um programa C, é possível declarar uma variável qualquer e uma região desta memória será automaticamente dedicada a esta variável. Por exemplo, considere a seguinte declaração: \begin{lstlisting} int A; \end{lstlisting} Quando o programa é executado, a declaração acima faz o sistema operacional alocar\footnote{Alocar uma memória para uma variável é obter uma região disponível da memória, de tamanho suficiente para conter os dados desta variável.} uma região disponível da memória, que será apelidada de {\bf A}. No exemplo, é ilustrada uma região da memória alocada. \begin{tabular}{cllllllllllll} & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & ... & N \\ \cline{2-13} {\bf M} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf 12}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} \\ \cline{2-13} \multicolumn{1}{l}{} & & & & & & & {\bf A} & & & & & \end{tabular} Ou seja, no exemplo acima, o símbolo A é, na verdade, um apelido para a célula M[6]. Um ponteiro é uma variável que será utilizada como índice do vetor memória, ou seja, um ponteiro é uma variável que aponta para uma determinada posição de memória. Por exemplo, suponha que tenha sido declarado um ponteiro de nome P. \begin{tabular}{cllllllllllll} & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & ... & N \\ \cline{2-13} {\bf M} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf 12}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf 6}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} \\ \cline{2-13} \multicolumn{1}{l}{} & & & & & & & {\bf A} & & & {\bf P} & & \end{tabular} A princípio, na ilustração acima, o ponteiro {\bf P} tem a mesma aparência que a variável {\bf A}, ou seja, é alocada também uma região de memória para que seu valor possa ser armazenado ({\it V[9]}) e a essa região denomina-se {\bf P}. A diferença entre um ponteiro e uma variável numérica tradicional é que um ponteiro é um tipo de dado que pode ser utilizado para {\it indexar} a memória, o que não é permitido fazer com as outras variáveis. Ou seja, o ponteiro {\bf P} pode ser usado em uma forma que equivale a {\it M[{\bf P}]}, ou seja, o seu conteúdo (que no exemplo é o valor {\bf 6}) pode ser utilizado para obter o conteúdo de uma determinada posição de memória. É importante ressaltar que a notação utilizada da memória como um vetor {\it M[ ]} é apenas para fins de ilustração. Não existe nenhum nome especial para denominar um vetor que é a memória. \section{Declarando ponteiros} Se um ponteiro aponta para uma determinada região de memória, faz sentido determinar qual é o tipo de dado armazenado na posição para a qual ele aponta. Dessa forma, sempre que um ponteiro é declarado, deve ser especificado o tipo de dado para o qual êle aponta. Como um ponteiro pode apontar para qualquer tipo de dado, faz sentido uma sintaxe que visa simplificar a declaração. Na linguagem C, para declarar um ponteiro basta acrescentar o símbolo '*' antes do que seria uma declaração de tipo tradicional na linguagem C. \begin{lstlisting} tipo *nome-do-ponteiro; \end{lstlisting} Exemplos de declaração de ponteiros: \begin{lstlisting} char *ptr1; // ponteiro para um char int *ptr2; // ponteiro para um int double *ptr3; // ponteiro para um double struct registro { int campo1, campo2; } *reg; // ponteiro para uma estrutura do tipo registro \end{lstlisting} Observe que um ponteiro também é considerado um tipo de dado, portanto não existe nenhuma restrição que impeça de se declarar um ponteiro que aponta para outro ponteiro. Por exemplo: \begin{lstlisting} int *P; int **Pp; \end{lstlisting} Acima, foi declarado uma variável chamada Pp que aponta para uma região de memória cujo conteúdo é do tipo int*. Essa situação é ilustrada abaixo: \begin{tabular}{cllllllllllll} & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & ... & N \\ \cline{2-13} {\bf M} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{32} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf 9}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf 3}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} \\ \cline{2-13} \multicolumn{1}{l}{} & & & & & & & {\bf Pp} & & & {\bf P} & & \end{tabular} Ou seja, {\it Pp} pode ser usado para indexar a memória {\it M} e obter o conteúdo da posição {\bf 9}, que por sua vez também é um ponteiro e pode ser usado para indexar a memória {\it M} e obter o conteúdo da posição {\bf 3}. Equivalentemente, se {\bf M} é um vetor, para obter o conteúdo da posição 3 a seguinte declaração seria suficiente: M[M[{\bf M[Pp]}]] $\leftarrow$ M[{\bf M[9]}] $\leftarrow$ M[3] \section{Utilizando ponteiros} Até agora foi discutido sobre ponteiros para indexar a memória, mas também foi observado que não existe um vetor de memória {\it M} para que o ponteiro possa ser utilizado para indexar esse vetor. Para indexar a memória utilizando o valor de uma variável que é ponteiro, basta preceder o nome da variável com o símbolo `*'. Neste contexto, o operador `*' é denominado {\it derreferenciador}. É necessário cuidado para não confundir o operador de {\bf derreferenciação} (que é unário) com o operador de {\bf multiplicação} (que é binário). Por exemplo, dada a seguinte declaração: \begin{lstlisting} int *P, A; \end{lstlisting} Para poder indexar a memória do valor de {\it P} e atribuir esse valor a variável A, é necessária a seguinte linha de código: A = *P; $\leftarrow$ equivalente a: {\bf A = M[P];} É importante diferenciar o símbolo {\it *P} segundo o contexto em que ele se encontra. Toda vez que aparece um tipo de dado, um `*' e logo após um símbolo que nomeia a variável (i.e., {\tt int *P}), isso não é um acesso à posição de memória, é apenas uma declaração que denomina que {\it P} será um ponteiro para uma posição de memória cujo conteúdo é do tipo {\it int}. Suponha que a memória está no seguinte estado: \begin{tabular}{cllllllllllll} & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & ... & N \\ \cline{2-13} {\bf M} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{31} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf 12}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf 3}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} \\ \cline{2-13} \multicolumn{1}{l}{} & & & & & & & {\bf A} & & & {\bf P} & & \end{tabular} Ao executar {\tt A = *P}, a memória assumiria a seguinte configuração: \begin{tabular}{cllllllllllll} & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & ... & N \\ \cline{2-13} {\bf M} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{31} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf {\it 31}}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf 3}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} \\ \cline{2-13} \multicolumn{1}{l}{} & & & & & & & {\bf A} & & & {\bf P} & & \end{tabular} Ou seja, o valor que está armazenado na posição 3 do vetor (31) é atribuído à variável {\it A}. É importante observar que a atribuição só é considerada válida porque {\it P} foi declarado como ponteiro para um {\it int}. Se {\it P} fosse declarado da seguinte forma \begin{lstlisting} struct { int x; } *P; \end{lstlisting} A atribuição {\tt A = *P} {\bf não} seria válida e decorreria em erro na compilação, embora a aparência na memória fosse a mesma, já que a estrutura tem apenas um campo que é do tipo {\it int}. No entanto, se {\it P} apontasse para uma estrutura, como proposto, como os tamanhos de ambas as variáveis são os mesmos, ainda parece fazer sentido a atribuição proposta. Com uma pequena modificação, proposta a seguir, a atribuição se torna possível. {\tt A = *((int*)P) } Considerando que {\it P} aponta para uma estrutura, faz sentido que o programador deseje acessar os membros dessa estrutura. Existem dois modos para acessar um campo de uma estrutura que é apontada por um ponteiro: \begin{lstlisting} (*P).nomecampo; P->nomecampo; \end{lstlisting} As declarações acima são equivalentes, ou seja, o resultado é o mesmo. Tome como a exemplo a seguinte declaração: \begin{lstlisting} struct { char codigo[2]; int n; float cr; } *P; strcpy(P->codigo, "X"); P->n = 23; (*P).cr = 13.6; \end{lstlisting} No vetor de memória a declaração poderia assumir o seguinte aspecto: \begin{tabular}{cllllllllllll} & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & ... & N \\ \cline{2-13} {\bf M} & \multicolumn{1}{|l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf 12}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} \\ \cline{2-13} \multicolumn{1}{l}{} & & & & & & & {\bf A} & & & & & \end{tabular} Por motivos de simplicidade, o exemplo acima assume que as variáveis do tipo {\it int} e {\it float} ocupam a mesma quantidade de espaço em memória, o que não acontece na realidade. O processo de indexar a memória com o valor do ponteiro é também conhecido como {\it derreferenciamento} do ponteiro. Até este momento, a utilização de ponteiros envolveu apenas indexar uma determinada região de memória, sem considerar a validade desta ação. Na realidade, um ponteiro não pode ser efetivamente usado se não estiver apontando para um região válida da memória, ou seja, uma região que tenha sido alocada especificamente para o o seu programa. No momento da declaração de um ponteiro, o seu conteúdo, que é a posição para onde está apontando, é {\it lixo}, como acontece na declaração de quaisquer outras variáveis. Portanto, é necessário especificar para onde deseja-se que o ponteiro aponte. Existem dois modos de fazer um ponteiro apontar para uma posição válida: \begin{itemize} \item fazer o ponteiro apontar para uma variável existente no programa; \item alocar um espaço dinamicamente para o ponteiro. {\it Alocação dinâmica} é assunto do próximo capítulo. Para fazer um ponteiro apontar para uma variável existente no programa, deve haver um meio para obter o endereço da variável em questão. \end{itemize} O operador \& , quando utilizado precedendo uma variável, obtêm o endereço de memória da variável em questão. Nesse contexto, o operador \& é chamado de {\it referenciador}. É necessário cuidado para não confundir o operador de {\it referência} (que é unário) com o operador de {\it AND} (que é binário). Dada a seguinte declaração de uma variável qualquer e a de um ponteiro para este tipo de variável: \begin{lstlisting} tipo nome var; tipo* P; \end{lstlisting} Para fazer como que o ponteiro {\it P} aponte para a variável {\it nome var}, a seguinte declaração é necessária: \begin{lstlisting} P = &nome var; \end{lstlisting} Por exemplo, suponha as seguintes declarações: \begin{lstlisting} struct { char codigo[2]; int n; float cr; } registro, *P; \end{lstlisting} A memória assumiria o seguinte aspecto para a declaração acima: \begin{tabular}{lllllllllllllllll} \multicolumn{1}{c} & 0 & 1 & 2 & & 3 & 4 & 5 & 6 & & 7 & 8 & & 9 & & ... & N \\ \cline{2-17} \multicolumn{1}{c}{{\bf M}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{X} & \multicolumn{1}{l|}{\0} & \multicolumn{1}{l|}{23} & \multicolumn{1}{l|}{3.6} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf 3}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} \\ \cline{2-17} & & & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{2}{r|}{.codigo} & \multicolumn{1}{r}{.n} & \multicolumn{1}{r|}{.cr} & \multicolumn{1}{l|}{} & & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf P}} & \multicolumn{1}{l|}{} & & \\ \cline{6-9} \cline{13-13} \cline{15-15} & & & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{4}{c|}{registro} & \multicolumn{1}{l|}{} & & & & & & & \\ \cline{5-10} \end{tabular} Após a seguinte linha de código: \begin{lstlisting} P = ®istro; \end{lstlisting} A memória assumiria o seguinte aspecto: \begin{tabular}{lllllllllllllllll} \multicolumn{1}{c}& 0 & 1 & 2 & & 3 & 4 & 5 & 6 & & 7 & 8 & & 9 & & ... & N \\ \cline{2-17} \multicolumn{1}{c}{{\bf M}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{X} & \multicolumn{1}{l|}{\0} & \multicolumn{1}{l|}{23} & \multicolumn{1}{l|}{3.6} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf 3}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} \\ \cline{2-17} & & & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{2}{r|}{.codigo} & \multicolumn{1}{r}{.n} & \multicolumn{1}{r|}{.cr} & \multicolumn{1}{l|}{} & & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf P}} & \multicolumn{1}{l|}{} & & \\ \cline{6-9} \cline{13-13} \cline{15-15} & & & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{4}{c|}{registro} & \multicolumn{1}{l|}{} & & & & & & & \\ \cline{5-10} \end{tabular} Observe que o valor do ponteiro {\it P} agora é {\bf 3}, que é a posição de memória onde inicia a estrutura {\it registro}. Observe o programa abaixo: \lstinputlisting{exemplos/ponteiro.c} O programa acima imprime na tela o alfabeto em maiúsculas, nada muito misterioso. Mas considere {\it como} o programa cumpre esta simples tarefa. O programa acima realiza uma impressão que se refere apenas ao ponteiro {\it pc}. A chave deste enigma está no ponteiro {\it pc}. Inicialmente, foi declarado um ponteiro para uma variável {\it char}. A seguir, é atribuída a posição de memória referente à variável {\it c} para o ponteiro {\it pc}. Dessa forma, é possível acessar indiretamente o conteúdo da variável {\it c}, derreferenciando o ponteiro. \subsection{Exercício} Dadas as seguintes declarações: \begin{lstlisting} struct computador { char processador[10]; // nome do processador int placa video; // codigo da placa de video int modem:1; // possui modem? (s/n) int modem code:7; // codigo do modem (se aplicavel) } comp1; typedef struct computador* comp_ponteiro; \end{lstlisting} Desenhe um diagrama da memória com a estrutura {\it comp_1} representada. Escreva o código necessário para que o ponteiro {\it comp_ponteiro} aponte para esta estrutura, e complete o diagrama com essa nova situação. Declare um novo ponteiro {\it pcomp} que aponte para {\it comp_ponteiro} e escreva como deveria ser o código para acessar um dos campos da estrutura apontada por {\it comp_ponteiro} através do {\it pcomp}. \section{Passagem de parêmetros por referência} Muitas vezes, existe a necessidade de que funções alterem o valor de um ou mais parâmetros. Para que isso seja possível é utilizada uma técnica conhecida como {\it passagem de parâmetro por referência}, êm contraposição ao método habitual que é a {\it passagem de parâmetro por valor}, a qual não reflete as alterações nas variáveis utilizadas na chamada da função. Na linguagem C na verdade não existe nenhum mecanismo específico só para manipular a passagem de parâmetro por referência. Para que seja possível alterar o valor de uma variável passada como parâmetro é necessário se utilizar de ponteiros. Em uma passagem por referência, é necessário que a variável de parâmetro seja, como o próprio nome da passagem sugere, uma referência para variável passada como parâmetro. Foi visto que ponteiros são capazes de apontar para variáveis e, portanto, podem servir como referência para uma variável. Para entender como o processor funciona, analise novamente o diagrama do vetor que representa a memória. \begin{lstlisting} int A, *P; A = 31; \end{lstlisting} \begin{tabular}{cllllllllllll} & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & ... & N \\ \cline{2-13} {\bf M} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf 31}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf ??}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} \\ \cline{2-13} \multicolumn{1}{l}{} & & & & & & & {\bf A} & & & {\bf P} & & \end{tabular} O diagrama acima apresenta a variável {\it A} e um ponteiro {\it P}. O objetivo é utilizar o ponteiro {\it P} para alterar o valor de {\it A}, ou seja, transformar {\it P} em uma referência para {\it A}. Para tanto, basta examinar a seção {\bf ??}. A seguinte linha de código faz com que {\it P} aponte para {\it A}: \begin{lstlisting} P = &A; \end{lstlisting} Desse modo, é possível alterar o valor de {\it A} através de {\it P}. *P = 5; $\leftarrow$ {\it Equivalente a M[P]=5.} Ao invés de declarar {\it P} como uma variável ordinária, é possível declarar uma função com o seguinte protótipo: \begin{lstlisting} tipo_retorno func(int *P); \end{lstlisting} \begin{tabular}{cllllllllllll} & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & ... & N \\ \cline{2-13} {\bf M} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf 31}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf 6}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} \\ \cline{2-13} \multicolumn{1}{l}{} & & & & & & & {\bf A} & & & {\bf P} & & \end{tabular} Dessa maneira, o que temos na verdade é uma função tal que um de seus parâmetros é um ponteiro. Como já foi visto, para fazer com que este ponteiro aponte para uma variável qualquer, é necessário atribuir a ele o endereço da variável. Portanto, para que {\it P} seja realmente uma referência para a variável que será passada como parâmetro, a chamada da função {\it func} deverá ser da seguinte forma: \begin{lstlisting} func(&A); \end{lstlisting} Ou seja, o que está acontecendo na realidade não é exatamente uma passagem por referência no sentido habitual do termo, mas a passagem do endereço de uma variável para que um ponteiro possa apontá-la e, por sua vez, através desse ponteiro a função será capaz de alterar o valor apontado pelo ponteiro. \subsection{Exercício} Escreva como seria o protótipo de uma função que receberá como um de seus parâmetros um ponteiro {\it P}, sendo que a função deverá ser capaz de alterar o ponteiro. Escreva também qual seria a linha de código para alterar o valor do ponteiro, para que ele aponte para uma variável local da função {\it A}. Que problemas esse tipo atribuição poderia causar? \section{Aritmética de ponteiros} É interessante observar que, embora ponteiros sejam considerados um tipo especial de dados, eles ainda são números e, como tal, faz sentido realizar algumas operações matemáticas sobre eles. A aritmética de ponteiros é restrita apenas a soma, subtração e comparações. O que significa adicionar um número a um ponteiro? Para compreender qual o efeito de uma soma ou subtração sobre um ponteiro, é interessante recorrer novamente ao diagrama da memória. Suponha as seguintes declarações: \begin{lstlisting} int vet[2] = f9, 31g; int *P; p = &vet[0]; // faz P apontar para a primeira celula de ver \end{lstlisting} \begin{tabular}{cllllllllllll} & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & ... & N \\ \cline{2-13} {\bf M} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf 9}} & \multicolumn{1}{l|}{{\bf 31}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf 2}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} \\ \cline{2-13} \multicolumn{1}{l}{} & & & \multicolumn{1}{c}{{\bf vet{[}0{]}}} & \multicolumn{1}{c}{{\bf vet{[}1{]}}} & & & & & & {\bf P} & & \end{tabular} Nesse caso, o conteúdo de {\it *P} é {\bf 9}. Se for realizada uma adição sobre {\it P}: \begin{lstlisting} P++; \end{lstlisting} Teremos: \begin{tabular}{cllllllllllll} & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & ... & N \\ \cline{2-13} {\bf M} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf 9}} & \multicolumn{1}{l|}{{\bf 31}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf 3}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} \\ \cline{2-13} \multicolumn{1}{l}{} & & & \multicolumn{1}{c}{{\bf vet{[}0{]}}} & \multicolumn{1}{c}{{\bf vet{[}1{]}}} & & & & & & {\bf P} & & \end{tabular} Agora, o conteúdo de *P é 31. Ou seja, realizar somas ou subtrações sobre ponteiros é nada mais do que fazer com que o ponteiro aponte para uma nova posição da memória. Observe o seguinte exemplo: \lstinputlisting{exemplos/ponteiro2.c} A saída desse programa será: \begin{verbatim} *p = A *(p+1) = r *(p+5) = é *(p+7) = i \end{verbatim} \subsection{Exercício} Observe pelos exemplos, que a aritmética de ponteiros cria uma forte relação entre ponteiros e vetores. Somar valores a um ponteiro, faz o ponteiro apontar para uma próxima célula de um vetor. Escreva um programa que obtenha como entrada do teclado uma palavra (que será armazenada em uma string) e imprima a string de trás para a frente. \section{Ponteiros e matrizes} Mais do que apenas uma relação, para a linguagem C matrizes são, na realidade, ponteiros para a primeira posição da matriz na memória. É esta a razão pela qual matrizes sempre são passadas por referência para funções. A única diferença entre uma matriz comum e um ponteiro, além da declaração, é que uma matriz é um tipo de dado cujo espaço é alocado no momento em que ela é declarada e seu ponteiro não pode ser alterado, assim como para qualquer variável não se pode alterar o seu próprio endereço, mas apenas seu conteúdo. Suponha a seguinte declaração: \begin{lstlisting} char str[5] = "sapo"; \end{lstlisting} A aparência dessa declaração na memória é a seguinte: \begin{tabular}{lllllllllllll} \multicolumn{1}{c}{} & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & ... & N \\ \cline{2-13} \multicolumn{1}{l|}{M} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf s}} & \multicolumn{1}{l|}{{\bf a}} & \multicolumn{1}{l|}{{\bf p}} & \multicolumn{1}{l|}{{\bf o}} & \multicolumn{1}{l|}{{\bf \0}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf }} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} \\ \cline{2-13} & & & \multicolumn{1}{c}{str{[}0{]}} & \multicolumn{1}{c}{str{[}1{]}} & \multicolumn{1}{c}{str{[}2{]}} & \multicolumn{1}{c}{str{[}3{]}} & \multicolumn{1}{c}{str{[}4{]}} & & & {\bf } & & \\ \cline{4-8} & & & \multicolumn{5}{c}{{\bf str}} & & & & & \end{tabular} Note que a variável {\it str} sem indexação é na realidade um ponteiro para a posição 2 da memória. Se a seguinte declaração é realizada: \begin{lstlisting} char str[5] = "sapo"; char *pon = str; \end{lstlisting} O efeito será: \begin{tabular}{lllllllllllll} \multicolumn{1}{c}{} & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & ... & N \\ \cline{2-13} \multicolumn{1}{l|}{M} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf s}} & \multicolumn{1}{l|}{{\bf a}} & \multicolumn{1}{l|}{{\bf p}} & \multicolumn{1}{l|}{{\bf o}} & \multicolumn{1}{l|}{{\bf \0}} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{{\bf 2}} & \multicolumn{1}{l|}{{\bf }} & \multicolumn{1}{l|}{} & \multicolumn{1}{l|}{} \\ \cline{2-13} & & & \multicolumn{1}{c}{str{[}0{]}} & \multicolumn{1}{c}{str{[}1{]}} & \multicolumn{1}{c}{str{[}2{]}} & \multicolumn{1}{c}{str{[}3{]}} & \multicolumn{1}{c}{str{[}4{]}} & & {\bf pon} & {\bf } & & \\ \cline{4-8} & & & \multicolumn{5}{c}{{\bf str}} & & & & & \end{tabular} A partir do momento que pon está apontando para o vetor {\it str}, este ponteiro poderá ser utilizado como se fosse o próprio vetor {\it str}. Por exemplo: \begin{verbatim} {\tt pon[3]} acessa o {\bf 3}º elemento de {\it str} (letra o). \end{verbatim} Na linguagem C todo ponteiro pode ser indexado dessa maneira, mesmo que a posição indexada não ''exista" (por exemplo, {\it pon[5]} ). Naturalmente, uma tentativa de acessar posições não alocadas, quase sempre decorrem em erros (dependendo do sistema operacional, o erro não é muito aparente). Na verdade, ao passar uma matriz para uma função, todo o processo acima ocorre naturalmente. É interessante observar que para funções receberem matriz bidimensionais é necessário que seu parâmetro seja um ponteiro para ponteiro. Por exemplo: \begin{lstlisting} tipo_retorno func(int **matriz); \end{lstlisting} \subsection{Exercício} Escreva uma função que receba duas matrizes 3x3, compute a soma dessas matrizes e imprima a resposta na tela. \section{Ponteiros para funções} Ponteiros para funções são tipos especiais de ponteiros. Em vez de armazenarem o endereço de uma área de dados, armazenam o endereço de uma função, ou seja, o endereço de memória para o qual o controle é transferido quando a função é chamada. O quadro a seguir mostra o modelo geral de declaração de ponteiros para funções e alguns exemplos: \begin{lstlisting} tipo_retorno (*nome_ponteiro) (!parâmetros?); void (*v fptr) (); int (*i fptr) (); double (*div fptr) (int, int); struct registro* (*reg fptr) (char*, char*, int, char*); \end{lstlisting} Nos exemplos acima, {\it v_ptr} é um ponteiro para qualquer função que não recebe argumentos nem retorna nada. {\it reg_fptr} é um ponteiro para uma função que receba como parâmetro 3 strings e um inteiro, ê retorne um ponteiro para uma struct registro. Exemplo de utilização: \begin{lstlisting} double f (double x) { // ... } double g (double x) { // ... } main () { // ... double (*fp) (double); double x, y; // ... if (x != 0) fp = f; else fp = g; // ... y = (*fp) (x); // ... } \end{lstlisting} \section{Problemas com ponteiros} Problemas com ponteiros são relativamente comuns e geralmente é muito de difícil de encontrar os erros relativos ao ponteiro. O erro mais comum é a tentativa de usar um ponteiro que não esteja apontado para um posição válida de memória. Dependendo do sistema operacional, o problema não é imediatamente acusado, mas tende a ter dimensões catastróficas. Imagine, por exemplo, que a posição para a qual o ponteiro aponta é uma região da memória utilizada pelo sistema operacional. Se o sistema operacional permitir a escrita nesta posição, o sistema pode até mesmo travar. Exemplo: \begin{lstlisting} int main(void) { int x, *p; x = 10; (*p) = x; // Em que endereço estamos armazenando x? return 0; } \end{lstlisting} Atribui o valor 10 a alguma localização desconhecida da memória. \end{document}