Ponteiros
e
Alocação Dinâmica de Memória
Programação de Computadores I
Natália Batista
https://sites.google.com/site/nataliacefetmg/
nataliabatista@decom.cefetmg.br
1. Memória
A memória RAM de qualquer computador é uma
sequência de bytes.
Cada byte armazena um de 256 possíveis
valores.
Cada objeto na memória ocupa um certo número
de bytes consecutivos. Exemplos:
Um char ocupa 1 byte.
Um int ocupa 4 bytes
Um double ocupa 8 bytes.
1. Memória
O operador
sizeof (
tipo
)
retorna o tamanho em bytes ocupado
em memória pelo tipo de dado passado como parâmetro.
Exemplo:
sizeof(int)
=> 4 bytes
sizeof(char)
=> 1 byte
sizeof(struct ponto)
=> 8 bytes
Os tamanhos
dependem da
arquitetura da
máquina.
2. Endereços
Os
bytes
da
memória
são
numerados
sequencialmente e o número de um byte é o seu
endereço
(=
address
).
Cada objeto na memória tem um endereço.
Geralmente o endereço de um objeto é o
endereço do seu primeiro byte.
O endereço é dado pelo operador
&
. Se
i
é uma
variável então:
&
i
é o seu endereço.
2. Endereços
Por exemplo, depois das declarações
os endereços das variáveis poderiam ser os seguintes:
char c;
int i;
struct {
int x, y;
} ponto;
int v[4];
c 89421
i 89422
ponto 89426
v[0] 89434
v[1] 89438
v[2] 89442
v[3] 89446
2. Endereços
No exemplo anterior:
&
i vale 89422.
&
v[3] vale 89446.
Outro exemplo: o segundo argumento da função
scanf
é o
endereço da variável onde deve ser depositado o objeto lido da
entrada:
int i;
scanf ("%d",
&
i);
c 89421
i 89422
ponto 89426
v[0] 89434
v[1] 89438
v[2] 89442
v[3] 89446
3. Ponteiros
Um ponteiro (= apontador =
pointer
) é um tipo
especial de variável que armazena endereços.
Exemplo:
int a;
int b;
int c;
int *ptr; //declara um ponteiro para um inteiro
a = 90;
b = 2;
c = 3;
ptr = &a;
3. Ponteiros
Há vários tipos de ponteiros:
ponteiros para bytes,
ponteiros para inteiros,
ponteiros para ponteiros para inteiros,
ponteiros para registros, etc.
Para declarar um ponteiro p para um inteiro:
int *p;
Para declarar um ponteiro p para um registro reg:
struct reg *p;
Para declarar um ponteiro r para um ponteiro que apontará um inteiro:
int **r;
3. Ponteiros
Se um ponteiro p armazena o endereço de uma
variável i, podemos dizer:
“p aponta para i" ou "p é o endereço de i".
Fonte: http://www.inf.pucrs.br/~pinho/PRGSWB/Ponteiros/ponteiros.html
Uma variável do tipo
t*
contém o
endereço de um objeto do tipo
t
.
3. Ponteiros
Operador de
derreferência
: *
acessa o conteúdo do endereço apontado
operador unário
Por exemplo, se i é uma variável e p vale &i
então dizer "*p" é o mesmo que dizer "i".
p
i
3. Ponteiros
Exercício: qual é o valor de
a
b
&a
&b
*a
Fonte: slides de aula da Prof.ª Cristina Murta.
3. Ponteiros
Exercício: suponha que a, b e c são variáveis
inteiras. O que o seguinte trecho de código faz?
int a, b, c;
int *p; // p é um ponteiro para um inteiro
int *q;
p = &a; // o valor de p é o endereço de a
q = &b; // q aponta para b
3. Ponteiros
Um ponteiro pode ter o valor especial
NULL
que não é endereço de lugar algum.
A macro NULL está definida na biblioteca
stdlib.h.
Seu valor é 0 na maioria dos computadores.
3. Ponteiros
Impressão de ponteiros:
Em C, pode-se imprimir o valor armazenado no
ponteiro (um endereço), usando-se a função
printf
com o formatador
%p
na string de formato.
Por exemplo:
Fonte: http://www.inf.pucrs.br/~pinho/PRGSWB/Ponteiros/ponteiros.html
#include <stdio.h>
int main(){
int x;
int *ptr;
ptr = &x;
printf("O endereço de X é: %p\n", ptr);
return 0;
4. Ponteiros em funções
Suponha que precisamos de uma função que troque os
valores de duas variáveis inteiras, digamos i e j.
A função
não produz o efeito desejado, pois recebe apenas cópias dos
valores das variáveis.
A função troca os valores dessas cópias, enquanto as
variáveis "originais" permanecem inalteradas.
4. Ponteiros em funções
Para obter o efeito desejado, é preciso passar à função os
endereços das variáveis:
5. Vetores e ponteiros
Existe uma relação intrínseca entre vetores e apontadores.
Exemplo:
int a[10];
define um vetor
a
de tamanho 10, que é alocado em um bloco contíguo de
memória de tamanho suficiente para conter os 10 objetos:
a[0], a[1], a[2], ..., a[9]
O nome do vetor é o endereço do seu primeiro elemento.
int *pa; //apontador para inteiro
pa = a;
pa = &a[0];
5. Vetores e ponteiros
Fonte: slides de aula da Prof.ª Cristina Murta.
Aritmética de ponteiros: exemplo
int a[10], i;
int *pa;
pa = a;
for(i=0; i<10; i++){
*(pa+i) = i;
}
int x = *pa; // copia a[0] em x
int y = *(pa+1); // copia a[1] em x
pa++
=> aponta para o próximo elemento
pa--
=> aponta para o anterior
(pa+i) - refere-se ao endereço de a[i]
*(pa+i) - refere-se ao valor armazenado em a[i]
6. Alocação dinâmica de memória
A memória alocada a cada programa durante sua
execução é dividida em quatro áreas:
Instruções
área que armazena o código C compilado e montado
em linguagem de máquina
Memória estática
onde são criadas as variáveis globais e locais estáticas
Pilha
área em que são executadas as funções e criadas as
variáveis locais
Memória dinâmica: Heap
destinada a armazenar dados alocados
dinamicamente
Dados estáticos
Dados estáticos
Dados
dinâmicos
(Heap)
Dados
dinâmicos
(Heap)
Pilha
Pilha
Instruções
Instruções
memória
6. Alocação dinâmica de memória
Linguagens de programação como C, C++ e Pascal
permitem dois tipos de alocação de memória:
estática e dinâmica.
Na
alocação estática
, o espaço para as variáveis é
reservado no início da execução, não podendo ser
alterado depois. Exemplo:
int a; int b[20];
Na
alocação dinâmica
, o espaço para as variáveis
pode ser alocado dinamicamente durante a
execução do programa.
6. Alocação dinâmica de memória
A memória alocada dinamicamente é acessada por
meio de
Apontadores
(
ponteiros
).
Essa memória faz parte de uma área de memória
chamada
heap.
Basicamente, o programa aloca e desaloca
porções de memória do
heap
durante a
execução.
6. Alocação dinâmica de memória
6. Alocação dinâmica de memória
A alocação dinâmica de memória permite reservar
espaços de memória de tamanho arbitrário durante a
execução do programa e acessá-los através de
apontadores.
A alocação e liberação desses espaços de memória é
feita por funções da biblioteca
stdlib.h
:
malloc() e calloc()
: alocam espaço de
memória.
realloc()
:
realoca um espaço de memória.
free()
: libera espaço de memória.
6. Alocação dinâmica de memória
Exemplos:
Alocação de um inteiro.
Alocação de um vetor com 100 caracteres.
Fonte: slides de aula da Prof.ª Cristina Murta..
int *ptr;
ptr = (int*) malloc(sizeof(int));
char *ptr;
6. Alocação dinâmica de memória
Alocação dinâmica de
matriz
: são implementadas como
vetores de vetores.
Uma matriz com m linhas e n colunas é um vetor cujos m
elementos são vetores de n elementos.
O elemento de M que está no cruzamento da linha i com
a coluna j é denotado por M[i][j].
int **M;
M = (int **) malloc (m * sizeof (int *));
for (int i = 0; i < m; ++i)
6. Alocação dinâmica de memória
Teste de Alocação:
memória não é infinita!
Para verificar se foi possível alcar a memória solicitada,
pode-se testar o valor do ponteiro usado para armazenar a
área alocada.
Se o valor deste ponteiro for NULL, não foi possível alocar
a memória.
Fonte: http://www.inf.pucrs.br/~pinho/PRGSWB/Ponteiros/ponteiros.html
int *ptr;
ptr = (int*) malloc(sizeof(int)*10);
if (ptr == NULL){
6. Alocação dinâmica de memória
Liberação de memória:
Todo o espaço alocado ao programa deve ser
retornado ao sistema operacional quando não for
mais necessário ao programa.
Função free(ptr);
Libera o uso de um bloco de memória, permitindo que
este espaço seja reaproveitado
6. Alocação dinâmica de memória
Exemplo:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *notas, numero, i;
printf("Entre com o número total de alunos: \n");
scanf("%d", &numero);
notas = (int *)
malloc
(numero * sizeof(int));
for (i = 0; i < numero; i++) {
printf("Digite a nota do aluno %d: ", i+1);
scanf("%d", notas+i);
printf("A nota do aluno %d é %d.\n" , i+1, notas[i]);
}
free
(notas);
}
6. Alocação dinâmica de memória
Mais um exemplo:
Fonte: slides de aula da Prof. David Menotti.
int *a, b;
b = 10;
a
b
Heap
Alocação
Estática
6. Alocação dinâmica de memória
Fonte: slides de aula da Prof. David Menotti.
int *a, b;
b = 10;
a = (int *) malloc(sizeof(int));
*a = 20;
a
20
b
Heap
Alocação
Estática
6. Alocação dinâmica de memória
Qual é o valor de b no final?
Fonte: adaptado dos slides de aula da Prof.ª Jussara Almeida..
int *a, b, *c;
b = 10;
a = (int *) malloc(sizeof(int));
*a = 20;
c = a;
free(a);
a = &b;
*a = 30;
a
20
b
Heap
Alocação
Estática
30
X
c
Memória foi desalocada.
O endereço contido no
7. Resumo: notação em C
Fonte: slides de aula da Prof.ª Jussara Almeida..