D 程式設計 - 指標



D 程式設計指標易於學習且趣味十足。一些 D 程式設計任務使用指標更容易完成,而其他 D 程式設計任務(例如動態記憶體分配)則無法在沒有指標的情況下完成。下面顯示了一個簡單的指標。

Pointer in D

指標並非直接指向變數,而是指向變數的地址。眾所周知,每個變數都是一個記憶體位置,每個記憶體位置都有其定義的地址,可以使用表示記憶體地址的取地址符 (&) 運算子訪問。考慮以下列印已定義變數地址的程式碼:

import std.stdio;
 
void main () { 
   int var1; 
   writeln("Address of var1 variable: ",&var1);  
   
   char var2[10]; 
   writeln("Address of var2 variable: ",&var2); 
}

編譯並執行上述程式碼後,將產生以下結果:

Address of var1 variable: 7FFF52691928 
Address of var2 variable: 7FFF52691930

什麼是指標?

指標是一個變數,其值是另一個變數的地址。像任何變數或常量一樣,必須在使用指標之前宣告它。指標變數宣告的一般形式為:

type *var-name;

這裡,type 是指標的基本型別;它必須是有效的程式設計型別,而 var-name 是指標變數的名稱。用於宣告指標的星號與用於乘法的星號相同。但是,在此語句中,星號用於將變數指定為指標。以下是有效的指標宣告:

int    *ip;    // pointer to an integer 
double *dp;    // pointer to a double 
float  *fp;    // pointer to a float 
char   *ch     // pointer to character

所有指標的實際資料型別,無論是整數、浮點數、字元還是其他型別,都是相同的,即表示記憶體地址的長十六進位制數。不同資料型別指標之間的唯一區別在於指標指向的變數或常量的型別。

在 D 程式設計中使用指標

使用指標時,有一些重要的操作。

  • 我們定義指標變數

  • 將變數的地址賦給指標

  • 最後訪問指標變數中地址的值。

這是透過使用一元運算子 * 來完成的,它返回位於其運算元指定的地址處的變數的值。以下示例使用了這些操作:

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); 
}

編譯並執行上述程式碼後,將產生以下結果:

Value of var variable: 20 
Address stored in ip variable: 7FFF5FB7E930 
Value of *ip variable: 20

空指標

如果您沒有確切的地址要賦值,則始終建議將指標賦值為 NULL。這是在變數宣告時完成的。賦值為空的指標稱為指標。

空指標是在包括 iostream 在內的多個標準庫中定義的值為零的常量。考慮以下程式:

import std.stdio;

void main () { 
   int  *ptr = null; 
   writeln("The value of ptr is " , ptr) ;  
}

編譯並執行上述程式碼後,將產生以下結果:

The value of ptr is null

在大多數作業系統上,程式不允許訪問地址 0 處的記憶體,因為該記憶體由作業系統保留。但是,記憶體地址 0 具有特殊的意義;它表示指標並非旨在指向可訪問的記憶體位置。

按照約定,如果指標包含空(零)值,則假定它不指向任何內容。可以使用 if 語句檢查空指標,如下所示:

if(ptr)     // succeeds if p is not null 
if(!ptr)    // succeeds if p is null

因此,如果所有未使用的指標都被賦予空值,並且避免使用空指標,則可以避免意外錯誤地使用未初始化的指標。許多時候,未初始化的變數包含一些垃圾值,這使得程式除錯變得困難。

指標運算

有四個算術運算子可以用於指標:++、--、+ 和 -

為了理解指標運算,讓我們考慮一個名為 ptr 的整數指標,它指向地址 1000。假設 32 位整數,讓我們對指標執行以下算術運算:

ptr++ 

ptr 將指向位置 1004,因為每次遞增 ptr 時,它都會指向下一個整數。此操作將指標移動到下一個記憶體位置,而不會影響記憶體位置的實際值。

如果 ptr 指向地址為 1000 的字元,則上述操作指向位置 1001,因為下一個字元將在 1001 處可用。

遞增指標

我們更傾向於在程式中使用指標而不是陣列,因為變數指標可以遞增,而陣列名則不能遞增,因為它是一個常量指標。以下程式遞增變數指標以訪問陣列的每個後續元素:

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); 
   } 
}

編譯並執行上述程式碼後,將產生以下結果:

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

指標與陣列

指標和陣列密切相關。但是,指標和陣列並非完全可以互換。例如,考慮以下程式:

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); 
   } 
}

在上述程式中,您可以看到 var.ptr[2] 用於設定第二個元素,而 ptr[0] 用於設定第零個元素。遞增運算子可用於 ptr,但不能用於 var。

編譯並執行上述程式碼後,將產生以下結果:

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

指向指標的指標

指向指標的指標是多重間接定址或指標鏈的一種形式。通常,指標包含變數的地址。當我們定義指向指標的指標時,第一個指標包含第二個指標的地址,第二個指標指向包含實際值的位置,如下所示。

C++ Pointer to Pointer

作為指向指標的指標的變數必須宣告為這種型別。這是透過在其名稱前面新增一個額外的星號來完成的。例如,以下是宣告指向 int 型別指標的指標的語法:

int **var; 

當目標值由指向指標的指標間接指向時,訪問該值需要兩次應用星號運算子,如下面的示例所示:

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); 
}

編譯並執行上述程式碼後,將產生以下結果:

Value of var :3000 
Value available at *ptr :3000 
Value available at **pptr :3000

將指標傳遞給函式

D 允許您將指標傳遞給函式。為此,它只需將函式引數宣告為指標型別。

以下簡單示例將指標傳遞給函式。

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; 
}

編譯並執行上述程式碼後,將產生以下結果:

Average is :214.4 

從函式返回指標

考慮以下函式,它使用指標返回 10 個數字,這意味著第一個陣列元素的地址。

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]; 
}

編譯並執行上述程式碼後,將產生以下結果:

*(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

指向陣列的指標

陣列名是指向陣列第一個元素的常量指標。因此,在宣告中:

double balance[50];

balance 是指向 &balance[0] 的指標,它是陣列 balance 的第一個元素的地址。因此,以下程式片段將 p 賦值為 balance 的第一個元素的地址:

double *p; 
double balance[10]; 
 
p = balance;

將陣列名用作常量指標以及反過來都是合法的。因此,*(balance + 4) 是訪問 balance[4] 處資料的合法方法。

一旦將第一個元素的地址儲存在 p 中,就可以使用 *p、*(p+1)、*(p+2) 等訪問陣列元素。以下示例顯示了上述所有概念:

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)); 
   } 
}

編譯並執行上述程式碼後,將產生以下結果:

Array values using pointer  
*(p + 0) : 1000 
*(p + 1) : 2 
*(p + 2) : 3.4 
*(p + 3) : 17
*(p + 4) : 50
廣告