PONTEIROS
Introdução
Para armazenar dados no computador um programa gerencia:
Onde a informação esta armazenada
Que tipo de informação é armazenada
Que valor é mantido lá
00001100
00110110 Endereços de Memória
3126 (short)
Introdução
A declaração de uma variável num programa realiza estes passos necessários para o
armazenamento de dados
short total; // declaração de variável
total = 3126; // atribuição de valor
00001100 00110110
3126 = total
0xCB20 0xCB21 0xCB22 0xCB23 0xCB24 0xCB25 = total 0xCB20 0xCB21 0xCB22 0xCB23 0xCB24 0xCB25 short
Endereços de Variáveis
Todo nome de variável esta associado a um endereço na memória
O operador de endereço & pode ser usado para obter a localização de uma variável
int total; // declaração de variável
total = 3126; // atribuição de valor
printf %d , total ; // valor da variável
Endereços de Variáveis
#include <stdio.h>
#include <stdlib.h>
int main() {
int copos = 6;
double cafe = 4.5;
printf("Valor de copos = %d , copos);
printf( endereço de copos = %p , &copos);
printf("Valor de cafe = %f , café); printf("Endereço de cafe = %p , &café);
system("pause");
Ponteiros
Existe outra estratégia para armazenar dados no computador:
Alocar memória manualmente
Guardar o endereço de memória em um ponteiro
Usar o ponteiro para acessar e modificar os dados
Ponteiros
Um ponteiro é um tipo especial que guarda valores que são endereços de memória
Da mesma forma que uma variável char tem um caractere como valor e um int tem um
número como valor
2.6
0x27FCF8
120
G ch
num ptr mult 0x27FCF8 0x27FCF9 0x27FCFD 0x27FD01
char ch = 'G';
int num = 120;
float mult = 2.6;
char * ptr = (char *) 0x27FCF8;
0x27FD05
Ponteiros
A declaração de um ponteiro segue o seguinte padrão:
char * ptr;
O tipo do elemento apontado Nome do ponteiro
Ponteiros
Como o ponteiro contém um endereço de memória, diz-se que ele aponta para aquela posição de memória
2.6
0x27FCF8
120
G ch
num ptr mult 0x27FCF8 0x27FCF9 0x27FCFD 0x27FD01 0x27FD05 0x27FD09
char ch = 'G';
int num = 120;
float mult = 2.6;
Ponteiros
O nome de um ponteiro
representa uma localização na memória
O operador de indireção * pode ser usado para obter o valor armazenado na memória
// declaração do ponteiro
char * ptr = (char *) 0x27FCF8;
ptr; // endereço armazenado
*ptr; // valor apontado
2.6
0x27FCF8
120
G ch
Ponteiros
#include <stdio.h>
#include <stdlib.h>
int main() {
int total = 6; // declara uma variável
int * pt; // declara um ponteiro
pt = &total; // atribui endereço de total
printf("Valor de total = %d" , total); printf("Valor de pt %d = , *pt);
printf("Endereço de total = %p , &total); printf("Endereço de pt = %p , pt);
*pt = *pt + 1; // altera valor
printf("Agora total vale = %d , total);
system("pause");
Variável
versus
Ponteiro
Ao usar uma variável:
O valor é um elemento que possui um nome
A localização do valor é um elemento derivado (&)
Ao usar um ponteiro:
A localização é um elemento que possui um nome
O valor é um elemento derivado (*)
int * pt = &total; // pt se refere ao endereço
*pt; // *pt se refere ao valor
int total = 6; // total se refere ao valor
Declaração de Ponteiros
Por que não se declara um ponteiro da mesma forma que um int, char ou float?
Não é suficiente dizer que uma variável é um ponteiro, é preciso também especificar para que tipo de dado ele aponta
char ch = 'G';
int num = 120;
float f = 2.1;
char * pc = &ch;
int * pi = #
float * pf = &f;
Declaração de Ponteiros
Na declaração de um ponteiro o uso de espaços ao redor do (*) é opcional
Cuidado com declarações múltiplas
int *ptr; // enfatiza que *ptr é um int
int* ptr; // enfatiza que ptr é um ponteiro para int
int * ptr; // estilo neutro
// p1 é um ponteiro para int, p2 é um int
int * p1, p2;
// p1 e p2 são ponteiros para int
Cuidado com Ponteiros
Ao declarar um ponteiro o computador não aloca automaticamente memória para
guardar o valor apontado
long * ptr; *ptr = 504;
ptr 0x27FCF8
504
ptr
float val;
float * ptr = &val; *ptr = 504;
Alocação de Memória
Ponteiros tem sido usados para guardar endereços de variáveis já existentes
Variáveis são memórias rotuladas durante o processo de compilação
Neste caso os ponteiros fornecem apenas uma
segunda forma de acesso as variáveis
O verdadeiro poder dos ponteiros está em
Alocação de Memória
A alocação de memória é feita com o
operador new
int * pn = new int;
Operador new
retorna o endereço da memória alocada Tipo de dado Ponteiro compatível com o
Alocação de Memória
#include <stdio.h>
#include <stdlib.h>
int main() {
int * pi = new int; // aloca memória para um inteiro
*pi = 1001; // guarda um valor lá
printf("Valor inteiro = %d \n , *pi); printf("Localização = %p \n\n , pi);
double * pd = new double; // aloca memória para um double
*pd = 500.35; // guarda um valor lá
printf("Valor do double = %lf , *pd); printf( Sua localização = %p \n\n , pd);
system("pause"); return 0;
Liberando Memória
Toda memória alocada com new deve ser liberada ao final do seu uso
O operador delete permite retornar a memória não mais usada para uso do sistema, ou de novas alocações
int * ps = new int; // aloca memória com new
... // usa memória
Liberando Memória
O operador delete libera a memória mas não destrói o ponteiro
O mesmo ponteiro pode ser usado para receber outro endereço de memória
int * ps;
ps = new int; // aloca memória com new
*ps = 30; printf( %d , *ps);
delete ps; // libera memória ao final
ps = new int; // aloca nova memória
*ps = 50;
printf( %d ,*ps);
Liberando Memória
Um uso de new deve ser sempre balanceado
com um uso de delete
Caso contrário tem-se um memory leak
int * ps = new int; // aloca memória com new
*ps = 30; printf( %d , *ps);
ps = new int; // memory leak
*ps = 50; printf( %d , *ps);
Liberando Memória
Não se pode liberar o mesmo bloco de memória duas vezes
int * ps = new int; // aloca memória com new
*ps = 30;
...
delete ps; // libera memória ao final
Liberando Memória
Não se pode usar delete para liberar memória criada com a declaração de variáveis
int val; // declaração de variável
val = 30;
...
Vetores Dinâmicos
Um vetor criado por uma declaração de variável é chamado de vetor estático
É preciso definir previamente o tamanho do vetor em tempo de compilação
Usando new é possível criar um vetor com tamanho definido durante a execução do programa
Vetores Dinâmicos
Para criar um vetor dinâmico com new basta passar o tipo e o número de elementos
O ponteiro recebe o endereço do primeiro elemento do vetor
int * vet = new int [20];
Operador new
Tipo de dado Ponteiro compatível com o
tipo de dado requisitado
Vetores Dinâmicos
Para liberar a memória de um vetor dinâmico
é preciso usar delete com uma notação especial
delete [] vet;
Operador delete Endereço da memória alocada
Vetores Dinâmicos
O ponteiro de um vetor dinâmico pode ser usado como se fosse um vetor
5 15
0x27FD09
0x27FD0D
30 0x27FD11
28 0x27FD15
pvet int * pvet = new int [5];
pvet[0] = 15; pvet[1] = 5; pvet[2] = 30; pvet[3] = 28; pvet[4] = 40;
40 0x27FD19 0x27FD1D 0x27FD01
0x27FD05
Vetores Dinâmicos
#include <stdio.h>
#include <stdlib.h>
int main() {
double * p3 = new double [3]; // memória para três doubles
p3[0] = 0.2; p3[1] = 0.5; p3[2] = 0.7;
printf("p3[1] = %lf \n\n", p3[1]);
p3 = p3 + 1; // incrementa o ponteiro
printf("Agora p3[0] = %lf", p3[0]); printf("Agora p3[1] = %lf , p3[1]); p3 = p3 - 1; // retorna ao inicio
delete [] p3; // libera a memória
system("pause"); return 0;
Vetores Dinâmicos
Note que um ponteiro é uma variável e seu conteúdo pode ser modificado através de uma atribuição
p3 = p3 + 1; // incrementa o ponteiro
Vetores Dinâmicos
Um ponteiro pode ser usado como um vetor
Um vetor pode ser usado como um ponteiro
int * pvet = new int [10]; pvet[0] = 15;
pvet[1] = pvet[0] + 5;
int vet[10];
*vet = 15; // vet[0] = 15;
Vetores Dinâmicos
Um vetor estático é um ponteiro constante
para o primeiro elemento do vetor
5 15
0x27FD09
0x27FD0D
30 0x27FD11
28 0x27FD15
vet
int vet[5]; vet[0] = 15; vet[1] = 5; vet[2] = 30; *(vet+3) = 28; *(vet+4) = 40;
vet = vet + 1; // inválido
40 0x27FD19 0x27FD1D 0x27FD01
0x27FD05
Registros Dinâmicos
O operador new também pode ser usado para criar registros dinâmicos
jogador * pj = new jogador;
Operador new
Tipo de dado Ponteiro compatível com o
tipo de dado requisitado
struct jogador {
Registros Dinâmicos
O operador membro (.) não pode ser usado
com um registro dinâmico
C oferece o operador (->) para acessar membros
de um registro dinâmico
Usa-se (.) com variáveis tipo registro
Usa-se (->) com ponteiros para registros
Registros Dinâmicos
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct jogador {
char nome[40]; float salario; unsigned gols; };
int main() {
jogador * pbeb = new jogador; strcpy(pbeb->nome, "Bebeto"); pbeb->salario = 200000;
pbeb->gols = 600;
printf("Contratação para o próximo ano: %s com salario R$ %f. \n , pbeb->nome, pbeb->salario);
delete pbeb;
system("pause"); return 0;
Registros Dinâmicos
Atribuição a um registro dinâmico:
jogador * prom = new jogador; strcpy(prom->nome, "Romario"); prom->salario = 300000;
Vetores Dinâmicos
O operador new também pode ser usado para criar vetores dinâmicos de registros
jogador * pt = new jogador [22];
Operador new
Tipo de dado Ponteiro compatível com o
tipo de dado requisitado
struct jogador {
char nome[40]; float salario; unsigned gols; };
Vetores Dinâmicos
O operador (.) deve ser usado com a notação de vetor
O operador (->) deve ser usado com a notação de ponteiro
printf( %s , pt->nome); // nome do primeiro jogador
printf( %f , (pt+1)->salario); // salario do segundo jogador
printf( %u , (pt+21)->gols); // gols do ultimo jogador
printf( %s , pt[0].nome); // nome do primeiro jogador
printf( %f , pt[1].salario); // salario do segundo jogador
Conclusão
Ponteiros são variáveis que armazenam endereços de memória
A sua principal função é guardar o endereço de memória alocada dinamicamente com o operador new
Permite alocar variáveis durante a execução
Permite criar vetores dinâmicos