De Objective Caml para C e C++/Construção de tipos: diferenças entre revisões

 
===Ponteiros===
 
====Introdução====
Uma variável C/C++ é a abstração de um trecho de memória que guarda um valor de um certo tipo, onde um trecho de memória é uma seqüëncia de posições contíguas na memória. Um trecho de memória é definido pelo endereço da primeira posição e o número de posições no trecho.
 
Para cada variável, o compilador deve associar algum trecho de memória para aquela variável. Para evitar qualquer problema de superposição e conflito, se o escopo de duas variáveis tem alguma interseção, os trechos de memória aos quais essas variáveis correspondem devem ser disjunAssim, uma variável de tipo <tt>long long int</tt> vai corresponder a um trecho de oito bytes na memória. O tamanho do tipo vai então depender diretamente do tipo de dados a ser representado. Por exemplo, quando temos um tipo registro, o tamanho de memória necessário para a representação de valores desse tipo é a soma dos tamanhos para representar cada campo do registro. Quando temos um tipo arranjo, o tamanho de memória necessário é igual a produto do número de elementos do arranjo com o tamanho necessário para armazenar cada elemento do arranjo.
 
As linguagens C e C++ possuem um operador unário, prefixado, nomeado <tt>sizeof</tt> que, aplicado a uma variável, ou a uma expressão de tipo (entre parênteses), retorna o tamanho em bytes do trecho de memória necessário para representar essa variável, ou valores do dado tipo. O pequeno programa seguinte ilustra os conceitos discutidos:
#include <cstdio>
int main ()
{
int a;
struct point {
double x;
double y;
} p;
float tab [4];
printf("tamanho de a = %lu\n", sizeof a);
printf("tamanho de p = %lu\n", sizeof p);
printf("tamanho de p.x = %lu\n", sizeof p.x);
printf("tamanho de p.y = %lu\n", sizeof p.y);
printf("tamanho de tab = %lu\n", sizeof tab);
printf("tamanho de tab[0] = %lu\n", sizeof tab[0]);
printf("tamanho de tab[1] = %lu\n", sizeof tab[1]);
printf("tamanho de tab[2] = %lu\n", sizeof tab[2]);
printf("tamanho de tab[3] = %lu\n", sizeof tab[3]);
}
A execução desse programa resulte na seguinte impressão na saída padrão:
tamanho de a = 4
tamanho de p = 16
tamanho de p.x = 8
tamanho de p.y = 8
tamanho de tab = 16
tamanho de tab[0] = 4
tamanho de tab[1] = 4
tamanho de tab[2] = 4
tamanho de tab[3] = 4
 
====O operador de endereço====
Em C e C++, há um operador que permite saber qual o endereço de memória associado a uma variável: é o operador <tt>&</tt>. É um operador unário, e é prefixado. Segue um pequeno programa que ilustra o uso desse operador.
#include <cstdio>
int main ()
{
int a;
struct point {
double x;
double y;
} p;
float tab [4];
printf("endereco de a = %p\n", &a);
printf("endereco de p = %p\n", &p);
printf("endereco de p.x = %p\n", &p.x);
printf("endereco de p.y = %p\n", &p.y);
printf("endereco de tab = %p\n", &tab);
printf("endereco de tab[0] = %p\n", &tab[0]);
printf("endereco de tab[1] = %p\n", &tab[1]);
printf("endereco de tab[2] = %p\n", &tab[2]);
printf("endereco de tab[3] = %p\n", &tab[3]);
printf("tab = %p\n", tab);
}
Observe que foi usada a diretiva de formatação <tt>%p</tt> para imprimir um endereço. A execução desse programa resulte na seguinte impressão na saída padrão:
endereco de a = 0xbffff8d8
endereco de p = 0xbffff8e0
endereco de p.x = 0xbffff8e0
endereco de p.y = 0xbffff8e8
endereco de tab = 0xbffff8f0
endereco de tab[0] = 0xbffff8f0
endereco de tab[1] = 0xbffff8f4
endereco de tab[2] = 0xbffff8f8
endereco de tab[3] = 0xbffff8fc
tab = 0xbffff8f0
Algumas observações devem ser feitas:
* os endereços são impressos em base hexadecimal.
* em um registro, campos sucessivos ficam em endereços sucessivos
* o endereço de um registro é o mesmo endereço do primeiro campo do registro
* em um arranjo, posições sucessivas ficam em endereços sucessivos
* o endereço de um arranjo é o mesmo endereço da primeira posição
* um arranjo nada mais é que o endereço da primeira posição desse arranjo (como discutimos no parágrafo sobre [[#Arranjos_como_parâmetros de funções|arranjos como parâmetros de funções]]).
O conceito de ponteiro é fundamental em programação. Embora simples, ele pode ser a fonte de muitas confusões e erros muito súteis em programas. Um valor de algum tipo ponteiro é o (endereço de) uma locação de memória que contem um certo valor. Portanto um ponteiro é algo que indica onde fica algo, digamos então que ''aponta'' para algo.
 
A sintaxe para declarar uma variável de um tipo ponteiro é a seguinte:
tipo * nome;
Nesse caso, <tt>nome</tt> é o nome da variável, e <tt>tipo</tt> é o tipo do valor que está na locação de memória apontada.
 
Em Objective Caml, um conceito similar, embora mais geral, é o de referência. O conceito de referência OCaml corresponde ao de uma variável C/C++ e também ao de ponteiro. Assim, em OCaml, um valor de tipo <tt>int ref<tt> é alguma locação de memória que contem um valor do tipo <tt>int</tt> e que pode ser alterado (dizemos que é ''mutável''). Ou seja é exatamente a mesma coisa que uma variável C ou C++ do tipo int. Tal variável pode ser lida em expressões e alterada com atribuições. O equivalente a um ponteiro C para um inteiro, ou seja a um valor do tipo <tt>int *</tt> seria, em OCaml, um valor do tipo <tt>int ref ref</tt>: corresponde à memorização de um local onde um inteiro é memorizado.
 
===Tipos referências===
187

edições