Programação D - Ponteiros
Os ponteiros de programação em D são fáceis e divertidos de aprender. Algumas tarefas de programação em D são realizadas mais facilmente com ponteiros, e outras tarefas de programação em D, como alocação de memória dinâmica, não podem ser realizadas sem eles. Um ponteiro simples é mostrado abaixo.
Em vez de apontar diretamente para a variável, o ponteiro aponta para o endereço da variável. Como você sabe, cada variável é um local da memória e cada local da memória tem seu endereço definido, que pode ser acessado usando o operador E comercial (&) que denota um endereço na memória. Considere o seguinte, que imprime o endereço das variáveis definidas -
import std.stdio;
void main () {
int var1;
writeln("Address of var1 variable: ",&var1);
char var2[10];
writeln("Address of var2 variable: ",&var2);
}
Quando o código acima é compilado e executado, ele produz o seguinte resultado -
Address of var1 variable: 7FFF52691928
Address of var2 variable: 7FFF52691930
O que são ponteiros?
UMA pointeré uma variável cujo valor é o endereço de outra variável. Como qualquer variável ou constante, você deve declarar um ponteiro antes de trabalhar com ele. A forma geral de uma declaração de variável de ponteiro é -
type *var-name;
Aqui, typeé o tipo base do ponteiro; deve ser um tipo de programação válido evar-nameé o nome da variável de ponteiro. O asterisco que você usou para declarar um ponteiro é o mesmo asterisco que você usa para a multiplicação. Contudo; nesta declaração, o asterisco está sendo usado para designar uma variável como um ponteiro. A seguir estão as declarações de ponteiro válidas -
int *ip; // pointer to an integer
double *dp; // pointer to a double
float *fp; // pointer to a float
char *ch // pointer to character
O tipo de dados real do valor de todos os ponteiros, seja inteiro, flutuante, caractere ou outro, é o mesmo, um número hexadecimal longo que representa um endereço de memória. A única diferença entre ponteiros de diferentes tipos de dados é o tipo de dados da variável ou constante para a qual o ponteiro aponta.
Usando ponteiros na programação D
Existem algumas operações importantes, quando usamos os ponteiros com muita freqüência.
nós definimos variáveis de ponteiro
atribuir o endereço de uma variável a um ponteiro
finalmente acesse o valor no endereço disponível na variável de ponteiro.
Isso é feito usando o operador unário *que retorna o valor da variável localizada no endereço especificado por seu operando. O exemplo a seguir faz uso dessas operações -
import std.stdio;
void main () {
int var = 20; // actual variable declaration.
int *ip; // pointer variable
ip = &var; // store address of var in pointer variable
writeln("Value of var variable: ",var);
writeln("Address stored in ip variable: ",ip);
writeln("Value of *ip variable: ",*ip);
}
Quando o código acima é compilado e executado, ele produz o seguinte resultado -
Value of var variable: 20
Address stored in ip variable: 7FFF5FB7E930
Value of *ip variable: 20
Ponteiros nulos
É sempre uma boa prática atribuir o ponteiro NULL a uma variável de ponteiro, caso você não tenha um endereço exato a ser atribuído. Isso é feito no momento da declaração da variável. Um ponteiro atribuído a nulo é chamado denull ponteiro.
O ponteiro nulo é uma constante com valor zero definido em várias bibliotecas padrão, incluindo iostream. Considere o seguinte programa -
import std.stdio;
void main () {
int *ptr = null;
writeln("The value of ptr is " , ptr) ;
}
Quando o código acima é compilado e executado, ele produz o seguinte resultado -
The value of ptr is null
Na maioria dos sistemas operacionais, os programas não têm permissão para acessar a memória no endereço 0 porque essa memória é reservada pelo sistema operacional. Contudo; o endereço de memória 0 tem um significado especial; ele sinaliza que o ponteiro não se destina a apontar para um local de memória acessível.
Por convenção, se um ponteiro contém o valor nulo (zero), ele não aponta para nada. Para verificar se há um ponteiro nulo, você pode usar uma instrução if da seguinte maneira -
if(ptr) // succeeds if p is not null
if(!ptr) // succeeds if p is null
Portanto, se todos os ponteiros não usados receberem o valor nulo e você evitar o uso de um ponteiro nulo, poderá evitar o uso incorreto acidental de um ponteiro não inicializado. Muitas vezes, as variáveis não inicializadas contêm alguns valores inúteis e torna-se difícil depurar o programa.
Pointer Arithmetic
Existem quatro operadores aritméticos que podem ser usados em ponteiros: ++, -, + e -
Para entender a aritmética de ponteiros, vamos considerar um ponteiro inteiro chamado ptr, que aponta para o endereço 1000. Assumindo números inteiros de 32 bits, vamos realizar a seguinte operação aritmática no ponteiro -
ptr++
então o ptrirá apontar para a localização 1004 porque cada vez que ptr é incrementado, ele aponta para o próximo inteiro. Esta operação moverá o ponteiro para o próximo local da memória sem afetar o valor real no local da memória.
E se ptr aponta para um personagem cujo endereço é 1000, então a operação acima aponta para o local 1001 porque o próximo caractere estará disponível em 1001.
Incrementando um Ponteiro
Preferimos usar um ponteiro em nosso programa em vez de um array porque o ponteiro variável pode ser incrementado, ao contrário do nome do array que não pode ser incrementado porque é um ponteiro constante. O programa a seguir incrementa o ponteiro variável para acessar cada elemento sucessivo da matriz -
import std.stdio;
const int MAX = 3;
void main () {
int var[MAX] = [10, 100, 200];
int *ptr = &var[0];
for (int i = 0; i < MAX; i++, ptr++) {
writeln("Address of var[" , i , "] = ",ptr);
writeln("Value of var[" , i , "] = ",*ptr);
}
}
Quando o código acima é compilado e executado, ele produz o seguinte resultado -
Address of var[0] = 18FDBC
Value of var[0] = 10
Address of var[1] = 18FDC0
Value of var[1] = 100
Address of var[2] = 18FDC4
Value of var[2] = 200
Ponteiros vs Array
Ponteiros e arrays estão fortemente relacionados. No entanto, ponteiros e matrizes não são completamente intercambiáveis. Por exemplo, considere o seguinte programa -
import std.stdio;
const int MAX = 3;
void main () {
int var[MAX] = [10, 100, 200];
int *ptr = &var[0];
var.ptr[2] = 290;
ptr[0] = 220;
for (int i = 0; i < MAX; i++, ptr++) {
writeln("Address of var[" , i , "] = ",ptr);
writeln("Value of var[" , i , "] = ",*ptr);
}
}
No programa acima, você pode ver var.ptr [2] para definir o segundo elemento e ptr [0] que é usado para definir o elemento zero. O operador de incremento pode ser usado com ptr, mas não com var.
Quando o código acima é compilado e executado, ele produz o seguinte resultado -
Address of var[0] = 18FDBC
Value of var[0] = 220
Address of var[1] = 18FDC0
Value of var[1] = 100
Address of var[2] = 18FDC4
Value of var[2] = 290
Ponteiro para Ponteiro
Um ponteiro para um ponteiro é uma forma de múltiplas vias indiretas ou uma cadeia de ponteiros. Normalmente, um ponteiro contém o endereço de uma variável. Quando definimos um ponteiro para um ponteiro, o primeiro ponteiro contém o endereço do segundo ponteiro, que aponta para a localização que contém o valor real, conforme mostrado abaixo.
Uma variável que é um ponteiro para um ponteiro deve ser declarada como tal. Isso é feito colocando um asterisco adicional na frente de seu nome. Por exemplo, a seguir está a sintaxe para declarar um ponteiro para um ponteiro do tipo int -
int **var;
Quando um valor de destino é indiretamente apontado por um ponteiro para um ponteiro, o acesso a esse valor requer que o operador asterisco seja aplicado duas vezes, como é mostrado abaixo no exemplo -
import std.stdio;
const int MAX = 3;
void main () {
int var = 3000;
writeln("Value of var :" , var);
int *ptr = &var;
writeln("Value available at *ptr :" ,*ptr);
int **pptr = &ptr;
writeln("Value available at **pptr :",**pptr);
}
Quando o código acima é compilado e executado, ele produz o seguinte resultado -
Value of var :3000
Value available at *ptr :3000
Value available at **pptr :3000
Passando Ponteiro para Funções
D permite que você passe um ponteiro para uma função. Para fazer isso, ele simplesmente declara o parâmetro da função como um tipo de ponteiro.
O exemplo simples a seguir passa um ponteiro para uma função.
import std.stdio;
void main () {
// an int array with 5 elements.
int balance[5] = [1000, 2, 3, 17, 50];
double avg;
avg = getAverage( &balance[0], 5 ) ;
writeln("Average is :" , avg);
}
double getAverage(int *arr, int size) {
int i;
double avg, sum = 0;
for (i = 0; i < size; ++i) {
sum += arr[i];
}
avg = sum/size;
return avg;
}
Quando o código acima é compilado e executado, ele produz o seguinte resultado -
Average is :214.4
Return Pointer from Functions
Considere a seguinte função, que retorna 10 números usando um ponteiro, significa o endereço do primeiro elemento da matriz.
import std.stdio;
void main () {
int *p = getNumber();
for ( int i = 0; i < 10; i++ ) {
writeln("*(p + " , i , ") : ",*(p + i));
}
}
int * getNumber( ) {
static int r [10];
for (int i = 0; i < 10; ++i) {
r[i] = i;
}
return &r[0];
}
Quando o código acima é compilado e executado, ele produz o seguinte resultado -
*(p + 0) : 0
*(p + 1) : 1
*(p + 2) : 2
*(p + 3) : 3
*(p + 4) : 4
*(p + 5) : 5
*(p + 6) : 6
*(p + 7) : 7
*(p + 8) : 8
*(p + 9) : 9
Ponteiro para uma matriz
Um nome de array é um ponteiro constante para o primeiro elemento do array. Portanto, na declaração -
double balance[50];
balanceé um ponteiro para & balance [0], que é o endereço do primeiro elemento da matriz balance. Assim, o seguinte fragmento de programa atribuip o endereço do primeiro elemento de balance -
double *p;
double balance[10];
p = balance;
É legal usar nomes de array como ponteiros constantes e vice-versa. Portanto, * (saldo + 4) é uma forma legítima de acessar os dados em saldo [4].
Depois de armazenar o endereço do primeiro elemento em p, você pode acessar os elementos do array usando * p, * (p + 1), * (p + 2) e assim por diante. O exemplo a seguir mostra todos os conceitos discutidos acima -
import std.stdio;
void main () {
// an array with 5 elements.
double balance[5] = [1000.0, 2.0, 3.4, 17.0, 50.0];
double *p;
p = &balance[0];
// output each array element's value
writeln("Array values using pointer " );
for ( int i = 0; i < 5; i++ ) {
writeln( "*(p + ", i, ") : ", *(p + i));
}
}
Quando o código acima é compilado e executado, ele produz o seguinte resultado -
Array values using pointer
*(p + 0) : 1000
*(p + 1) : 2
*(p + 2) : 3.4
*(p + 3) : 17
*(p + 4) : 50