Buscar

Alocação Dinâmica de Memória em C e C++

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ê também pode ser Premium ajudando estudantes

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ê também pode ser Premium ajudando estudantes

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ê também pode ser Premium ajudando estudantes
Você viu 3, do total de 10 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

Você também pode ser Premium ajudando estudantes

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ê também pode ser Premium ajudando estudantes

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ê também pode ser Premium ajudando estudantes
Você viu 6, do total de 10 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

Você também pode ser Premium ajudando estudantes

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ê também pode ser Premium ajudando estudantes

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ê também pode ser Premium ajudando estudantes
Você viu 9, do total de 10 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

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

APOSTILA : LINGUAGENS DE PROGRAMAÇÃO C E C++ 
Angelo Passaro Página VII.1 Segunda Revisão / 2002 
 
VVIIII.. AALLOOCCAAÇÇÃÃOO DDIINNÂÂMMIICCAA DDEE MMEEMMÓÓRRIIAA 
Até o momento, vimos como atribuir a ponteiros o endereço de variáveis que já haviam sido definidas 
estaticamente. 
Nesta seção, vamos verificar como criar dinamicamente variáveis, arrays e outros tipos derivados. Criar 
variáveis dinamicamente significa reservar memória para um novo objeto ( de tipo intrínseco ou derivado) em 
tempo de execução ou, mais simplesmente, durante a execução do programa. 
Para tal faremos uso de algumas funções das bibliotecas básicas do C e C++, a saber, as funções malloc(), 
calloc(), realloc(), free(), da biblioteca padrão da linguagem C, e os operadores new e delete, específicos da 
linguagem C++. 
VII.A. Alocação dinâmica com funções da biblioteca padrão 
da linguagem C VII.A 
 
VII.A.1. Função malloc() VII.A.1 
A função malloc() reserva uma área de memória (inicialmente livre, ou seja não utilizada pelo sistema) de 
comprimento tamanho bytes para o programa que a requisitou. 
Um ponteiro para o início da área reservada é devolvido ao programa. O retorno do valor zero (ou NULL) 
significa que não foi possível reservar a quantidade de memória requisitada pelo programa. 
É importante que o retorno NULL seja sempre verificado para evitar o uso de memória que não pôde ser 
reservada para o programa. 
Sintaxe: 
#include <stdlib.h> //( Diretiva de pré-compilação exigida) 
void *malloc (size_t tamanho); 
onde size_t é um tipo de dado cuja definição depende da implementação (ou seja, do fabricante do compilador – 
consulte o arquivo stdlib.h para verificar a definição desse tipo). 
Note que a função malloc devolve um ponteiro para void. Observe os exemplos a seguir para verificar como 
um ponteiro void é modificado para o tipo de ponteiro de interesse. 
 
Exemplo 1: 
double * p_dvar = 0; // inicializa um ponteiro para doubles com zero (ou seja, NULL). 
 
p_dvar = (double *)malloc(800); // reserva 800 bytes de memória para o programa. 
 // Como p_dvar é um ponteiro para uma área de memória que deve 
 // armazenar números do tipo double (veja a declaração), significa que 
 // foi reservado um espaço de memória contínuo para armazenar até10 
APOSTILA : LINGUAGENS DE PROGRAMAÇÃO C E C++ 
Angelo Passaro Página VII.2 Segunda Revisão / 2002 
 
 // números do tipo double. Em outras palavras, reservou-se um espaço 
 // para um array de 10 elementos do tipo double. 
 // Observe o uso do operador cast (seção III.D.3) 
 
if (p_dvar == 0) cout << “\a ERRO : Nao foi possivel reservar a memória requisitada\n”; 
 
Exemplo 2: 
double * p_dvar = 0; // inicializa um ponteiro para doubles com zero (ou seja NULL). 
 
