C 陣列 vs 指標

2021-06-22 16:25:46 字數 2996 閱讀 4051

在很多c++的入門書籍裡,在介紹陣列的時候,都會提到,陣列名可以看成乙個常量指標。這句話本身問題並不大,但是由於沒有對指標與陣列區別的深入解釋,會使一些人不能正確了解陣列與指標的聯絡與不同。這裡對陣列與指標的不同做一點簡單的介紹。

首先要明確的是,陣列與指標的概念。陣列和指標是c++中的兩種不同的資料型別。陣列指的是在一片連續的記憶體空間中儲存的n個相同型別的物件(object,c++標準中,記憶體中任何資料都可以被稱做object,比如整數,類),而指標則是乙個物件的位址。

從 陣列與指標的概念來看,他們應該是很不相同的,但為什麼陣列名可以看成乙個常量指標呢,這要是因為在c++中,陣列不能做為絕大多數操作符 (operator)的操作物件。當陣列型別的變數出現在表示式中時,它幾乎總是被通過自動型別轉換轉換指標型別(array-to-pointer conversion),這個指標指向陣列的第乙個成員。由於這個指標是由自動型別轉換生成的,是乙個臨時變數,因而不能被賦值,這使它表現得像乙個常 量。於是,「陣列名可以看成乙個常量指標」。

事實上,只要陣列變數被求值,它就一定會轉換成指標。這使得大多數情況下陣列可以直接被當成乙個指標使用。同時,這種轉換也會發生在一些「意想不到」的情況下。

c++的引數是按值傳遞的,當陣列出現在函式引數的位置時,就需要對它求值,從而使它被轉換成乙個指標。這就決定了,陣列不能成為函式的引數。

但是我們知道,函式的定義中是可以出現陣列形式的引數的。c++標準規定,在函式定義中出現的陣列形式的引數,其型別會被自動地變換為指標型別。從而,函式不會有陣列型別的引數,陣列形式的引數的型別都被變換為相應的指標型別。由於陣列與指標的表現在大多數情況下是相同的,這一般不會帶來什麼問題。但是,在陣列與指標表現不同的時候,就需要注意了。

int func(

int *a, // a 是乙個指標

int b, // b 是乙個指標

int c[5]); // c 還是乙個指標

// 該函式宣告與以下是等價的

int func(

int *a,

int *b,

int *c);

請記住,函式的引數的型別永遠不會是陣列。當引數定義以陣列形式出現的時候,它實際是乙個指標。

陣列與指標的不同上面說過了,在需要對陣列求值的時候,它一定會被轉換成乙個指標。但是有些情況下,陣列並不會被求值。這時陣列不會被轉換為指標,它與指標的表現也就可能不同了。其中最經常遇到的情況是,陣列做為sizeof運算子引數的時候。

sizeof運算子計算物件所占用的記憶體的大小,它只需要其引數的型別即可,不需要對引數求值。這時候,陣列不會被轉換為指標。sizeof會計算出陣列物件的總大小,即陣列中所有元素所占用空間的總和。

例如:

// 假設 sizeof(char) = 1, sizeof(short) = 2, sizeof(short *) = 4

short arr[5];

short *p = arr;

int arrsize = sizeof(arr); // 2x5 = 10

int psize = sizeof(p); // 4

int num_element = sizeof(arr)/sizeof(arr[0]); // 5 ,求陣列的元素個數

這裡可以看到,對陣列可以用sizeof(arr)/sizeof(arr[0])的方式來計算陣列中元素的個數。但是對指標是不行的。在函式中對待陣列形式的引數要特別注意這一點。函式的陣列形式的引數的實際型別是指標,對它使用sizeof運算子得到的指標變數的大小,而不能得到陣列的實際大小。

類似的執行符還有乙個,就是typeid。typeid作用於乙個變數,將得到它的型別(乙個表示型別的std::type_info變數)。它也不需要對其引數求值,對陣列與指標,它將返回不同結果。

operator & 用於取出物件的位址,它同樣不需要對其物件求值。於是,它作用於陣列與指標的效果是不同的。當它作用於指標的時候,將得到指標位址,其它型別是乙個指向指標的指標(也就是通常說的二級指標)。當它將作用於乙個陣列時,將得到陣列的位址(其值等於陣列首元素的位址),其型別是乙個指向陣列的指標。

short arr[5], a;

short *p = &a;

short **pp = &p; // 指標 p 的位址

short *p1 = &(p[0]); // 指標所指向的物件(p[0])的位址 (&a), p1=p

short (*parr)[5] = &arr; // 陣列 arr 的位址

short *p2 = &(arr[0]); // 陣列中第乙個元素(arr[0])的位址,其值與 arr 的位址是相同的

陣列可以看成乙個指標,很多人於是很自然地認為二維陣列也可以看成乙個二維指標,但實際不是這樣的。

在c++中,二維陣列本質上乙個陣列的陣列。而二維指標則是指標的指標。當二維陣列被轉換為指標時,它將被轉換為乙個指向陣列的指標,而不能轉換成指向指標的指標(二維指標)。對二維陣列使用 operator * ,得到的結果為乙個一維陣列(當然多陣列情況下它會被立即轉換成乙個指標),而對二維指標使用operator *,將直接得到乙個一維指標。

short arr[3][5];

short (*parr)[5] = arr; // 指向陣列的指標

short **pp = arr; // 錯誤,指向陣列的指標與指向指標的指標是不同的型別

int i = sizeof(*arr); // 10 = sizeof(short) * 5 = sizeof(short[5])

int j = sizeof(*pp); // 4 = sizeof(short *)

還見到過有人使用強制型別轉換將二維陣列轉換成二維指標,如:(short**)arr。誠然,由於c++對這種強制型別轉換並不做合法性檢查,從而可以通過編譯,但是這種方法不能得到乙個合法的二維指標。這是由於,二維指標指向的乙個指標,而二維陣列的成員是一維陣列,即使將其強制轉換為二維指標,其記憶體中實際儲存的仍然是乙個一維陣列,而不是指標。

函式指標VS指標函式 陣列指標VS指標陣列

定義乙個函式 void fuc1 int a 宣告函式指標 int fucptr int 函式指標fucptr指向函式fuc1 fucptr fuc1 呼叫函式指標 fucptr 10 int array1 10 定義乙個陣列 int arrayptr 10 定義乙個陣列指標 arrayptr ar...

字元陣列VS字元指標

1.以字串形式出現的,編譯器都會為該字串自動新增乙個 0作為結束符,如在 中寫 abc 那麼編譯器幫你儲存的是 abc 0 2.abc 是常量嗎?答案是有時是,有時不是。不是常量的情況 abc 作為字元陣列初始值的時候就不是,如 char str abc 因為定義的是乙個字元陣列,所以就相當於定義了...

字元陣列VS字元指標

1.以字串形式出現的,編譯器都會為該字串自動新增乙個 0作為結束符,如在 中寫 abc 那麼編譯器幫你儲存的是 abc 0 2.abc 是常量嗎?答案是有時是,有時不是。不是常量的情況 abc 作為字元陣列初始值的時候就不是,如 char str abc 因為定義的是乙個字元陣列,所以就相當於定義了...