D Programmierung - Zeiger
D Programmierzeiger sind einfach und machen Spaß zu lernen. Einige D-Programmieraufgaben können einfacher mit Zeigern ausgeführt werden, und andere D-Programmieraufgaben, wie z. B. die dynamische Speicherzuweisung, können ohne sie nicht ausgeführt werden. Ein einfacher Zeiger ist unten gezeigt.
Anstatt direkt auf die Variable zu zeigen, zeigt der Zeiger auf die Adresse der Variablen. Wie Sie wissen, ist jede Variable ein Speicherort, und für jeden Speicherort ist eine Adresse definiert, auf die mit dem kaufmännischen Und (&) -Operator zugegriffen werden kann, der eine Adresse im Speicher angibt. Beachten Sie Folgendes, das die Adresse der definierten Variablen druckt:
import std.stdio;
void main () {
int var1;
writeln("Address of var1 variable: ",&var1);
char var2[10];
writeln("Address of var2 variable: ",&var2);
}
Wenn der obige Code kompiliert und ausgeführt wird, ergibt sich das folgende Ergebnis:
Address of var1 variable: 7FFF52691928
Address of var2 variable: 7FFF52691930
Was sind Zeiger?
EIN pointerist eine Variable, deren Wert die Adresse einer anderen Variablen ist. Wie bei jeder Variablen oder Konstante müssen Sie einen Zeiger deklarieren, bevor Sie damit arbeiten können. Die allgemeine Form einer Zeigervariablendeklaration lautet -
type *var-name;
Hier, typeist der Basistyp des Zeigers; Es muss ein gültiger Programmiertyp sein undvar-nameist der Name der Zeigervariable. Das Sternchen, mit dem Sie einen Zeiger deklariert haben, ist das gleiche Sternchen, das Sie für die Multiplikation verwenden. Jedoch; In dieser Anweisung wird das Sternchen verwendet, um eine Variable als Zeiger zu kennzeichnen. Es folgen die gültigen Zeigerdeklarationen -
int *ip; // pointer to an integer
double *dp; // pointer to a double
float *fp; // pointer to a float
char *ch // pointer to character
Der tatsächliche Datentyp des Werts aller Zeiger, ob Ganzzahl, Gleitkomma, Zeichen oder auf andere Weise, ist derselbe, eine lange Hexadezimalzahl, die eine Speicheradresse darstellt. Der einzige Unterschied zwischen Zeigern verschiedener Datentypen ist der Datentyp der Variablen oder Konstante, auf die der Zeiger zeigt.
Verwenden von Zeigern in der D-Programmierung
Es gibt nur wenige wichtige Operationen, wenn wir die Zeiger sehr häufig verwenden.
Wir definieren Zeigervariablen
Weisen Sie einem Zeiger die Adresse einer Variablen zu
Greifen Sie schließlich auf den Wert an der Adresse zu, die in der Zeigervariablen verfügbar ist.
Dies erfolgt mit einem unären Operator *Dies gibt den Wert der Variablen zurück, die sich an der durch ihren Operanden angegebenen Adresse befindet. Das folgende Beispiel verwendet diese Operationen -
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);
}
Wenn der obige Code kompiliert und ausgeführt wird, ergibt sich das folgende Ergebnis:
Value of var variable: 20
Address stored in ip variable: 7FFF5FB7E930
Value of *ip variable: 20
Nullzeiger
Es wird immer empfohlen, den Zeiger NULL einer Zeigervariablen zuzuweisen, falls Sie keine genaue Adresse haben, die zugewiesen werden soll. Dies erfolgt zum Zeitpunkt der Variablendeklaration. Ein Zeiger, dem null zugewiesen ist, heißt anull Zeiger.
Der Nullzeiger ist eine Konstante mit dem Wert Null, die in mehreren Standardbibliotheken einschließlich iostream definiert ist. Betrachten Sie das folgende Programm -
import std.stdio;
void main () {
int *ptr = null;
writeln("The value of ptr is " , ptr) ;
}
Wenn der obige Code kompiliert und ausgeführt wird, ergibt sich das folgende Ergebnis:
The value of ptr is null
Auf den meisten Betriebssystemen dürfen Programme unter der Adresse 0 nicht auf den Speicher zugreifen, da dieser Speicher vom Betriebssystem reserviert wird. Jedoch; die Speicheradresse 0 hat eine besondere Bedeutung; es signalisiert, dass der Zeiger nicht auf einen zugänglichen Speicherort zeigen soll.
Wenn ein Zeiger den Wert Null (Null) enthält, wird konventionell angenommen, dass er auf nichts zeigt. Um nach einem Nullzeiger zu suchen, können Sie eine if-Anweisung wie folgt verwenden:
if(ptr) // succeeds if p is not null
if(!ptr) // succeeds if p is null
Wenn also alle nicht verwendeten Zeiger den Nullwert erhalten und Sie die Verwendung eines Nullzeigers vermeiden, können Sie den versehentlichen Missbrauch eines nicht initialisierten Zeigers vermeiden. Nicht initialisierte Variablen enthalten häufig einige Junk-Werte, und es wird schwierig, das Programm zu debuggen.
Zeigerarithmetik
Es gibt vier arithmetische Operatoren, die für Zeiger verwendet werden können: ++, -, + und -
Um die Zeigerarithmetik zu verstehen, betrachten wir einen ganzzahligen Zeiger mit dem Namen ptrNehmen wir an, dass 32-Bit-Ganzzahlen vorliegen, und führen Sie die folgende arithmatische Operation für den Zeiger aus:
ptr++
dann ist die ptrzeigt auf die Position 1004, da jedes Mal, wenn ptr inkrementiert wird, es auf die nächste Ganzzahl zeigt. Diese Operation bewegt den Zeiger zum nächsten Speicherort, ohne den tatsächlichen Wert am Speicherort zu beeinflussen.
Wenn ptr zeigt auf ein Zeichen, dessen Adresse 1000 ist, dann zeigt die obige Operation auf die Position 1001, da das nächste Zeichen bei 1001 verfügbar sein wird.
Inkrementieren eines Zeigers
Wir bevorzugen die Verwendung eines Zeigers in unserem Programm anstelle eines Arrays, da der variable Zeiger inkrementiert werden kann, im Gegensatz zum Array-Namen, der nicht inkrementiert werden kann, da es sich um einen konstanten Zeiger handelt. Das folgende Programm erhöht den Variablenzeiger, um auf jedes nachfolgende Element des Arrays zuzugreifen -
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);
}
}
Wenn der obige Code kompiliert und ausgeführt wird, ergibt sich das folgende Ergebnis:
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
Zeiger gegen Array
Zeiger und Arrays sind eng miteinander verbunden. Zeiger und Arrays sind jedoch nicht vollständig austauschbar. Betrachten Sie zum Beispiel das folgende Programm -
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);
}
}
Im obigen Programm sehen Sie var.ptr [2], um das zweite Element festzulegen, und ptr [0], mit dem das nullte Element festgelegt wird. Der Inkrement-Operator kann mit ptr verwendet werden, nicht jedoch mit var.
Wenn der obige Code kompiliert und ausgeführt wird, ergibt sich das folgende Ergebnis:
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
Zeiger auf Zeiger
Ein Zeiger auf einen Zeiger ist eine Form der Mehrfachindirektion oder eine Kette von Zeigern. Normalerweise enthält ein Zeiger die Adresse einer Variablen. Wenn wir einen Zeiger auf einen Zeiger definieren, enthält der erste Zeiger die Adresse des zweiten Zeigers, der auf die Position zeigt, die den tatsächlichen Wert enthält, wie unten gezeigt.
Eine Variable, die ein Zeiger auf einen Zeiger ist, muss als solche deklariert werden. Dazu setzen Sie ein zusätzliches Sternchen vor den Namen. Im Folgenden finden Sie beispielsweise die Syntax zum Deklarieren eines Zeigers auf einen Zeiger vom Typ int -
int **var;
Wenn ein Zeiger auf einen Zeiger indirekt auf einen Zielwert zeigt, muss für den Zugriff auf diesen Wert der Sternchenoperator zweimal angewendet werden, wie im folgenden Beispiel gezeigt.
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);
}
Wenn der obige Code kompiliert und ausgeführt wird, ergibt sich das folgende Ergebnis:
Value of var :3000
Value available at *ptr :3000
Value available at **pptr :3000
Zeiger an Funktionen übergeben
Mit D können Sie einen Zeiger auf eine Funktion übergeben. Dazu deklariert es einfach den Funktionsparameter als Zeigertyp.
Das folgende einfache Beispiel übergibt einen Zeiger auf eine Funktion.
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;
}
Wenn der obige Code zusammen kompiliert und ausgeführt wird, ergibt sich das folgende Ergebnis:
Average is :214.4
Zeiger von Funktionen zurückgeben
Betrachten Sie die folgende Funktion, die 10 Zahlen mit einem Zeiger zurückgibt, bedeutet die Adresse des ersten Array-Elements.
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];
}
Wenn der obige Code kompiliert und ausgeführt wird, ergibt sich das folgende Ergebnis:
*(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
Zeiger auf ein Array
Ein Array-Name ist ein konstanter Zeiger auf das erste Element des Arrays. Daher in der Erklärung -
double balance[50];
balanceist ein Zeiger auf & balance [0], die Adresse des ersten Elements der Array-Bilanz. Somit wird das folgende Programmfragment zugewiesenp die Adresse des ersten Elements von balance - -
double *p;
double balance[10];
p = balance;
Es ist zulässig, Array-Namen als konstante Zeiger zu verwenden und umgekehrt. Daher ist * (Kontostand + 4) eine legitime Möglichkeit, auf die Daten im Kontostand zuzugreifen [4].
Sobald Sie die Adresse des ersten Elements in p gespeichert haben, können Sie mit * p, * (p + 1), * (p + 2) usw. auf Array-Elemente zugreifen. Das folgende Beispiel zeigt alle oben diskutierten Konzepte -
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));
}
}
Wenn der obige Code kompiliert und ausgeführt wird, ergibt sich das folgende Ergebnis:
Array values using pointer
*(p + 0) : 1000
*(p + 1) : 2
*(p + 2) : 3.4
*(p + 3) : 17
*(p + 4) : 50