p_dvar = (double *) malloc(10 * sizeof(double)); // Reserva 800 bytes de memória para o programa. 
 // Observe o uso do operador cast (seção III.D.3) 
 // Significa que foi reservado um espaço de memória contínuo para 
 // armazenar um array de até 10 números do tipo double. 
 // Observem o uso do operador sizeof() (consulte o capítulo III, 
 // seção III.A.8). Este operador costuma ser utilizado em conjunto 
 // com malloc para facilitar a determinação do número de bytes 
 // necessários, principalmente se esse número depende de dados 
 // fornecidos pelo usuário do programa (veja exemplo a seguir). 
 
if ( ! p_dvar) cout << “\a ERRO : Nao foi possivel reservar a memória requisitada\n”; 
 
Exemplo 3: 
long int * p_lvar = 0; // inicializa um ponteiro para long int com zero (ou seja NULL). 
short int tam; 
cout << “entre com o tamanho do array a ser dimensionado : “; 
cin >> tam; 
 
p_lvar = (long int *) malloc(tam * sizeof(long)); // Reserva (tam * 4) bytes de memória para o programa, 
 // ou seja, tenta reservar um espaço de memória contínuo para 
 // armazenar um array de tam números do tipo long int. 
if ( ! p_lvar) cout << “\a ERRO : Nao foi possivel reservar a memória requisitada\n”; 
 
VII.A.2. Função calloc() VII.A.2 
APOSTILA : LINGUAGENS DE PROGRAMAÇÃO C E C++ 
Angelo Passaro Página VII.3 Segunda Revisão / 2002 
 
A função calloc() reserva uma área de memória (inicialmente livre) para num_elementos de um dado tipo, 
com cada um ocupando um comprimento de tamanho bytes, para o programa que a requisitou. Além disso, 
inicializa os elementos com o valor zero. 
Um ponteiro para o início da área reservada é devolvido ao programa. O retorno do valor zero (ou NULL) 
significa que não foi possível reservar a quantidade de memória requisitada pelo programa. 
É importante que o retorno NULL seja sempre verificado para evitar o uso de memória que não pôde ser 
reservada para o programa. 
Sintaxe: 
#include <stdlib.h> //( Diretiva de pré-compilação exigida) 
void *calloc (size_t num_elementos, size_t tamanho); 
onde size_t é um tipo de dado cuja definição depende da implementação (ou seja, do fabricante do compilador – 
consulte o arquivo stdlib.h para verificar a definição desse tipo). 
Note que a função calloc devolve um ponteiro para void. Observe os exemplos a seguir para verificar como 
um ponteiro void é modificado para o tipo de ponteiro de interesse. 
Exemplo 1: 
double * p_dvar = 0; // declara e inicializa um ponteiro para doubles com zero (ou seja NULL). 
 
p_dvar = (double *) calloc(10, 8)); // reserva 800 bytes de memória para o programa e 
 // inicializa os elementos com zero. 
 // Observe o uso do operador cast (seção III.D.3) 
 
if (p_dvar == 0) cout << “\a ERRO : Nao foi possivel reservar a memória requisitada\n”; 
 
Exemplo 2: 
double * p_dvar = 0; // declara e inicializa um ponteiro para doubles com zero (ou seja NULL). 
 
p_dvar = (double *) calloc(10, sizeof(double)); // Reserva 800 bytes de memória para o programa e 
 // inicializa os elementos com zero. 
 // Observe o uso do operador cast (seção III.D.3) 
 // Observe, novamente, o uso do operador sizeof() (consulte o// capítulo III, seção III.A.8). 
if ( ! p_dvar) cout << “\a ERRO : Nao foi possivel reservar a memória requisitada\n”; 
 
Exemplo 3: 
long int * p_lvar = 0; // inicializa um ponteiro para long int com zero (ou seja NULL). 
short int tam; 
APOSTILA : LINGUAGENS DE PROGRAMAÇÃO C E C++ 
Angelo Passaro Página VII.4 Segunda Revisão / 2002 
 
cout << “entre com o tamanho do array a ser dimensionado : “; 
cin >> tam; 
 
