A força das duas linguagens, no entanto, é também seu calcanhar de Aquiles. Muitos programadores temem C/C++ justamente por causa dessa proximidade com a linguagem de máquina, personificada no uso de um recurso-símbolo : os ponteiros. Alguns fatores contribuem para esse temor mas acredito que o principal é a falta de conhecimento sobre a sua real utilidade e funcionamento.
Então, pra começar, o que são exatamente os "ponteiros" e pra que servem ? Um ponteiro é uma variável cujo conteúdo é um endereço na memória do computador. Em outras palavras, é uma posição de memória que "aponta" para outra posição de memória. Com um diagrama fica mais fácil visualizar o conceito:
No quadro acima, nome é uma variável do tipo "ponteiro para char". Isto é, o conteúdo dela indica um local na memória do computador que será usado para armazenar caracteres. O que está acontecendo no programa retratado é uma alocação dinâmica de memória, onde o sistema operacional faz para nós a reserva de uma posição da memória e coloca o endereço reservado na nossa variável. Podemos, então, usar esse ponteiro para acessar o conteúdo da memória reservada.
Os ponteiros, portanto, flexibilizam o desenho de uma aplicação, permitindo a ela alocar memória dinamicamente para tratar situações nas quais não sabemos de antemão com quantos elementos teremos que lidar. Isso também permite adiar a alocação até que os elementos sejam efetivamente necessários, reduzindo o consumo de memória. Ainda, no caso de classes, a solução para o tratamento de heranças é uma implementação fortemente calcada em ponteiros.
Uma outra característica dos ponteiros é que eles facilitam o fluxo das informações dentro do programa; sem eles, passar estruturas grandes como parâmetro exigiria a movimentação de grande quantidade de bytes enquanto com eles é necessário trafegar apenas os bytes que carregam o endereço de memória (a quantidade exata depende do tipo de arquitetura do sistema mas em Win32 são 4 bytes).
Isso tudo levanta, então, outras questões: quando usar exclusivamente o ponteiro no meu código, quando e como acessar o conteúdo e que sintaxe usar para diferenciar ambos os casos ?
Na figura no início do post aparece o uso mais comum dos ponteiros, que é manter referência a alguma memória alocada dinamicamente. Obviamente, armazenar o endereço alocado é imprescindível para que possamos acessar o seu conteúdo e também para devolver a memória ao sistema operacional quando não formos mais usá-la.
typedef struct {
bool Assigned;
long Id;
char Nome[51];
double Saldo;
} TWPessoa;
TWPessoa *lPessoa = new TWPessoa;
/* ... */
delete lPessoa;
lPessoa = NULL;
bool Assigned;
long Id;
char Nome[51];
double Saldo;
} TWPessoa;
TWPessoa *lPessoa = new TWPessoa;
/* ... */
delete lPessoa;
lPessoa = NULL;
O valor NULL é um ponteiro especial para indicar que uma variável não está apontando para lugar algum, isto é, que a variável não está alocada.
Como regra, se o nome da variável aparece sozinho no código, sem decorações (como pontos, setas ou asteriscos) então estamos trabalhando com a variável ponteiro. Nesta situação, o que temos é o endereço de memória para onde nossa variável aponta. É o que está acontecendo no quadro anterior, onde a memória para a estrutura é alocada e o endereço que o sistema operacional reservou para ela é jogado em nossa variável.
A passagem de um ponteiro por parâmetro é outro uso corriqueiro. Formalmente, isso é conhecido como "passagem por referência", o que significa que apenas o endereço é trafegado. É por essa razão que as alterações feitas ao conteúdo de um parâmetro passado por referência são enxergadas também fora da função: ambos os lados apontam exatamente o mesmo endereço na memória.
Quando há alguma decoração anexada à variável no código, então estamos nos referindo ao conteúdo apontado por essa variável. Em C/C++, há várias sintaxes diferentes para acessar o conteúdo apontado, como mostra o trecho abaixo:
/* As 3 linhas abaixo fazem exatamente a mesma coisa :
Vai até a posição de memória "apontada" por lPessoa;
No local reservado para a variável Assigned, coloque o valor true. */
lPessoa->Assigned = true;
lPessoa[0].Assigned = true;
(*lPessoa).Assigned = true;
Vai até a posição de memória "apontada" por lPessoa;
No local reservado para a variável Assigned, coloque o valor true. */
lPessoa->Assigned = true;
lPessoa[0].Assigned = true;
(*lPessoa).Assigned = true;
O C/C++ ainda tem um sintaxe para que você possa recuperar o endereço de uma variável de alocação estática, independentemente se é um tipo atômico (int, double, char, etc.) ou um tipo criado pelo usuário (struct, class). Poder fazer essa recuperação é útil, por exemplo, para evitar trafegar o dado estático. Novamente, aqui você só trafegaria os bytes relativos ao ponteiro:
TWPessoa lPessoaEstatica;
ResetaPessoa (&lPessoaEstatica);
ResetaPessoa (&lPessoaEstatica);
Pelo rumo dessa discussão, percebemos que qualquer que seja a linguagem utilizada por você, com certeza ela utiliza ponteiros internamente - mesmo que de forma disfarçada - já que eles são a base para o acesso à memória do computador. Assim, conhecer e utilizar bem esse recurso é um excelente começo para construir bons programas em qualquer linguagem.
Nenhum comentário :
Postar um comentário
OBS: Os comentários enviados a este Blog são submetidos a moderação. Por isso, eles serão publicados somente após aprovação.
Observação: somente um membro deste blog pode postar um comentário.