alocacao_dinamica.tex 7.94 KB
Newer Older
1 2 3 4
\documentclass[apostila.tex]{subfiles}


\begin{document}
Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
5
\chapter{Alocação dinâmica de memória}
6
\label{cap:alocdin}
7
Toda e qualquer informação que um programa utiliza está localizada na memória. Mas para que um
Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
8 9
programa possa utilizar uma área de memória para armazenar informação, é necessário que tal área seja
previamente alocada, ou seja, é necessário requisitar ao sistema operacional que reserve uma área de
10
memória para o programa e que a proteja, afim de que outros programas não venham a ler/gravar dados
Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
11
na região de memória reservada ao programa em questão.
12

13 14
Imagine se um programa que utilizasse, para armazenar um índice de um for, a mesma área de
memória que outro programa usaria para armazenar uma entrada do teclado. Ou então, que a mesma
15
área de memória venha a ser utilizada tanto para armazenar dados de um programa quanto para
Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
16 17
armazenar o código de outro programa em execução. Catástrofes de todos os tipos podem ocorrer em
tais circunstâncias e se não houver um gerenciamento de memória por parte do sistema operacional,
18 19
programar seria um desafio ainda maior, senão inviável.

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
20 21
Alocar uma área de memória significa pedir ao sistema operacional que reserve uma área para uso
exclusivo do nosso programa.
22

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
23
\section{Alocação estática $\times$ alocação dinâmica}
24

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
25 26 27 28
Existem duas maneiras de se alocar memória em um programa em C.

A primeira maneira é chamada alocação estática. Quando o sistema operacional inicia a execução de
um programa, ele aloca três regiões de memória: o segmento de código, o segmento de dados e o segmento
29 30
de pilha.

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
31 32 33
No segmento de código o sistema operacional coloca o código do programa. No segmento de dados
são colocadas as variáveis globais, constantes e variáveis static. No segmento de pilha são armazenadas,
entre outras coisas, as variáveis locais das funções do programa.
34

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
35
O problema é que o tamanho desses segmentos é fixo (calculado pelo compilador), ou seja, não pode
36
ser mudado durante a execução de um programa. Imagine que um programa, no meio de uma tarefa,
Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
37
necessite ler um arquivo de 2 Mb do disco, processá-lo e devolvê-lo para o disco. Se não for declarado no
38 39 40 41
código do programa um array de 2 Mb de tamanho, não haverá como processar o arquivo.

Agora suponha que foi declarado um matriz de 2 Mb e o programa consegue manipular o arquivo.
Suponha que esse é um arquivo de configuração e só precisa ser utilizado uma vez durante as 10 horas
Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
42 43
em que o programa ficou em execução. Como o tamanho dos segmentos é fixo (daí o nome alocação
estática), esses 2 Mb de memória alocados estaticamente estariam reservados para o programa, mas não
44 45 46
seriam utilizados (um exemplo de programa mal-comportado).

Ou seja, o programa estaria retendo 2 Mb de memória que poderiam ser usados por outros programas
Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
47
e essa memória alocada, mas não utilizada, pode trazer problemas, como impedir que se possa executar
48 49 50 51 52 53 54 55 56
outros programas por falta de memória.

Poderia ser argumentado que pelo menos o programa funciona. Agora suponha que aquele arquivo
tivesse seu tamanho aumentado para 2.5 Mb. Seria necessáro alterar no código o tamanho da matriz e
recompilar o programa cada vez que mudasse o tamanho do arquivo.

O ideal é que esses 2 Mb de memória sejam alocados somente quando forem necessários e sejam
liberados para outros programas quando deixassem de ser úteis.

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
57 58 59
É aí que entra a alocação dinâmica: a alocação dinâmica permite que o programa reserve uma área
de memória de qualquer tamanho (dentro dos limites do tamanho da memória, é claro) em tempo de
execução. Isso quer dizer que o programa/programador/compilador não precisa saber antecipadamente
60 61 62 63
o tamanho do bloco de memória de que o nosso programa precisa.

Durante a execução, o programa descobre qual é o tamanho da área de memória que necessita e pede
ao sistema operacional para reservar uma área de memória daquele tamanho. O sistema operacional
Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
64
reserva a área requisitada (se possível) e devolve para o programa o endereço do primeiro byte da área
65 66
de memória alocada. No programa, esse endereço pode ser armazenado em um ponteiro.

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
67
\section{sizeof}
68
\label{sec:sizeof}
Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
69 70

Antes de apresentar as funções de manipulação de memória dinâmica, é importante descrever o operador
71 72 73 74 75
sizeof.