p_lvar = (long int *) calloc(tam, sizeof(long)); // Reserva (tam * 4) bytes de memória para o programa, 
 // ou seja, tenta reservar um espaço de memória contínuo para 
 // armazenar um array de tam números do tipo long int. 
if ( ! p_lvar) cout << “\a ERRO : Nao foi possivel reservar a memória requisitada\n”; 
VII.A.3. Função realloc() 
A função realloc() modifica o tamanho de uma área de memória previamente alocada, cujo endereço é dado 
por ptr, para tamanho bytes. O conteúdo armazenado na área original é deslocado para a nova área alocada, e a 
área original é liberada. Se ptr é NULL, a função realloc() se comporta de forma análoga à função malloc(), 
reservando o espaço requisitado. 
Um ponteiro para o início da nova área reservada é devolvido ao programa. O retorno do valor zero (ou 
NULL) significa que não foi possível reservar a quantidade de memória requisitada pelo programa. Neste caso, o 
conteúdo da área original permanece inalterado. 
É importante que o retorno NULL seja sempre verificado para evitar o uso de memória que não pôde ser 
reservada para o programa. 
Sintaxe: 
#include <stdlib.h> //( Diretiva de pré-compilação exigida) 
void *realloc (void * ptr, size_t tamanho); 
onde size_t é um tipo de dado cuja definição depende da implementação (ou seja, do fabricante do compilador – 
consulte o arquivo stdlib.h para verificar a definição desse tipo). 
Note que a função realloc também devolve um ponteiro para void. 
Exemplo: 
double * p_dvar = 0; // declara e inicializa um ponteiro para doubles com zero (ou seja NULL). 
 
p_dvar = (double *) calloc(10, 8)); // reserva 800 bytes de memória para o programa e 
 // inicializa os elementos com zero. 
 // Observe o uso do operador cast (seção III.D.3) 
if (p_dvar == 0) cout << “\a ERRO : Nao foi possivel reservar a memória requisitada\n”; 
else { 
double * p_aux = o_dvar; // observe a inicialização do novo ponteiro 
p_dvar = (double *) realloc(p_aux, 100 * sizeof (double)); // requisita mais área de memória 
if (p_dvar == p_aux) cout << “\a AVISO : Nao foi possivel reservar mais memória \n”; 
} 
APOSTILA : LINGUAGENS DE PROGRAMAÇÃO C E C++ 
Angelo Passaro Página VII.5 Segunda Revisão / 2002 
 
 
VII.A.4. Função free() VII.A.4 
A função free() libera uma área de memória previamente alocada pelas funções malloc() ou calloc(), cujo 
endereço é dado por ptr. Se ptr é NULL, nenhuma memória é liberada. 
Sintaxe: 
#include <stdlib.h> //( Diretiva de pré-compilação exigida) 
void *free (void * ptr); 
 
Exemplos: 
 
Considere os ponteiros alocados nos exemplos das seções VII.A.1 e VII.A.2. 
free (p_dvar); 
free (p_lvar); 
Observação: 
A função free() deve ser utilizada para liberar memória reservada pelas funções malloc() ou calloc() e não 
o operador delete() apresentado na seção VII.B.2. 
 
 
VII.B. Alocação dinâmica na linguagem C++ VII.B 
A linguagem C++ herda as funções da biblioteca padrão da linguagem C. Isto significa que é possível 
utilizar as funções para alocação e liberação dinâmica de memória da linguagem C, apresentadas na seção VII.A. 
Contudo, a linguagem C++ apresenta formas mais sofisticadas para a alocação dinâmica de memória. As 
vantagens das formas específicas do C++ somente podem ser totalmente apreciadas quando da utilização de 
classes. Por essa razão, os tópicos apresentados nas subseções seguintes voltarão a ser abordados oportunamente. 
VII.B.1. Operador new() VII.B.1 
O operador new() cria um objeto dinâmico do tipo especificado. A seguinte seqüência de processamento é 
executada quando do uso do operador new(). 
1. reserva a memória necessária para conter o objeto; 
2. Inicializa o objeto (o uso de construtores para inicialização de objetos será abordado mais adiante). 
Tipos de dados intrínsecos não são inicializados; 
3. retorna um ponteiro para o objeto com o tipo apropriado (não se utiliza o operador cast). 
Sintaxe: 
APOSTILA : LINGUAGENS DE PROGRAMAÇÃO C E C++ 
Angelo Passaro Página VII.6 Segunda Revisão / 2002 
 
