跳轉到內容

C 語言中的多型資料結構/指標

來自 Wikibooks,開放的書籍,為開放的世界

← C 結構體簡介 | 遞迴 →

指標是 C 語言中最基本的構造之一。指標是一個變數,它儲存另一個變數的地址(在記憶體中),供函式引用。透過使用指標,我們能夠實現更高層次的資料操作,因為在 C 語言中,沒有開發出任何演算法支援在建立它們的函式之外的函式中修改靜態變數。

簡單的指標操作

[編輯 | 編輯原始碼]

開發人員可以使用指標運算子 (*) 顯式宣告指標

int *p_int ;

本書中使用的標準約定是在被指向的變數前面加上 "p_",以表示它是指標。這在函式中存在多個相同型別但有些是指標,有些不是指標時很有用。要訪問指標指向的資訊,程式設計師必須使用解引用運算子(也是星號)來表示引用。

printf( "%d" , *p_int ) ;

如果一個變數不是指標,但需要在被呼叫的函式內部修改,那麼可以使用地址運算子 (&) 來建立一個 "即時" 指標傳遞給函式。許多程式設計師理解 scanf() 函式需要在讀取的變數前面加上地址運算子,但並非所有程式設計師都知道原因。這是因為 scanf() 需要影響儲存資訊的變數。

指標通常用於跟蹤動態分配的記憶體。在本演示中,指標是多餘的,但它將說明指標的原理。

#include <stdio.h>

int main( int argc , char *argv[] ) {

   int i ;
   int *p_i = &i ;

   i = 5 ;
   printf( "%d " , i ) ;

   *p_i = 7 ;
   printf( "%d\n" , *p_i ) ;

   return 0 ;

}

首先,建立一個整數 i,它是 main() 的靜態變數。然後,建立指標 p_i 並將其值設定為 i 的地址。接下來,將 i 的值賦值為 5 並列印。最後,將 p_i 指向的 整數賦值為 7 並列印。

注意解引用和地址運算子的使用。正確使用指標、解引用和地址運算子是理解多型的關鍵。

動態記憶體分配

[編輯 | 編輯原始碼]

指標最重要的用途可能是跟蹤動態分配(在執行時)的記憶體。記憶體使用 malloc() 函式動態分配。 malloc() 只有一個引數(要分配的空間大小,以位元組為單位),並返回指向 void 的指標,該指標通常被重新強制轉換為指向其他型別的指標。動態記憶體分配的挑戰在於型別大小可能依賴於機器,這意味著一臺計算機上的整數在記憶體中的大小可能與另一臺計算機上的整數不同。為了克服這個問題,C 描述了一個 sizeof 運算子,它接受一個型別名稱作為引數,並返回該型別的大小(以位元組為單位),作為一個整數。例如,要為一個字元動態分配記憶體空間,開發人員可以編寫

char *p_char ;
p_char = (char *)malloc( sizeof ( char ) ) ;

更復雜地說,動態記憶體分配通常用於編譯時不知道要保留的空間大小的情況。在下面的程式中,系統會詢問使用者他們想要儲存多少個整數,並存儲它們。

#include <stdio.h>
#include <stdlib.h>

int main( int argc , char *argv[] ) {

   int i , amount , *p_int ;

   printf( "How many integers would you like to store? " ) ;
   scanf( "%d" , &amount ) ;

   p_int = (int *)malloc( sizeof ( int ) * amount ) ;

   printf( "Please enter %d integers, separated with a space, to store. " , amount ) ;
   for ( i = 0 ; i < amount ; i++ ) scanf( "%d" , p_int[i] ) ;

   for ( i = 0 ; i < amount ; i++ ) printf( "%d " , p_int[i] ) ;
   printf( "\n" ) ;

   return 0 ;

}

注意 stdlib 是如何在標頭檔案中包含的。malloc()stdlib.h 中宣告,因此它必須與 stdio.h 一起包含在使用它的程式中。

還要注意 malloc() 保留的空間是如何使用陣列的標準語法引用的。這是因為 malloc() 按順序保留空間(全部排成一行),因此可以將指標運算(下面討論)應用於它。換句話說,當 malloc() 為一個給定型別的多個元素保留空間時,它會為該型別的陣列分配空間。

結構體上的指標操作

[編輯 | 編輯原始碼]

