Вы находитесь на странице: 1из 8

Programar em C++/Alocação dinâmica

de memória
< Programar em C++
Ir para: navegação, pesquisa
Esta página precisa ser reciclada (discuta).
Ao melhorá-la, você estará ajudando o Wikilivros.

Índice
 1 Alocação dinâmica de memória
 2 Operador new
 3 Operador Delete - Memory Leak
 4 Retornando um ponteiro para uma variável local
 5 Retornando um Ponteiro a uma Variável Local Estática
 6 Returning a Pointer to a Dynamically Created Variable
 7 Alocar dinamicamente Arrays
 8 Dangling Pointers
 9 Verificar a existência de memória para dinâmica

Alocação dinâmica de memória


O compilador reserva espaço na memória para todos os dados declarados
explicitamente, mas se usarmos ponteiros precisamos reservar o espaço necessário e
colocar o endereço inicial nos mesmos. Para isto, podemos usar o endereço de uma
variável definida previamente ou reservar o espaço necessário no momento que
precisemos. Este espaço que precisamos reservar em tempo de execução é chamada de
memória alocada dinamicamente.

Refere-se à possibilidade de termos o nosso programa a correr e o utilizador ter de


inserir dados e como tal não sabemos exactamente a quantidade de dados que o
utilizador vai colocar, portanto temos de arranjar uma memória que nos permita lidar
com esta indeterminação quanto à quantidade de dados inseridos.

Este é o caso em que não sabemos no momento da programação a quantidade de dados


que deverão ser inseridos mas o programa já está a correr. É tentar responder a
perguntas: quantas pessoas existem na tua turma? Quantas letras vamos escrever, etc.
Em vez de estarmos a prever um limite superior para abarcar todas as situações, temos
esta possibilidade do dinâmico. Além de que colocar no momento da programação cria
reserva de memória por isso, estaríamos a reservar memória para um limite que
possivelmente não iríamos ter necessidade. O exemplo típico disto é os processadores
de texto. em que não sabemos a quantidade de letras que o utilizador vai escrever.

Vamos voltar a uma ponta solta num dos capítulos anteriores, onde queríamos fazer
com que o utilizador dissesse quantos elementos do array é que se deveria utilizar. Já
dissemos antes que o declarador do nº de elementos do array tem de ser ou uma
constante ou um literal, mas não pode ser uma variável. Isso dá erro. Aqui vai o
exemplo desse erro:

#include <iostream>
using namespace std;
int main ()
{
int numTests;
cout << "Enter the number of test scores:";
cin >> numTests;
int testScore[numTests];
system ("pause");
return 0;
}

A razão da exigência de ter um constant (ou literal) é que vamos alocar memória para o
array na altura da compilação, e o compilador necessita de saber exactamente a
quantidade de memória que deve reservar… porém se a variável é o size declarator, o
compilador não sabe quanta memória deve reservar para alocar a variável, pois o seu
valor pode mudar.

Operador new
Reformulando o exemplo anterior agora com dados dinâmicos.

#include <iostream>
using namespace std;
int main ()
{
int numTests;
cout << "Enter the number of test scores:";
cin >> numTests;
int * iPtr = new int[numTests]; //colocamos um ponteiro
no inicio da memória dinâmica
for (int i = 0; i < numTests; i++)
{
cout << "Enter test score #" << i + 1 << " : ";
cin >> iPtr[i];
}
for (int i = 0; i < numTests; i++)
cout << "Test score #" << i + 1 << " is "<< iPtr[i] << endl;
delete [] iPtr;
system ("pause");
return 0;
}

Ou seja conseguimos criar um array onde é o utilizador a definir o tamanho do array e


que depois coloca o valor para cada um dos elementos.

O operador new retorna o endereço onde começa o bloco de memória. e como retorna
um endereço vamos colocá-lo num pointer.

Necessitamos do uso do pointer que deverá ser do mesmo tipo que a tipologia de
variável que é alocada dinamicamente. int * iPtr = new int[numTests];
 Temos termo NEW. Que é um operador cuja função é alocar dinamicamente
