C語言中文網 目錄
首頁 > 編程筆記 > C語言筆記 > 指針 閱讀:9,739

C語言數組指針和指針數組

在許多 C 程序中,指針常被用于引用數組,或者作為數組的元素。指向數組的指針常被簡稱為數組指針(array pointer),而具有指針類型元素的數組則被稱為指針數組(pointer array)。

數組指針

為了便于舉例,下面的描述均以一個 int 數組為例。同樣的原理可以應用于其他類型數組,包括多維數組。

要聲明指向數組類型的指針,必須使用括號,如下所示:
int (* arrPtr)[10] = NULL;   // 一個指針,它指向一個有10個int元素的數組

如果沒有括號,則聲明 int*arrPtr[l0];表示 arrPtr 是一個具有 10 個 int 類型指針的數組。

在該例中,指向有 10 個 int 元素的數組的指針會被初始化為 NULL。然而,如果把合適數組的地址分配給它,那么表達式 *arrPtr 會獲得數組,并且(*arrPtr)[i] 會獲得索引值為 i 的數組元素。根據下標運算符的規則,表達式(*arrPtr)[i] 等同于 *((*arrPtr)+i)。因此,**arrPtr 獲得數組的第一個元素,其索引值為 0。

為了展示數組指針 arrPtr 的幾個運算,下例使用它來定位一個二維數組的某些元素,也就是矩陣內的某些行:
int matrix[3][10];            // 3行,10列的數組
                                    // 數組名稱是一個指向第一個元素的指針,也就是第一行的指針
arrPtr = matrix;            // 使得arrPtr指向矩陣的第一行
(*arrPtr)[0] = 5;       // 將5賦值給第一行的第一個元素
arrPtr[2][9] = 6;           // 將6賦值給最后一行的最后一個元素
++arrPtr;                   // 將指針移動到下一行
(*arrPtr)[0] = 7;           // 將7賦值給第二行的第一個元素

在初始化賦值后,arrPtr 指向矩陣的第一個行,正如矩陣名稱 matrix 一樣。在這種情況下,使用 arrPtr 獲取元素的方式與使用 matrix 完全一樣。例如,賦值運算(*arrPtr)[0]=5 等效于 arrPtr[0][0]=5 和 matrix[0][0]=5。

然而,與數組名稱 matrix 不同的是,指針名稱 arrPtr 并不代表一個常量地址,如運算 ++arrPtr 所示,它進行了自增運算。這個自增運算會造成存儲在數組指針的地址增加一個數組空間大小,在本例中,即增加矩陣一行的空間大小,也就是 10 乘以 int 元素在內存中所占字節數量。

如果想把一個多維數組傳入函數,則必須聲明對應的函數參數為數組指針。最后要注意的是,如果 a 是一個具有 10 個 int 類型元素的數組,那么無法使用下面的方式對前面例子中的指針 arrPtr 賦值:
arrPtr = a;                  // 錯誤:指針類型不匹配

錯誤的原因是,數組名字,例如上文的 a,會被隱式地轉換為指針,指向數組第一個元素,而不是指向整個數組。指向 int 的指針沒有被隱式地轉換為指向 int 數組的指針。本例中的賦值操作需要顯式的類型轉換,在類型轉換運算符中明確指定目標類型是
int (*) [10]:
arrPtr = (int (*)[10])a;        // 合法

在前文 arrPtr 的聲明語句(int(*arrPtr)[10]=NULL;)中,刪除其中標識符 arrPtr,就可得到 int(*)[10],即對應的數組指針類型。然而,為了提高可讀性和靈活性,可以利用 typedef 為所用的類型定義一個簡單的名字:
typedef int ARRAY_t[10];    // 定義一個“具有10個元素數組”類型名稱
ARRAY_t  a,                     // 具有該類型的數組
         *arrPtr;               // 一個指向該數組類型的指針
arrPtr = (ARRAY_t *)a;  // 使得arrPtr指向a

指針數組

指針數組(也就是元素為指針類型的數組)常常作為二維數組的一種便捷替代方式。一般情況下,這種數組中的指針會指向動態分配的內存區域。