new tipo (lista_de_inicializacao) ; 
new tipo [dim1] (lista_de_inicializacao); 
 
A primeira sintaxe cria um objeto do tipo especificado, enquanto a segunda cria um array de objetos do tipo 
especificado, com o número de dimensões indicada pelo número de pares []. A lista de inicialização é opcional e 
permite definir os valores de inicialização dos elementos do objeto criado. Este recurso somente será 
compreendido quando do uso de recursos da programação orientada a objetos. 
 
Exemplos: 
int * p_i1 = new int; // cria um objeto do tipo inteiro 
int * p_i2 = new int (1); // cria um objeto do tipo int inicializando-o com o valor 1 
int * p_i3 = new int [2]; // cria um ponteiro para um array de 2 inteiros 
float * p_f1 = new float [3]; // cria um array de 3 objetos do tipo float 
struct S { int i; double f; }; 
S * p_s1 = new S ; 
S * p_s2 = new S [8]; // cria um ponteiro para um array de 8 structs S 
 
VII.B.2. Operador delete() VII.B.2 
O operador delete() é utilizado para liberar a memória reservada com o uso do operador new(). A seguinte 
seqüência de processamento é executada quando do uso do operador delete(). 
1. Deinicializa o objeto (utilizando o destruidor da classe – assunto que será abordado oportunamente). 
Este processamento não é executado quando de um objeto de tipo intrínseco; 
2. Libera a memória reservada anteriormente. 
Sintaxe: 
delete identificador_do_ponteiro ; 
delete [ ] identificador_do_ponteiro; 
 
A primeira sintaxe libera a memória reservada a um objeto, identificado por identificador_do_ponteiro, 
reservada pelo operador new(). A segunda forma libera a memória reservada a um array de objetos, identificados 
por identificador_do_ponteiro, reservada pelo operador new [] (). Note-se que o operador de subscrito [ ] deve ser 
especificado quando se libera a memória reservada para um array. 
Observações: 
O operador delete deve ser utilizado para liberar memória reservada pelo operador new () e não a função 
free() apresentado na seção VII.A.. 
O operador delete() não deve ser utilizado em um identificador_de_ponteiro para um objeto que foi 
eliminado anteriormente. Tal procedimento pode levar a danos sérios na execução do próprio programa 
ou de outros processos. 
APOSTILA : LINGUAGENS DE PROGRAMAÇÃO C E C++ 
Angelo Passaro Página VII.7 Segunda Revisão / 2002 
 
 
Nos exemplos a seguir são liberadas as memórias alocadas na seção VII.B.1.Exemplos: 
delete p_i1; 
delete p_i2; 
delete [ ] p_i3; // elimina o array de 2 inteiros 
delete [ ] p_f1; // elimina o array de objetos do tipo float 
delete p_s1; // elimina uma struct S 
delete [ ] p_s2; // elimina o array de structs S 
VII.C. Exemplos de Alocação Dinâmica VII.C 
VII.C.1. Soma dos valores e um conjunto de dados definidos em 
tempo de execução 
Nesta seção mostramos como reservar memória dinamicamente para um vetor e, depois, os elementos do 
vetor são somados. Apresentamos, primeiramente, uma versão do programa que utiliza vetores estáticos. A 
segunda versão executa a mesma operação, porém utilizando alocação dinâmica. Compare os dois programas para 
identificar as diferenças. 
Versão com vetores estáticos 
 