memória
 Temos a tipologia da variável a alocar dinamicamente
 Repare que NÃO temos o nome do array
 Uma vez que o array fica sem nome para nos referirmos a cada elemento do
array teremos de usar o pointer.

Podemos inicializar de duas maneiras:

int *IDpt = new int;


*IDpt = 5;

ou

int *IDpt = new int(5); //Allocates an int object and initializes


it to value 5.
char *letter = new char('J');

Operador Delete - Memory Leak


O tempo de vida de uma variável criada dinamicamente é o tempo de execução do
programa. Se um ponteiro aponta para uma variável dinâmica e fica out of scope, já não
conseguiremos aceder a essa memória criada dinamicamente. Fica indisponível. A isso
se chama Memory Leak

Explicando: se alocamos memória dinamicamente dentro de uma função usando um


ponteiro local, quando a função termina, o ponteiro será destruído, mas a memória
mantém-se. Assim já não teríamos maneira de chamar essa memória porque ela não tem
nome! Apenas tínhamos o endereço que estava no ponteiro.

Portanto, se realmente não necessitamos mais dos dados que estão nessa memória
dinâmica, em vez de eles estarem a ocupar espaço vamos apagá-los! Necessitamos de
libertar essa memória através do operador delete – este operador entrega ao sistema
operativo a memoria reservada dinamicamente.

A sintaxe é

delete [] iPtr;

Este delete operator não apaga o pointer mas sim a memória onde o ponteiro aponta

dynamic memory allocation funciona porque a memória não é reservada no momento da


compilação, mas antes na execução. Em vez de ser no STACK (compilação) a memória
é reservada no HEAP (execução). O heap é uma parte da memória que é usada como
memória temporária.

Pergunta: onde é que fica situado o Heap?


Vamos explicar melhor todo este processo: pois isto tem de entrar na cabeça!!!

void myfunction()
{
int *pt;
int av;
pt = new int(1024);
....
....
//No delete
}
int main()
{
while (some condition exists) // Pseudo-code
{
myfunction();
}
exit 0;
}

quando a função “myfunction” é chamada a variável “av” é criada no stack e quando a


função acaba a variavel é retirada do stack. O mesmo acontece com o ponteiro pt, ele é
uma variável local. ou seja quando a função acaba o ponteiro também termina e é
retirado do stack. Porém o objecto alocado dinamicamente ainda existe. E agora não
conseguimos apagar esse objecto por que ele não tem nome e a única maneira que
tínhamos para saber onde ele estava era através do ponteiro que terminou quando
termina a função pois ele é local. então á medida que o programa continua a operar mais
e mais memória será perdida do Heap (free store). se o programa continuar o tempo
suficiente, deixaremos de ter memória disponível e o programa deixará de operar.

Retornando um ponteiro para uma variável local


#include <iostream>
using namespace std;
char * setName();
int main (void)
{
char* str = setName(); //ponteiros para a função
cout << str; //imprimo o valor do
ponteiros?
system (“pause”);
return 0;
}
char* setName (void)
{
char name[80];
cout << "Enter your name: ";
cin.getline (name, 80);
return name;
}

O que se passou aqui é que o ponteiro que sai da função setName aponta para o array
local cuja vida acaba quando a função termina de executar. A solução é estender o
tempo de vida. Uma solução era tornar esse array global, mas existem alternativas
melhores.
Retornando um Ponteiro a uma Variável Local
Estática
Uma dessas alternativas é

#include <iostream>
using namespace std;
char * setName();
int main (void)
{
char* str = setName();
cout << str;
system (“pause”);
return 0;
}
char* setName (void)
{
static char name[80]; //crio como static
cout << "Enter your name: ";
cin.getline (name, 80);
return name;
}

A diferença é que usamos a palavra static. O ponteiro do setName aponta para o array
local, e como foi utilizado o static, ele perdura até fim da função, terminando apenas
quando o programa acaba.

Returning a Pointer to a Dynamically Created


Variable
Outra alternative, talvez melhor

