A maior rede de estudos do Brasil

Grátis
275 pág.
Apostila C

Pré-visualização | Página 33 de 45

sa˜o guardados na memo´ria, e´
possı´vel criar um ponteiro que aponta para o enderec¸o de outro ponteiro.
A linguagem C permite criar ponteiros com diferentes nı´veis
de apontamento, isto e´, ponteiros que apontam para outros
ponteiros.
Em linguagem C, a declarac¸a˜o de um ponteiro para ponteiro pelo progra-
mador segue a seguinte forma geral:
tipo do ponteiro **nome do ponteiro;
Note que agora usamos dois asteriscos (*) para informar ao compilador
que aquela varia´vel na˜o vai guardar um valor, mas sim um enderec¸o de
memo´ria para outro enderec¸o de memo´ria para aquele tipo especificado.
Para ficar mais claro, veja o exemplo abaixo:
Exemplo: ponteiro para ponteiro.
1 #include <s t d i o . h>
2 #include <s t d l i b . h>
3 i n t main ( ) {
4 i n t x = 10;
5 i n t ∗p = &x ;
6 i n t ∗∗p2 = &p ;
7 / / Enderec¸o em p2
8 p r i n t f ( ‘ ‘ Endereco em p2 : %
p\n ’ ’ , p2 ) ;
9 / / Conteudo do enderec¸o
10 p r i n t f ( ‘ ‘ Conteudo em ∗p2 :
%p\n ’ ’ ,∗p2 ) ;
11 / / Conteudo do enderec¸o do
enderec¸o
12 p r i n t f ( ‘ ‘ Conteudo em ∗∗p2 :
%d\n ’ ’ ,∗∗p2 ) ;
13 system ( ‘ ‘ pause ’ ’ ) ;
14 return 0;
15 }
No exemplo acima, foi declarado um ponteiro que aponta para outro pon-
teiro (p2). Nesse caso, esse ponteiro guarda o enderec¸o de um segundo
ponteiro (linha 8, enderec¸o de p), que por sua vez guarda o enderec¸o de
uma varia´vel. Assim, se tentarmos acessar o conteu´do do ponteiro (*p2),
208
iremos acessar o enderec¸o guardado dentro do ponteiro (p), que nada
mais e´ do que o enderec¸o da varia´vel x (linha 10). Como o p2 e´ um pon-
teiro para ponteiro, isso significa que podemos acessar o seu conteu´do
duas vezes. Afinal, seu conteu´do (*p2) e´ um enderec¸o. Assim, o comando
**p2 acessa o conteu´do do enderec¸o do enderec¸o apontado por (p2), isto
e´, a varia´vel x (linha 12).
Em um ponteiro para ponteiro, o primeiro ponteiro conte´m
o enderec¸o do segundo ponteiro que aponta para uma
varia´vel com o valor desejado.
A linguagem C permite ainda criar um ponteiro que aponte para outro pon-
teiro, que aponte para outro ponteiro, etc, criando assim diferentes nı´veis
de apontamento ou enderec¸amento. Com isso, podemos criar um ponteiro
para ponteiro, ou, um ponteiro para ponteiro para ponteiro, e assim por
diante.
E´ a quantidade de asteriscos (*) na declarac¸a˜o do ponteiro
que indica o nu´mero de nı´veis de apontamento do ponteiro.
1 #include <s t d i o . h>
2 #include <s t d l i b . h>
3 i n t main ( ) {
4 / / v a r i a´ v e l i n t e i r a
5 i n t x ;
6 / / pon te i ro para um i n t e i r o (1 n ı´ v e l )
7 i n t ∗p1 ;
8 / / pon te i ro para pon te i ro de i n t e i r o (2 n ı´ v e i s )
9 i n t ∗∗p2 ;
10 / / pon te i ro para pon te i ro para pon te i ro de
i n t e i r o (3 n ı´ v e i s )
11 i n t ∗∗∗p3 ;
12 system ( ‘ ‘ pause ’ ’ ) ;
13 return 0;
14 }
Consequentemente, devemos respeitar a quantidade de asteriscos (*) utili-
zados na declarac¸a˜o do ponteiro para acessar corretamente o seu conteu´do,
como mostra o exemplo abaixo:
209
Acessando o conteu´do de um ponteiro para ponteiro.
1 #include <s t d i o . h>
2 #include <s t d l i b . h>
3 i n t main ( ) {
4 char l e t r a = ’ a ’ ;
5 char ∗ptrChar = & l e t r a ;
6 char ∗∗ p t rP t rChar = &
ptrChar ;
7 char ∗∗∗ p t r P t r = &
pt rP t rChar ;
8 p r i n t f ( ‘ ‘ Conteudo em ∗
ptrChar : %c\n ’ ’ ,∗
ptrChar ) ;
9 p r i n t f ( ‘ ‘ Conteudo em ∗∗
p t rP t rChar : %c\n ’ ’ ,∗∗
p t rP t rChar ) ;
10 p r i n t f ( ‘ ‘ Conteudo em ∗∗∗
p t r P t r : %c\n ’ ’ ,∗∗∗
p t r P t r ) ;
11 system ( ‘ ‘ pause ’ ’ ) ;
12 return 0;
13 }
A linguagem C permite que se crie um ponteiro com um nu´mero infinito
de nı´veis de apontamento. Pore´m, na pra´tica, deve-se evitar trabalhar com
muitos nı´veis de apontamento. Isso ocorre por que cada nova nı´vel de
apontamento adicionada aumenta a complexidade em lidar com aquele
ponteiro e, consequentemente, dificulta a compreensa˜o dos programas,
causando assim confusa˜o e facilitando o surgimento de erros.
210
10 ALOCAC¸A˜O DINAˆMICA
Sempre que escrevemos um programa, e´ preciso reservar espac¸o para os
dados que sera˜o processados. Para isso usamos as varia´veis.
Uma varia´vel e´ uma posic¸a˜o de memo´ria previamente re-
servada e que pode ser usada para armazenar algum
dado.
Uma varia´vel e´ uma posic¸a˜o de memo´ria que armazena um dado que pode
ser usado pelo programa. No entanto, por ser uma posic¸a˜o previamente
reservada, uma varia´vel deve ser declarada durante o desenvolvimento do
programa.
Toda varia´vel deve ser declarada antes de ser usada.
Infelizmente, nem sempre e´ possı´vel saber o quanto de memo´ria um pro-
grama ira´ precisar.
Imagine o seguinte problema: precisamos construir um
programa que processe os valores dos sala´rios dos fun-
ciona´rios de uma pequena empresa.
Uma soluc¸a˜o simples para resolver esse problema poderia ser declarar um
array do tipo float bem grande com, por exemplo, umas 1.000 posic¸o˜es:
float salarios[1000];
Esse array parece uma soluc¸a˜o possı´vel para o problema. Infelizmente,
essa soluc¸a˜o possui dois problemas:
• Se a empresa tiver menos de 1.000 funciona´rios: esse array sera´ um
exemplo de desperdı´cio de memo´ria. Um array de 1.000 posic¸o˜es e´
declarado quando na˜o se sabe, de fato, se as 1.000 posic¸oes sera˜o
necessa´rias;
• Se a empresa tiver mais de 1.000 funciona´rios: esse array sera´ insu-
ficiente para lidar com s dados de todos os funciona´rios. O programa
na˜o atende as necessidades da empresa.
211
Na declarac¸a˜o de uma array, e´ dito para reservar uma certa quantidade
de memo´ria para armazenar os elementos do array. Pore´m, neste modo
de declarac¸a˜o, a quantidade de memo´ria reservada deve ser fixa. Surge
enta˜o a necessidade de se utilizar ponteiros juntos com arrays.
Um ponteiro e´ uma varia´vel que guarda o enderec¸o de um
dado na memo´ria.
Ale´m disso, e´ importante lembrar que arrays sa˜o agrupamentos sequen-
ciais de dados de um mesmo tipo na memo´ria.
O nome do array e´ apenas um ponteiro que aponta para
o primeiro elemento do array.
A linguagem C permite alocar (reservar) dinamicamente (em tempo de
execuc¸a˜o) blocos de memo´rias utilizando ponteiros. A esse processo da´-
se o nome de alocac¸a˜o dinaˆmica. A alocac¸a˜o dinaˆmica permite ao pro-
gramador “criar” arrays em tempo de execuc¸a˜o, ou seja, alocar memo´ria
para novos arrays quando o programa esta´ sendo executado, e na˜o ape-
nas quando se esta´ escrevendo o programa. Ela e´ utilizada quando na˜o se
sabe ao certo quanto de memo´ria sera´ necessa´rio para armazenar os da-
dos com que se quer trabalhar. Desse modo, pode-se definir o tamanho do
array em tempo de execuc¸a˜o, evitando assim o desperdı´cio de memo´ria.
A alocac¸a˜o dinaˆmica consiste em requisitar um espac¸o
de memo´ria ao computador, em tempo de execuc¸a˜o, o
qual devolve para o programa o enderec¸o do inı´cio desse
espac¸o alocado usando um ponteiro.
212
10.1 FUNC¸O˜ES PARA ALOCAC¸A˜O DE MEMO´RIA
A linguagem C ANSI usa apenas 4 func¸o˜es para o sistema de alocac¸a˜o
dinaˆmica, disponı´veis na biblioteca stdlib.h. Sa˜o elas:
• malloc
• calloc
• realloc
• free
Ale´m dessas func¸o˜es, existe tambe´m a func¸a˜o sizeof que auxilia as de-
mais func¸o˜es no processo de alocac¸a˜o de memo´ria. A seguir, sera˜o apre-
sentados os detalhes necessa´rios para um programador usar alocac¸a˜o
dinaˆmica em seu programa.
10.1.1 SIZEOF()
No momento da alocac¸a˜o da memo´ria, deve-se levar em conta o tamanho
do dado alocado.
Alocar memo´ria para um elemento do tipo int e´ diferente
de alocar memo´ria para um elemento do tipo float.
Isso ocorre pois tipos diferentes podem ter tamanhos diferentes na memo´ria.
O tipo float, por exemplo, ocupa mais espac¸o na memo´ria que o tipo int.
A func¸a˜o sizeof() e´ usada para saber o nu´mero de bytes
necessa´rios para alocar um u´nico elemento de um deter-
minado tipo de dado.
A func¸a˜o sizeof() e´ usada para se saber o tamanho em bytes de varia´veis
ou de tipos. Ela pode ser usada de duas formas:
sizeof nome da varia´vel
sizeof (nome