O operador sizeof é usado para obter o tamanho, em bytes, de um determinado tipo de dado.
A sintaxe geral é:

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
76
\begin{lstlisting}
77
sizeof(tipo) // ou ainda
Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
78 79
sizeof(variavel)
\end{lstlisting}
80

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
81 82
O sizeof retorna o tamanho do tipo passado como parâmetro ou do tipo da variável passada como
parâmetro.
83

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
84 85 86 87 88 89
Exemplos:

\begin{lstlisting}
struct coord {
	int x, y, z;
};
90 91 92

struct coord coordenada1;

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
93 94 95 96 97 98
sizeof(struct coord); // obtêm o valor 12 (4 bytes por int )
sizeof(coordenada1); // obtêm o valor 12 (4 bytes por int )
sizeof(int); // obtêm o valor 4
\end{lstlisting}

Esse operador é extremamente útil quando é necessário trabalhar com alocação dinâmica de memória
99 100 101
porque permite ao programa determinar o quanto de memória deve ser alocado para um determinado
tipo de dado.

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
102
\section{Função malloc()}
103

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
104 105
A função malloc requisita ao sistema operacional para alocar uma área de memória do tamanho especificado.
Essa função é extremamente útil para gerar matrizes cujo tamanho não é possível ser definido antes
106 107 108 109 110
de executar o programa. Além disso, existem estruturas de dados (listas, filas, pilhas, entre outras) que
tem tamanho variável e precisam dessa função para serem implementadas.

O protótipo da função malloc é:

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
111 112 113 114
\begin{lstlisting}
void *malloc (unsigned int numero_de_bytes);
\end{lstlisting}

115 116 117
A função recebe como argumento o tamanho em bytes de memória que se deseja alocar e devolve um
ponteiro do tipo void* para o primeiro byte da área de memória alocada.

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
118
Em caso de erro (não há memória suficiente), o valor retornado é NULL. Como um ponteiro do tipo
119 120 121
void* não tem tipo definido, pode ser utilizado um casting para especificar que tipo de ponteiro ele deverá
ser.

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
122
Em algumas implementações da linguagem C, o compilador limita a quantidade de memória que o
123 124
programador pode alocar (ex.: no compilador Borland C 3.0, o máximo é 64Kb).

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
125
Exemplo:
126
\vspace*{\fill}
127

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
128 129 130
\begin{lstlisting}
#include <stdlib.h>
#include <stdio.h>
131

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
132
int main(){
133

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
134 135
	int *vetor, tamanho;
	printf("Digite o tamanho do vetor:");
136

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
137 138
	scanf("%d", &tamanho);
	vetor = (int*)malloc(sizeof(int)*tamanho);
139

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
140 141 142
	return 0;
}
\end{lstlisting}
143 144

Observe que para alocar o tamanho correto para um vetor de int, é necessário multiplicar o número
Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
145
de células que se deseja pelo tamanho do tipo de dado int porque cada célula individual é do tamanho
146 147
int.

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
148
\section{Função free()}
149

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
150 151
A função free é a inversa da função malloc, isto é, ela desaloca (libera) uma área de memória previamente
alocada pela função malloc. Abaixo temos a declaração da função:
152

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
153 154 155
\begin{lstlisting}
void free (void *memblock);
\end{lstlisting}
156 157


Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
158 159
A função recebe um único argumento, o qual é um ponteiro para uma área de memória previamente
alocada com malloc.
160

161 162 163 164 165 166
\vspace*{\fill}

\textit{Este espaço foi propositalmente deixado em branco. Veja o código completo na próxima página.}

\vspace*{\fill}

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
167
\lstinputlisting{exemplos/alocacao_dinamica.c}
168

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
169
\section{Exercícios}
170

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
171 172 173 174
1) Faça uma função gera matriz com os seguintes parâmetros:
\begin{lstlisting}
int*** geramatriz(int x, int y, int z, int tam);
\end{lstlisting}
175

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
176
A função deverá alocar uma matriz de dimensão 3, cada dimensão deverá ter tamanho tam e, ao final,
177 178
a função retorna a matriz.

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
179 180
2) Crie uma função que seja capaz de redimensionar um vetor previamente alocado (vetor de dimensão
1).
181

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
182
A função será chamada realoca e receberá os seguintes parâmetros:
183

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
184 185 186
\begin{lstlisting}
int* realoca(int *vetor, int tam, int novo tam);
\end{lstlisting}
187

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
188
Onde vetor é o vetor que deve ser realocado, tam é o tamanho velho do vetor e novo tam é o novo
189 190 191
tamanho.

A função deverá ser capaz de preservar o conteúdo de vetor. Se o novo tam for menor que tam, a
Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
192
informação das células excedentes deve ser descartada.
193 194

\end{document}