例如,如果需要處理字符串,可以將它們存儲在一個二維數組中,該數組行空間大小必須足以存儲下可能出現的最長字符串:
#define ARRAY_LEN 100
#define STRLEN_MAX 256
char myStrings[ARRAY_LEN][STRLEN_MAX] =
{ // 墨菲定律的幾條推論:
“會出錯的事,總會出錯。”
“世上沒有絕對正確的事情。”
“每個解決辦法都會衍生出新的問題。”
};

然而,這個方式造成內存浪費,25600 字節中只有一小部分被實際使用到。一方面,短字符串會讓大部分的行是空的;另一個方面,有些行根本沒有用到,但卻得為它預留內存。

一個簡單的解決方案是,使用指針數組,讓指針指向對象(在此處的對象就是字符串),然后只給實際存在的對象分配內存(未用到的數組元素則是空指針)。
#define ARRAY_LEN 100
char *myStrPtr[ARRAY_LEN] =        // char指針的數組
{ // 墨菲定律的幾條推論:
“會出錯的事,總會出錯。”
“世上沒有絕對正確的事情。”
“每個解決辦法都會衍生出新的問題。”
};

圖 1 展示了對象在內存中的存儲情況:


圖 1

尚未使用的指針可以在運行時指向另一個字符串。所需的存儲空間可以利用這種常見方法來動態地保留。當不再需要該內存時,可以釋放。

例 1 中的程序是一個簡單版本的過濾器工具 sort。它從標準輸入流中讀取文字,根據字母順序對行排序,然后將結果在標準輸出中顯示出來。這個程序沒有移動任何字符串,它實際排序的是一個指針數組。

【例1】對文字各行進行排序的簡單程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *getLine(void);                                 // 讀取文本中的一行
int str_compare(const void *, const void *);

#define NLINES_MAX 1000                                      // 定義文本行數的最大值
char *linePtr[NLINES_MAX];                           // char指針的數組

int main()
{
  // 讀入行
  int n = 0;                                         // 讀入行的數量
  for ( ; n < NLINES_MAX && (linePtr[n] = getLine()) != NULL; ++n )
    ;
  if ( !feof(stdin) )                                // 處理錯誤
  {
    if ( n == NLINES_MAX )
      fputs( "sorttext: too many lines.\n", stderr );
    else
      fputs( "sorttext: error reading from stdin.\n", stderr );
  }
  else                                                       // 排序和輸出
  {
    qsort( linePtr, n, sizeof(char*), str_compare ); // 排序
    for ( char **p = linePtr; p < linePtr+n; ++p )   // 輸出
          puts(*p);
  }
  return 0;
}

// 從stdin中的文本讀取一行;忽略尾部的換行符
// 返回值: 一個指向所讀字符串的指針,或者為NULL,當讀到文字結尾時或發生錯誤時
#define LEN_MAX 512                                  // 定義一行字符數的最大值
  
char *getLine()
{
  char buffer[LEN_MAX], *linePtr = NULL;
  if ( fgets( buffer, LEN_MAX, stdin ) != NULL )
  {
    size_t len = strlen( buffer );

    if ( buffer[len-1] == '\n' )                     // 去掉尾部的換行符
      buffer[len-1] = '\0';
    else
      ++len;

    if ( (linePtr = malloc( len )) != NULL )         // 為行獲得內存空間
      strcpy( linePtr, buffer );                     // 將行復制到已分配區域
  }
  return linePtr;
}

// 比較函數,供qsort()使用
// 參數:兩個指針,指向數組內待排序的兩個元素,這里,兩個指針都是char **類型
int str_compare( const void *p1, const void *p2 )
{
  return strcmp( *(char **)p1, *(char **)p2 );
}

在例 1 中,常量 NLINES_MAX 限制了一行文字中字符數量的最大值。然而,我們可以通過動態地創建指向文本行指針的數組,達到消除該限制的目的。

精美而實用的網站,提供C語言C++STLLinuxShellJavaGo語言等教程,以及socketGCCviSwing設計模式JSP等專題。

Copyright ?2011-2018 biancheng.net, 陜ICP備15000209號

底部Logo