除了標準成員運算子 (.) 之外,ANSI C 還包括一個不同的成員運算子符號 ->。它同時執行兩個操作:它解引用運算子前面的結構體名稱,並計算其成員(運算子後面的)。這在使用指標引用結構體時很有用。使用上一章中的員工資料結構

struct employee_data *p_search ;
p_search = (struct employee_data *)malloc( sizeof ( struct employee_data ) ) ;

strcpy(p_search -> name, "Ryan");
strcpy((*p_search).name, "Ryan");

最下面的兩個語句執行完全相同的操作。因為在本書的絕大部分內容中都會使用指向結構體的指標,所以會更多地使用解引用-成員運算子。請注意,子成員仍然必須使用正常的成員運算子引用。

使用指標返回函式值

[編輯 | 編輯原始碼]

C 語言中的函式只能返回一個值。這是程序式程式設計正規化的必然結果。但是,有時需要更改多個變數。為此,必須將指向該變數的指標傳遞給被呼叫的函式。有關此主題的更多資訊,請參閱附錄 1,函式呼叫。那裡的資訊非常寶貴,請務必在繼續之前仔細閱讀。本書的其餘部分假設讀者已經閱讀並理解了附錄 1 的內容。

指標運算

[編輯 | 編輯原始碼]

執行指標運算是一個非常簡單的概念,但實際操作起來有些困難。對陣列進行指標加法是隱式完成的,使用下標運算子 []。以下語句將返回 TRUE

int A[4] ;
( A[2] == *(A + 2) ) ;

對指標進行加法會導致引用地址 "向前移動" 指定的單位數,乘以型別的尺寸。因此,&A[2] 是 A[0] 的位置,加上 兩個整數在記憶體中的大小(從而得到陣列中的第三個元素,因為陣列從索引 0 開始)。指標運算也可以應用於聯合體和結構體,但這種方法不像成員引用那樣常用。

指向函式的指標

[編輯 | 編輯原始碼]

C 語言的一個非常強大的功能是能夠獲取函式的地址並將其儲存為指標。這樣,理論上可以編寫一個函式,然後讓該函式呼叫一個僅在執行時才知道的另一個函式(從而減少了所需的條件程式設計量)。每個 C 程式設計師都熟悉標準函式宣告語法

return_type function( parameter_1 , parameter_2 ... ) ;

與變數類似,函式也有型別。假設它們不是 void 型別,所有函式都會返回資料,並且該資料必須有型別。作為這種結構的副作用,程式設計師可以宣告指向函式的指標。

return_type (*p_function)( parameter_1 , parameter_2 ... ) ;

此程式碼可用於在同一行程式碼中執行任意數量的函式,在本文件中的文字處理程式中演示。

#include <stdio.h>

void print_reverse( char *string ) ;
void print_normal( char *string ) ;
int main( int argc , char *argv[] ) {

   char input_string[32] ;
   int state , i ;
   void (*p_function)( char *string ) ;

   for ( i = 0 ; i < 32 ; i++ )
      input_string[i] = '\0' ;

   printf( "Please enter a short line of text: " ) ;
   scanf( "%s" , input_string ) ;

   printf( "Would you like to print this string in reverse? 1 for yes, 0 for no. " ) ;
   scanf( "%d" , &state ) ;

   if ( state == 1 )
      p_function = print_reverse ;
   else if ( state == 0 )
      p_function = print_normal ;
   else {
      printf( "Error: Must enter 0 or 1. Exiting." ) ;
      exit 1; 
   }

   p_function( input_string ) ;

   return 0 ;

}
void print_reverse( char *string ) {

   int i ;

   for ( i = 31 ; i >= 0 ; i-- ) {
      if ( string[i] != '\0' )
         putchar( string[i] ) ;
   }

   printf( "\n" ) ;

}
void print_normal( char *string ) {

   int i = 0 ;

   while ( ( i < 32 ) && ( string[i] != '\0' ) ) {
      putchar( string[i] ) ;
      i++ ;
   }

   printf( "\n" ) ;

}

在這個程式中,有兩個函式,print_reverse()print_normal()。一個只是列印字串,另一個則以相反順序列印字串的內容。但是,main() 中的實際函式呼叫在這兩種情況下都是一樣的

p_function( input_string ) ;

這是透過 C 語言能夠透過指向該函式的指標引用函式,並正確地將引數傳遞給該函式而實現的。

華夏公益教科書