#include <iostream.h> 
 
const short int DIM = 100; 
 
void main(void) 
{ 
 
 int N,i; 
 float p_dado[DIM]; // vetor limitado a 100 elementos 
 float soma; 
 cout << "\n Entre o número de dados a serem somados: "; 
 cout.flush(); 
 do{ 
 cin >> N; 
 } while (N <=0 || N > 100); // para garantir o limite do vetor 
 
 for (i = 0; i < N; i++) 
 { 
 cout << endl << "Forneca o dado "<< (i+1)<< ": "; 
 cin >> p_dado[i] ; 
 } 
 
 // calculo da soma 
 for (i = 0, soma = 0; i < N; soma += p_dado[i++]); 
 
 // saida de dados 
 cout << endl << endl << "DADOS FORNECIDOS "; 
 cout << endl << "indice dado "; 
 
 for (i = 0; i < N; i++) 
APOSTILA : LINGUAGENS DE PROGRAMAÇÃO C E C++ 
Angelo Passaro Página VII.8 Segunda Revisão / 2002 
 
 { 
 cout << endl << i << " " << p_dado[i] ; 
 } 
 cout << endl << endl 
 << "A soma dos dados fornecidos e : "<< soma << endl; 
 cout.flush(); 
 
} 
 
Versão com alocação dinâmica 
 
#include <iostream.h> 
 
 
void main(void) 
{ 
 int N,i; 
 float *p_dado; // declara o ponteiro para float 
 float soma; 
 cout << "\n Entre o número de dados para o cálculo da soma: "; 
 cout.flush(); 
 cin >> N; 
 p_dado = new float [N]; // reserva memória dinamicamente para todo o conjunto de dados 
 
 if (p_dado) 
 { // executa o bloco se o vetor tiver sido criado 
 for (i = 0; i < N; i++) 
 { 
 cout << endl << "Forneca o dado "<< (i+1) <<": "; 
 cin >> p_dado[i]; 
 } 
 
 // calculo da soma 
 for (i = 0, soma = 0; i < N; soma += p_dado[i++]); 
 
 
 // saida de dados 
 cout << endl << endl << "DADOS FORNECIDOS "; 
 cout << endl << "indice dado"; 
 
 for (i = 0; i < N; i++) 
 { 
 cout << endl << i << " " << p_dado[i] ; 
 } 
 
 cout << endl << endl 
 << "A soma dos dados fornecidos e : "<< soma << endl; 
 cout.flush(); 
 } 
 
 if (p_dado) delete [] p_dado; // retorna memória para o sistema somente se 
 // a reserva tiver ocorrido com sucesso. 
 
} 
 
VII.C.2. Calculo da Média ponderada de um conjunto de dados 
definidos em tempo de execução 
A média ponderada de um conjunto de dados, definidos pelo array dado, com pesos definidos pelo array 
peso, é dada por: 
APOSTILA : LINGUAGENS DE PROGRAMAÇÃO C E C++ 
Angelo Passaro Página VII.9 Segunda Revisão / 2002 
 
