Buscar

01 Ponteiros em C Parte I - Conceitos Básicos

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você viu 3, do total de 6 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você viu 6, do total de 6 páginas

Prévia do material em texto

1 
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 
1 Introduçã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. 
2 O 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 
... ... 
 
 2 
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. 
3 Declarando 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. 
 3 
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); 
} 
4 Expressões com Ponteiros 
Em geral, expressões que envolvem ponteiros obedecem as mesmas regras das 
expressões em C. 
 
 4 
4.1 Atribuiçõ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.2 Aritmé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 
i 
3000 
3001 
i + 1 
3002 
3003 
i + 2 
3004 
3005 
 ... ... 
 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 queas 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.3 Comparaçõ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 
5 Exercí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); 
}

Outros materiais