Sei sulla pagina 1di 7

LINGUAGEM E TÉCNICA DE PROGRAMAÇÃO I

Profa. Gisele Busichia Baioco


gisele@ceset.unicamp.br

Algoritmos Estruturados e
Linguagem de Programação Estruturada

Ponteiros em C
Parte I – Conceitos Básicos
1Introdução
Uma das mais poderosas características oferecidas pela linguagem C é o uso de
ponteiros. Ponteiros proporcionam o acesso a variáveis sem referenciá-las diretamente, ou
seja, por meio do endereço de memória das variáveis.
Algumas razões para o uso de ponteiros:
• fornecem maneiras pelas quais as funções em C podem modificar os argumentos que
recebem;
• usá-los no lugar de vetores, matrizes e strings para aumentar a efeciência;
• para alocação dinâmica de memória;
• para criar estruturas de dados complexas, como listas encadeadas, pilhas, árvores, etc.
Além dos ponteiros serem uma das características mais fortes de C, também é a mais
perigosa. O uso incorreto de ponteiros provoca desde erros difíceis de serem encontrados
até uma queda no sistema.

2O que são Ponteiros?


Uma variável do tipo int guarda números inteiros. As variáveis do tipo float guardam
números de ponto flutuante. Uma variável do tipo char guarda caracteres. Um ponteiro é
uma variável que guarda endereços de memória. Ou seja, uma variável do tipo ponteiro
contém o endereço de memória de uma outra variável e pode-se dizer que a primeira variável
aponta para a segunda.
Esquematicamente tem-se:

Memória do Computador
Endereço Conteúdo
1000 1003
1001
1002
1003
1004
1005
1006
... ...

1
Assim, usado um ponteiro é possível acessar o conteúdo de memória de uma variável
sem referenciá-la diretamente, o que é conhecido como endereçamento indireto.
Além do endereçamento indireto, um ponteiro em C também pode ser utilizado em um
programa para alocar memória em tempo de execução, o que é conhecido como alocação
dinâmica de memória.
Essas duas possibilidades de utilização de ponteiros serão abordadas com detalhes no
decorrer deste texto.

3Declarando e Utilizando Variáveis Ponteiro


Assim como todas as variáveis, uma variável ponteiro também tem tipo. Em C,
quando se declara uma variável ponteiro, deve-se informar para que tipo de variável irá
apontar. Por exemplo, um ponteiro int aponta para uma variável do tipo inteiro, isto é, guarda
o endereço de um inteiro.
Sintaxe de declaração de variáveis ponteiro em C:

tipo-de-dado *nome;

onde:
tipo-de-dado: é um tipo de dado válido em C;
nome: é um identificador válido para a variável ponteiro. É o asterisco (*) que faz o
compilador saber que a variável não vai armazenar um valor, mas sim um endereço de
memória que contenha um valor do tipo especificado.

Exemplos de declarações:

int *p; /* declara um ponteiro para o tipo inteiro */


char *temp, *p2; /* declara dois ponteiros para o tipo caractere */

Os ponteiros p, temp e p2 ainda não foram inicializados (como toda variável do C que
é apenas declarada). Isso significa que eles apontam para um lugar indefinido da memória.
Esse lugar pode estar, por exemplo, na porção da memória reservada ao sistema operacional
do computador. Usar o ponteiro nessas circunstâncias pode levar a um travamento do
computador. Desse modo, um ponteiro deve ser inicializado (apontado para algum lugar
conhecido) antes de ser usado.
Para atribuir um valor a um ponteiro recém-criado a fim de inicializá-lo, bastaria
igualá-lo a um valor de memória. Mas, como saber a posição na memória de uma variável do
programa? Seria muito difícil saber o endereço de cada variável usada em um programa,
mesmo porque esses endereços são determinados pelo compilador (em tempo de compilação)
e realocados na execução. Pode-se então deixar que o compilador faça esse trabalho. Para
saber o endereço de uma variável basta usar o operador &. Por exemplo:
int cont;
int *p;
cont = 10;
p = &cont; /* obtém o endereço de cont e atribui para p */

Nesse exemplo declarou-se uma variável do tipo inteiro cont e uma variável ponteiro
para inteiro p. Em seguida atribuiu-se o valor 10 para cont. A expressão &cont fornece o
endereço da variável cont, o qual é armazenado em p. Nesse momento, p está inicializado e
pode, então, ser utilizado.

2
Deve-se observar no exemplo anterior que o valor de cont não foi alterado, ou seja,
cont continua com o conteúdo 10. Pode-se, então, alterar o valor de cont usando p. Para tanto
usa-se o operador *.
Considerando o exemplo anterior, no momento em que se executa a instrução:
p = &cont;

A expressão *p passa a ser equivalente à própria variável cont. Isso significa que,
pode-se alterar o valor de cont para 12, basta fazer:

*p = 12;

Observação: O símbolo utilizado para multiplicação e o símbolo que obtém o valor


armazenado no endereço referenciado por ponteiros são iguais (*). Apesar disso, esses
operadores não tem nenhuma relação entre si. Os operadores de ponteiros & e * têm
precedência mais alta que os operadores aritméticos, exceto o menos unário, com o qual têm a
mesma precedência.
Exemplos do uso de variáveis ponteiro:

#include <stdio.h>

