Ponteiros e Alocação Dinâmica de Memória

34 

Full text

(1)

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

(2)

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.

(3)

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.

(4)

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.

(5)

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

(6)

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

(7)

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;

(8)

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;

(9)

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

.

(10)

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

(11)

3. Ponteiros

Exercício: qual é o valor de

a

b

&a

&b

*a

Fonte: slides de aula da Prof.ª Cristina Murta.

(12)

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

(13)

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.

(14)

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;

(15)

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.

(16)

4. Ponteiros em funções

Para obter o efeito desejado, é preciso passar à função os

endereços das variáveis:

(17)

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];

(18)

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]

(19)

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

(20)

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.

(21)

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.

(22)

6. Alocação dinâmica de memória

(23)

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.

(24)

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;

(25)

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)

(26)

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){

(27)

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

(28)

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);

}

(29)

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

(30)

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

(31)

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

(32)

7. Resumo: notação em C

Fonte: slides de aula da Prof.ª Jussara Almeida..

Definição de p como um apontador para uma variável do tipo T

T *p;

Alocação de memória para uma variável apontada por p

p = (T*) malloc(sizeof(T));

Desalocação/Liberação de memória

free(p);

Conteudo da variável apontada por p

*p;

Valor nulo para um apontador

NULL;

Endereço de uma variável a

(33)

8. Erros comuns

Esquecer de alocar memória e tentar acessar o

conteúdo da variável.

Copiar o valor do apontador ao invés do valor da

variável apontada.

Esquecer de desalocar memória.

Ela é desalocada ao fim do programa ou procedimento

função onde a variável está declarada, mas pode ser

um problema em loops.

Tentar acessar o conteúdo da variável depois de

desalocá-la.

(34)

9. Atividades sugeridas

Leitura e exercícios do livro-texto (Damas):

Figure

Updating...

References

Updating...

Download now (34 página)