Programación D - Punteros
Los indicadores de programación D son fáciles y divertidos de aprender. Algunas tareas de programación D se realizan más fácilmente con punteros y otras tareas de programación D, como la asignación de memoria dinámica, no se pueden realizar sin ellos. A continuación se muestra un puntero simple.
En lugar de apuntar directamente a la variable, el puntero apunta a la dirección de la variable. Como sabe, cada variable es una ubicación de memoria y cada ubicación de memoria tiene su dirección definida a la que se puede acceder utilizando el operador y comercial (&) que denota una dirección en la memoria. Considere lo siguiente que imprime la dirección de las variables definidas:
import std.stdio;
void main () {
int var1;
writeln("Address of var1 variable: ",&var1);
char var2[10];
writeln("Address of var2 variable: ",&var2);
}
Cuando se compila y ejecuta el código anterior, produce el siguiente resultado:
Address of var1 variable: 7FFF52691928
Address of var2 variable: 7FFF52691930
¿Qué son los punteros?
UN pointeres una variable cuyo valor es la dirección de otra variable. Como cualquier variable o constante, debe declarar un puntero antes de poder trabajar con él. La forma general de una declaración de variable de puntero es:
type *var-name;
Aquí, typees el tipo base del puntero; debe ser un tipo de programación válido yvar-namees el nombre de la variable de puntero. El asterisco que usó para declarar un puntero es el mismo asterisco que usa para la multiplicación. Sin embargo; en esta declaración, el asterisco se utiliza para designar una variable como puntero. A continuación se muestra la declaración de puntero válida:
int *ip; // pointer to an integer
double *dp; // pointer to a double
float *fp; // pointer to a float
char *ch // pointer to character
El tipo de datos real del valor de todos los punteros, ya sean enteros, flotantes, de caracteres u otros, es el mismo, un número hexadecimal largo que representa una dirección de memoria. La única diferencia entre punteros de diferentes tipos de datos es el tipo de datos de la variable o constante a la que apunta el puntero.
Usando punteros en la programación D
Hay pocas operaciones importantes cuando usamos los punteros con mucha frecuencia.
definimos un puntero variables
asignar la dirección de una variable a un puntero
finalmente acceda al valor en la dirección disponible en la variable de puntero.
Esto se hace usando un operador unario *que devuelve el valor de la variable ubicada en la dirección especificada por su operando. El siguiente ejemplo hace uso de estas operaciones:
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);
}
Cuando se compila y ejecuta el código anterior, produce el siguiente resultado:
Value of var variable: 20
Address stored in ip variable: 7FFF5FB7E930
Value of *ip variable: 20
Punteros nulos
Siempre es una buena práctica asignar el puntero NULL a una variable de puntero en caso de que no tenga una dirección exacta para asignar. Esto se hace en el momento de la declaración de la variable. Un puntero que se asigna nulo se llama unnull puntero.
El puntero nulo es una constante con un valor de cero definido en varias bibliotecas estándar, incluida iostream. Considere el siguiente programa:
import std.stdio;
void main () {
int *ptr = null;
writeln("The value of ptr is " , ptr) ;
}
Cuando se compila y ejecuta el código anterior, produce el siguiente resultado:
The value of ptr is null
En la mayoría de los sistemas operativos, los programas no pueden acceder a la memoria en la dirección 0 porque esa memoria está reservada por el sistema operativo. Sin embargo; la dirección de memoria 0 tiene un significado especial; indica que el puntero no está destinado a apuntar a una ubicación de memoria accesible.
Por convención, si un puntero contiene el valor nulo (cero), se supone que no apunta a nada. Para verificar si hay un puntero nulo, puede usar una declaración if de la siguiente manera:
if(ptr) // succeeds if p is not null
if(!ptr) // succeeds if p is null
Por lo tanto, si todos los punteros no utilizados reciben el valor nulo y evita el uso de un puntero nulo, puede evitar el mal uso accidental de un puntero no inicializado. Muchas veces, las variables no inicializadas contienen algunos valores basura y resulta difícil depurar el programa.
Aritmética de punteros
Hay cuatro operadores aritméticos que se pueden usar en punteros: ++, -, + y -
Para entender la aritmética de punteros, consideremos un puntero entero llamado ptr, que apunta a la dirección 1000. Suponiendo números enteros de 32 bits, realicemos la siguiente operación aritmática en el puntero:
ptr++
entonces la ptrapuntará a la ubicación 1004 porque cada vez que se incrementa ptr, apunta al siguiente entero. Esta operación moverá el puntero a la siguiente ubicación de memoria sin afectar el valor real en la ubicación de memoria.
Si ptr apunta a un carácter cuya dirección es 1000, luego la operación anterior apunta a la ubicación 1001 porque el próximo carácter estará disponible en 1001.
Incrementar un puntero
Preferimos usar un puntero en nuestro programa en lugar de una matriz porque el puntero variable se puede incrementar, a diferencia del nombre de la matriz que no se puede incrementar porque es un puntero constante. El siguiente programa incrementa el puntero variable para acceder a cada elemento sucesivo de la 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);
}
}
Cuando se compila y ejecuta el código anterior, produce el siguiente 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
Punteros vs matriz
Los punteros y las matrices están estrechamente relacionados. Sin embargo, los punteros y las matrices no son completamente intercambiables. Por ejemplo, considere el siguiente 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);
}
}
En el programa anterior, puede ver var.ptr [2] para establecer el segundo elemento y ptr [0] que se usa para establecer el elemento cero. El operador de incremento se puede usar con ptr pero no con var.
Cuando se compila y ejecuta el código anterior, produce el siguiente 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
Puntero a puntero
Un puntero a un puntero es una forma de direccionamiento indirecto múltiple o una cadena de punteros. Normalmente, un puntero contiene la dirección de una variable. Cuando definimos un puntero a un puntero, el primer puntero contiene la dirección del segundo puntero, que apunta a la ubicación que contiene el valor real como se muestra a continuación.
Una variable que es un puntero a un puntero debe declararse como tal. Esto se hace colocando un asterisco adicional delante de su nombre. Por ejemplo, la siguiente es la sintaxis para declarar un puntero a un puntero de tipo int:
int **var;
Cuando un valor objetivo es apuntado indirectamente por un puntero a un puntero, entonces acceder a ese valor requiere que el operador de asterisco se aplique dos veces, como se muestra a continuación en el ejemplo:
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);
}
Cuando se compila y ejecuta el código anterior, produce el siguiente resultado:
Value of var :3000
Value available at *ptr :3000
Value available at **pptr :3000
Pasar puntero a funciones
D le permite pasar un puntero a una función. Para hacerlo, simplemente declara el parámetro de función como un tipo de puntero.
El siguiente ejemplo simple pasa un puntero a una función.
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;
}
Cuando el código anterior se compila y se ejecuta, produce el siguiente resultado:
Average is :214.4
Puntero de retorno de funciones
Considere la siguiente función, que devuelve 10 números usando un puntero, significa la dirección del primer elemento de la 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];
}
Cuando se compila y ejecuta el código anterior, produce el siguiente 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
Puntero a una matriz
Un nombre de matriz es un puntero constante al primer elemento de la matriz. Por lo tanto, en la declaración:
double balance[50];
balancees un puntero a & balance [0], que es la dirección del primer elemento de la matriz balance. Por lo tanto, el siguiente fragmento de programa asignap la dirección del primer elemento de balance -
double *p;
double balance[10];
p = balance;
Es legal utilizar nombres de matrices como punteros constantes y viceversa. Por tanto, * (saldo + 4) es una forma legítima de acceder a los datos en saldo [4].
Una vez que almacene la dirección del primer elemento en p, puede acceder a los elementos de la matriz usando * p, * (p + 1), * (p + 2), etc. El siguiente ejemplo muestra todos los conceptos discutidos anteriormente:
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));
}
}
Cuando se compila y ejecuta el código anterior, produce el siguiente resultado:
Array values using pointer
*(p + 0) : 1000
*(p + 1) : 2
*(p + 2) : 3.4
*(p + 3) : 17
*(p + 4) : 50