main()
{
int num, valor;
int *p;
num = 55;
p = &num; /* Obtém o endereço de num */
valor = *p; /* a variável valor recebe o mesmo valor de num de
maneira indireta */
printf("Valor = %d\n", valor);
printf("Endereco para onde o ponteiro aponta: %p\n", p);
printf("Valor da variavel apontada: %d\n", *p);
}

Deve-se observar que o código %p usado na função printf() indica que deve imprimir
um endereço (em hexadecimal).
#include <stdio.h>

main()
{
int num,*p;
num = 55;
p = &num; /* Obtém o endereço de num */
printf("Valor inicial: %d\n", num);
*p = 100; /* Altera o valor de num de maneira indireta */
printf("Valor final: %d\n", num);
}

4Expressões com Ponteiros


Em geral, expressões que envolvem ponteiros obedecem as mesmas regras das
expressões em C.

3
4.1Atribuições com Ponteiros
Considerando dois ponteiros p1 e p2 pode-se atribuir o conteúdo de p1 a p2 fazendo
p1 = p2. A atribuição p1 = p2 faz com que p1 aponte para o mesmo endereço que p2. Deve-
se observar que se fosse necessário fazer com que a variável apontada por p1 tenha o mesmo
conteúdo da variável apontada por p2 o comando seria *p1=*p2.
Por exemplo, o programa seguinte exibe o endereço de memória de x (em
hexadecimal) por meio do ponteiro p2:
#include <stdio.h>

main()
{
int x;
int *p1, *p2;
p1 = &x;
p2 = p1; /* atribui p1 para p2 */
printf("%p %p %p",&x, p1, p2); /* imprime 3 vezes o endereço
de memória de x */
}

4.2Aritmética com Ponteiros


Em C, pode-se fazer apenas duas operações aritméticas em ponteiros: incremento (+) e
decremento (-).
Cada vez que o computador incrementa (ou decrementa) um ponteiro, ele aponta para
o endereço de memória do próximo elemento (ou elemento anterior) de seu tipo. Por exemplo,
quando o computador incrementar um ponteiro do tipo char, seu endereço será aumentado
em 1 (o tipo char ocupa 1 byte na memória); porém, quando o computador incrementar um
ponteiro do tipo int, seu endereço será aumentado em 2 (o tipo int ocupa 2 bytes de
memória).
Esquematicamente tem-se:

char *c = 3000;
Memória do Computador
Endereço Conteúdo
c 3000
c+1 3001
c+2 3002
c+3 3003
c+4 3004
c+5 3005
... ...

int *i = 3000;
Memória do Computador
Endereço Conteúdo
3000
i
3001
3002
i+1
3003
3004
i+2
3005

4
... ...

5
Supondo que p é um ponteiro, as operações são escritas como:

p++; /* ou p = p + 1 */
p--; /* ou p = p – 1 */
p = p + 5; /* ou p += 5 */

Mais uma vez vale lembrar que as operações são com os ponteiros e não com o
conteúdo das variáveis para as quais eles apontam. Por exemplo, para incrementar o conteúdo
da variável apontada pelo ponteiro p, faz-se:

(*p)++; /* ou *p += 1 ou *p = *p + 1 */

Caso seja necessário usar o conteúdo do ponteiro 15 posições adiante, faz-se:

*(p+15)

Exemplos de operações válidas com ponteiros em C:

1) se p aponta para a variável x do tipo inteiro, então *p pode ocorrer em qualquer


contexto em que x possa ocorrer, por exemplo:
y = *p + 1; /* atribui a y o valor de x e soma um */
d = sqrt((double)*p); /* atribui a d a raiz quadrada de x,
o qual é convertido em um double
antes de ser usado por sqrt */

2) referências a ponteiros podem ocorrer do lado esquerdo de atribuições. Por


exemplo, tendo um ponteiro p que aponta a variável x do tipo inteiro, então:
p = &x; /* p aponta para x */
*p = 0; /* atribui 0 a x */
(*p)++ /* incrementa o valor de x */

Entretanto, existem operações que não podem ser realizadas com ponteiros. Por
exemplo, não se pode dividir ou multiplicar ponteiros, adicionar dois ponteiros, adicionar ou
subtrair valores do tipo float e double de ponteiros.

4.3Comparações entre ponteiros


Pode-se comparar dois ponteiros usando operadores relacionais (<, <=, >, >=, ==, !=).
Mas que informação é obtida quando compara-se dois ponteiros? Bem, em primeiro
lugar, pode-se saber se dois ponteiros são iguais ou diferentes (== e !=), ou seja, se apontam
ou não para o mesmo endereço de memória. No caso de operações do tipo <, <=, >, >=
compara-se qual ponteiro aponta para uma posição mais alta (ou mais baixa) na memória.
A comparação entre dois ponteiros se escreve como a comparação entre outras duas
variáveis quaisquer, por exemplo:

p1 > p2

6
5Exercícios de Fixação
1) Explique a diferença entre:
p++; (*p)++; *(p++);

2) Explique o significado de:


*(p+10)

3) Explique o que você entendeu sobre comparação entre ponteiros.

4) Qual o valor de y no final do programa? Tente primeiro descobrir e depois verifique no


computador o resultado.

#include <stdio.h>

main()
{
int y, *p, x;
y = 0;
p = &y;
x = *p;
x = 4;
(*p)++;
x--;
(*p) += x;
printf ("y = %d\n", y);
}

Potrebbero piacerti anche