C語言指標作為引數和返回值

2020-07-16 10:04:27
C 語言本質上是傳值呼叫(call by value)的語言,因為函數的形參都是區域性變數,它們通過傳入的實參進行初始化。

C 語言的優點是,只要表示式的型別適當,就可以當作實參。另一方面,缺點是在啟用函數時,如需要複製巨量資料物件,則執行成本很高。而且,函數沒有辦法修改原始變數(呼叫者的變數),只能修改原始變數的複製版本。

然而,如果函數的實參是變數的地址,那麼函數就可以通過指標,直接獲取該原始變數,並修改原始變數的值。所以,C 語言也提供了傳址呼叫(call by reference)函數。

一個典型的例子就是標準函數 scanf(),它從標準輸入流中讀入資料,然後將結果放在它的變數中,該變數由呼叫者提供的指標引數所參照:
int var;
scanf( "%d", &var );

該函數呼叫會將字串當作十進位制數讀入,然後轉換為整數,再將它的值儲存在區域性變數 var 的記憶體地址上。

下面的函數 initNode()初始化一個結構變數。呼叫者將該結構的地址當作引數來傳遞。
#include <string.h>                    // 包含了memset() 和 strcpy()的原型
struct Node { long key;
              char name[32];
              /* ... 更多結構化的成員 ... */
              struct Node *next;
           };
void initNode( struct Node *pNode )     // 初始化*pNode結構
{
  memset( pNode, 0, sizeof(*pNode) );
  strcpy( pNode->name, "XXXXX" );
}

即使函數只需要讀取變數的值,而不需要修改變數,傳遞變數地址仍然在許多時候更為高效。這是因為傳遞地址可以避免複製資料,只有變數地址會被推入棧中。

如果函數不修改變數,那麼應該將對應的引數宣告成唯讀指標,如下面的例子所示:
void printNode( const struct Node *pNode );
{
  printf( "Key: %ldn", pNode->key );
  printf( "Name: %sn", pNode->name );
  /* ... */
}

當在呼叫函數時把陣列名作為引數,那麼也是在進行“傳址呼叫”,因為陣列名會自動地被轉換為指向陣列內第一個元素的指標。

通常情況下,函數需要返回指標,如下例函數 mkNode()。該函數動態地建立一個新的 Node 物件,並將其地址傳遞給呼叫者:
#include <stdlib.h>
struct Node *mkNode()
{
  struct Node *pNode = malloc( sizeof(struct Node) );
  if ( pNode != NULL )
    initNode( pNode );
  return pNode;
}

如果無法為新的 Node 物件分配儲存空間,則函數 mkNode()會返回一個空指標。返回指標的函數通常採用返回空指標來表示失敗。例如,一個搜尋函數,如果找到了滿足條件的物件,則返回該物件的地址,如果沒有找到滿足條件的物件,則返回空指標。