�
�
=
=
= N
1i
N
1i
]i[peso
]i[dado*]i[peso
p_media 
 
Pode-se observar que, para o cálculo da média ponderada são necessários um vetor de dados e um vetor de 
pesos, além do número de elementos dos dois vetores. Um programa simples para o cálculo da média ponderada, 
com uso de vetores estáticos, é apresentado a seguir: 
 
#include <iostream.h> 
 
const short int DIM = 100; // declara e define DIM, uma constante do tipo short int 
 
void main(void) 
{ 
 
 int N,i; 
 float p_dado[DIM], p_peso[DIM]; // vetores limitados a 100 elementos 
 float soma, soma_peso, media_p; 
 cout << "\n Entre o número de dados para o cálculo da média ponderada: "; 
 cout.flush(); 
 do{ 
 cin >> N; 
 } while (N <=0 || N > 100); // para garantir que o número de elementos lidos esteja nos limites do vetor 
 
 for (i = 0; i < N; i++) 
 { 
 cout << endl << "Forneca o dado "<< (i+1) 
 << " e o seu peso (separados por espaço): "; 
 cin >> p_dado[i] >> p_peso[i] ; 
 } 
 
 // calculo da media ponderada 
 for (i = 0, soma = soma_peso = 0; i < N; ) 
 { 
 soma += p_peso[i] * p_dado[i]; 
 soma_peso += p_peso[i++]; 
 } 
 media_p = soma/soma_peso; 
 
 // saida de dados 
 cout << endl << endl << "DADOS FORNECIDOS "; 
 cout << endl << "indice dado peso"; 
 
 for (i = 0; i < N; i++) 
 { 
 cout << endl << i << " " << p_dado[i] 
 << " " << p_peso[i] ; 
 } 
 cout << endl << endl 
 << "A media ponderada dos dados fornecidos e : "<< media_p << endl; 
 cout.flush(); 
 
} 
 
O programa a seguir é uma variante do programa anterior, no qual, tanto o vetor peso como vetor de dados 
são alocados dinamicamente. 
 
APOSTILA : LINGUAGENS DE PROGRAMAÇÃO C E C++ 
Angelo Passaro Página VII.10 Segunda Revisão / 2002 
 
#include <iostream.h> 
 
void main(void) 
{ 
 int N,i; 
 float *p_dado, *p_peso; // declaração dos ponteiros para float 
 float soma, soma_peso, media_p; 
 
 // solicita o número de dados para o cálculo da média ponderada (corresponde ao N na equação) 
 cout << "\n Entre o número de dados para o cálculo da média ponderada: "; 
 cout.flush(); // Força a impressão da "stream" (ou seja, da cadeia de caracteres) 
 cin >> N; 
 
 // Neste ponto reserva a memória necessária para os vetores de dados e de peso (alocação dinâmica) 
 p_dado = new float [N]; 
 p_peso = new float [N]; 
 
 if (p_dado && p_peso) 
 { // executa o bloco se ambos os vetores tiverem sido criados 
 for (i = 0; i < N; i++) 
 { 
 cout << endl << "Forneca o dado "<< (i+1) 
 << " e o seu peso (separado por espaço): "; 
 cin >> p_dado[i] >> p_peso[i] ; 
 } 
 
 // calculo da media ponderada 
 for (i = 0, soma = soma_peso = 0; i < N; ) 
 { 
 soma += p_peso[i] * p_dado[i]; 
 soma_peso += p_peso[i++]; 
 } 
 
 media_p = soma/soma_peso; 
 
 // saida de dados 
 cout << endl << endl << "DADOS FORNECIDOS "; 
 cout << endl << "indice dado peso"; 
 
 for (i = 0; i < N; i++) 
 { 
 cout << endl << i << " " << p_dado[i] 
 << " " << p_peso[i] ; 
 } 
 
 cout << endl << endl 
 << "A media ponderada dos dados fornecidos e : "<< media_p; 
 cout.flush(); 
 } 
 
 if (p_dado) delete [] p_dado; // como não se tem certeza qual vetor não foi 
 if (p_peso) delete [] p_peso; // criado, testa-se o ponteiro antes do comando delete 
 
} 
 
Exercicios: 
1. Escreva um programa que faça o cálculo da média aritmética de um número de dados definidos em 
tempo de execução. 
2. Modifique o programa, escrevendo e utilizando uma função que faz o cálculo da média aritmética de 
um número qualquer de dados. (Considere, primeiramente, quais são os parâmetros que devem ser 
passados à função e qual o valor de retorno). 
3. Modifique o programa escrito no exercício 1, incluindo uma função para o cálculo da média ponderada.

Outros materiais

Materiais relacionados

Perguntas relacionadas

Materiais recentes

Perguntas Recentes