#include <iostream>
using namespace std;
char * setName();
int main (void)
{
char* str= setName();
cout << str;
delete [] str; //faço o delete para evitar o memory leak
system ("pause");
return 0;
}
char* setName (void)
{
char* name = new char[80]; //crio ponteiro chamado de name e dou
o valor do endereço da memoria dinâmica
cout << "Enter your name: ";
cin.getline (name, 80);
return name;
}

Isto funciona porque o ponteiro retornado da função setname aponta para o array cujo
tempo de vida persiste. O address do ponteiro local é atribuído no main a outro ponteiro
no main –str. Depois este ponteiro é usado até ao fim da execução do programa. Este é
um exemplo onde diferentes ponteiros apontam para o mesmo endereço.

Mas ter atenção que se fizermos o delete através de um ponteiro, ter cuidado com o
segundo ponteiro que aponta para a memoria que acabou de ser deslocada.

Alocar dinamicamente Arrays


Ora o que fizemos antes com variáveis, vamos ter de fazer com arrays.

int *pt = new int[1024]; //allocates an array of 1024 ints


double *myBills = new double[10000];
/* This doesn't mean I owe 10000.0, but rather allocates an array of
10000 doubles to hold the amounts of the thousands of bills I receive
monthly. */

Notar a diferença:

int *pt = new int[1024]; //allocates an array of 1024 ints


int *pt = new int(1024); //allocates a single int with value 1024

a melhor maneira para alocar um array dinamicamente é usar o loop

int *buff = new int[1024];


for (i = 0; i < 1024; i++)
{
*buff = 52; //Assigns 52 to each element;
buff++;
}
ou se quisermos desta maneira
int *buff = new int[1024];
for (i = 0; i < 1024; i++)
{
buff[i] = 52; //Assigns 52 to each element;
}

para utilizar o delete em arrays

delete[] pt;
delete[] myBills;

Dangling Pointers
int *myPointer;
myPointer = new int(10);
cout << "The value of myPointer is " << *myPointer << endl;
delete myPointer;
*myPointer = 5;
cout << "The value of myPointer is " << *myPointer << endl;

neste exemplo libertámos a memória dinâmica, mas o ponteiro continua isto é um bug
tremendo, e muito dificil de detectar. o programa continua a correr e a secção de
memória pode ser usada por outro objecto dinâmico. acontece que essa memoria estará
corrompida se continuar a usar o myPointer. a melhor maneira é depois do delete fazer
apontar para zero, fazê-lo um ponteiro nulo. se tentarem usar o ponteiro iremos ter a run
time exception e o bug pode ser identificado

assim corrigindo o código anterior ficaríamos com

int *myPointer;
myPointer = new int(10);
cout << "The value of myPointer is " << *myPointer << endl;
delete myPointer;
myPointer = 0;
*myPointer = 5; //This statement will cause an run-time exception,
now.
cout << "The value of myPointer is " << *myPointer << endl;

Verificar a existência de memória para dinâmica


Na alocação dinâmica temos de nos certificar de que a alocação no heap foi feita com
sucesso e podemos ver isso de duas maneiras:

 Uma é as excepções (este é o método defaut)

bobby = new int [5]; // if it fails an exception is thrown


“bad_alloc” exception

vamos ver este caso quase no ultimo capitulo- isto é uma capítulo avançado

 notthrow, aqui no caso de não se conseguir a memória retorna um ponteiro nulo,


e o programa continua.

bobby = new (nothrow) int [5];

supostamente este método pode ser tedioso para grandes projectos

Vamos ver um exemplo com o caso de nothrow

// rememb-o-matic
#include <iostream>
using namespace std;
int main ()
{
int i,n,* p;
cout << "How many numbers would you like to type? ";
cin >> i;
p= new (nothrow) int[i]; //criámos I variaveis na
execução
if (p == 0)
cout << "Error: memory could not be allocated";
else
{
for (n=0; n<i; n++)
{
cout << "Enter number: ";
cin >> p[n];
}
cout << "You have entered: ";
for (n=0; n<i; n++)
cout << p[n] << ", ";
delete[] p;
}
system ("pause");
return 0;
}