alocacao_dinamica.tex 8.96 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
seriam utilizados (um exemplo de programa mal-comportado).

46
Ou seja, o programa estaria retendo 2 Mb de memória que poderiam ser usados por outros programas, podendo impedir a execução dos mesmos por falta de memória.
47 48 49 50 51 52 53 54

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
55 56 57
É 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
58
o tamanho do bloco de memória de que o programa precisa.
59 60 61

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
62
reserva a área requisitada (se possível) e devolve para o programa o endereço do primeiro byte da área
63 64
de memória alocada. No programa, esse endereço pode ser armazenado em um ponteiro.

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

Antes de apresentar as funções de manipulação de memória dinâmica, é importante descrever o operador
69 70
sizeof.

71
O operador sizeof(sim, é um operador) é retorna o tamanho, em bytes, de um determinado tipo de dado ou variável.
72 73
A sintaxe geral é:

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

79
Veja os Exemplos:
Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
80 81 82

\begin{lstlisting}
struct coord {
83 84 85
	int x; //4 bytes
	char y; // 1 bytes
	char z[3]; //3 bytes
Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
86
};
87

88
struct coord coord_var;
89

90 91
sizeof(struct coord); // obtêm o valor 8 (4 bytes por int + 1 por char + 3 pelo array de chars)
sizeof(coord_var); // obtêm o valor 8 (4 bytes por int + 1 por char + 3 pelo array de chars)
Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
92 93 94 95
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
96 97 98
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
99
\section{Função malloc()}
100

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
101 102
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
103
de executar o programa. Além disso, existem diversas estruturas de dados (listas, filas, pilhas, entre outras) que
104 105 106 107
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
108 109 110 111
\begin{lstlisting}
void *malloc (unsigned int numero_de_bytes);
\end{lstlisting}

112 113 114
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
115
Em caso de erro (não há memória suficiente), o valor retornado é NULL. Como um ponteiro do tipo
116 117 118
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
119
Em algumas implementações da linguagem C, o compilador limita a quantidade de memória que o
120 121
programador pode alocar (ex.: no compilador Borland C 3.0, o máximo é 64Kb).

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
122
Exemplo:
123
\vspace*{\fill}
124

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
125 126 127
\begin{lstlisting}
#include <stdlib.h>
#include <stdio.h>
128

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
129
int main(){
130

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
131 132
	int *vetor, tamanho;
	printf("Digite o tamanho do vetor:");
133

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
134
	scanf("%d", &tamanho);
135
	vetor = malloc(sizeof(int)*tamanho);
136

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
137 138 139
	return 0;
}
\end{lstlisting}
140 141

Observe que para alocar o tamanho correto para um vetor de int, é necessário multiplicar o número
142 143 144 145 146 147 148 149
de células que se deseja pelo tamanho do tipo de dado int, pois cada célula é do tamanho de um
inteiro.

\subsection{Não faça casting}

É muito comum vermos em exemplos de código utilizando a função malloc o \textit{casting} do retorno da função, pois em versões antigas de C (pre ANSI C), o retorno do malloc era um \textit{char *}, o que justificava o casting. Entretanto, este comportamento foi alterado e o retorno é um \textit{void *}. Mas então, por que realizar um \textit{casting}?

Primeiro, é desenecessário realizar esta conversão, sendo que ela é feita automaticamente. Mas principalmente, ao realizar o \textit{casting} explícito, você pode estar ocultando algum erro, como esquecer de incluir a biblioteca <stdlib.h>.
150

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

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
153 154
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:
155

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
156 157 158
\begin{lstlisting}
void free (void *memblock);
\end{lstlisting}
159 160


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

164 165 166 167 168 169 170 171 172 173 174 175 176

\section{Função realloc()}

A função realloc serve para realocar um espaço de memória previamente alocado, podendo aumentar ou dimuir este espaço.

O protótipo da função realloc é:
\begin{lstlisting}
void *realloc(void *ptr, size_t size)
\end{lstlisting}

A função recebe dois parâmetros. O primeiro é um apontador para o endereço da memória que será realocado e o segundo é o tamanho do espaço desejado.


177 178 179 180 181 182
\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
183
\lstinputlisting{exemplos/alocacao_dinamica.c}
184

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
185
\section{Exercícios}
186

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

192
A função deverá alocar uma matriz de dimensão 3, cada dimensão deverá ter tamanho "tam" e, ao final,
193 194
a função retorna a matriz.

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

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

Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
200
\begin{lstlisting}
201
int* realoca(int *vetor, int tam, int novo_tam);
Jomaro Rodrigues's avatar
Jomaro Rodrigues committed
202
\end{lstlisting}
203

204
Onde "vetor" é o vetor que deve ser realocado, "tam"  é o tamanho velho do vetor e "novo\_tam" é o novo
205 206
tamanho.

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

210
\end{document}