誤解陣列衰變
使用多維陣列,指標陣列等的程式碼中的一個常見問題是 Type**
和 Type[M][N]
基本上是不同的型別:
#include <stdio.h>
void print_strings(char **strings, size_t n)
{
size_t i;
for (i = 0; i < n; i++)
puts(strings[i]);
}
int main(void)
{
char s[4][20] = {"Example 1", "Example 2", "Example 3", "Example 4"};
print_strings(s, 4);
return 0;
}
示例編譯器輸出:
file1.c: In function 'main':
file1.c:13:23: error: passing argument 1 of 'print_strings' from incompatible pointer type [-Wincompatible-pointer-types]
print_strings(strings, 4);
^
file1.c:3:10: note: expected 'char **' but argument is of type 'char (*)[20]'
void print_strings(char **strings, size_t n)
該錯誤表明 main
函式中的 s
陣列被傳遞給函式 print_strings
,函式 print_strings
需要不同的指標型別。它還包括一個註釋,表示 print_strings
所期望的型別以及從 main
傳遞給它的型別。
問題是由於稱為陣列衰減的東西。當 s
的型別 char[4][20]
(由 4 個 20 個字元的陣列組成的陣列)傳遞給函式時會發生什麼呢?它變成了指向它的第一個元素的指標,好像你寫了 &s[0]
,它的型別為 char (*)[20]
(指向 1 個陣列的指標) 20 個字元)。對於任何陣列都會發生這種情況,包括指標陣列,陣列陣列(3-D 陣列)陣列以及指向陣列的指標陣列。下面的表格說明了陣列衰減時會發生什麼。突出顯示型別描述中的更改以說明發生的情況:
在衰變之前 | 衰變之後 | ||
---|---|---|---|
char [20] |
陣列(20 個字元) | char * |
指標(1 個字元) |
char [4][20] |
(4 個 20 個字元陣列 )的陣列 | char (*)[20] |
指向(1 個 20 個字元的陣列 ) |
char *[4] |
陣列(4 個指向 1 個字元) | char ** |
指向(1 指向 1 個字元的指標 ) |
char [3][4][20] |
陣列(3 個陣列,4 個陣列,20 個字元) | char (*)[4][20] |
指向(1 個 4 個 20 個字元陣列的陣列) |
char (*[4])[20] |
陣列(4 個指向 1 個 20 個字元的陣列) | char (**)[20] |
指向(1 個指向 1 個 20 個字元陣列的指標 ) |
如果陣列可以衰減到指標,那麼可以說指標可以被認為是至少 1 個元素的陣列。一個例外是空指標,它指向什麼都不是,因此不是陣列。
陣列衰減只發生一次。如果陣列已衰減為指標,則它現在是指標,而不是陣列。即使你有一個指向陣列的指標,記住指標可能被認為是至少一個元素的陣列,因此已經發生了陣列衰減。
換句話說,指向陣列的指標(char (*)[20]
)永遠不會成為指向指標(char **
)的指標。要修復 print_strings
功能,只需讓它接收正確的型別:
void print_strings(char (*strings)[20], size_t n)
/* OR */
void print_strings(char strings[][20], size_t n)
當你希望 print_strings
函式對任何字元陣列都是通用的時候會出現問題:如果有 30 個字元而不是 20 個字元怎麼辦?還是 50?答案是在陣列引數之前新增另一個引數:
#include <stdio.h>
/*
* Note the rearranged parameters and the change in the parameter name
* from the previous definitions:
* n (number of strings)
* => scount (string count)
*
* Of course, you could also use one of the following highly recommended forms
* for the `strings` parameter instead:
*
* char strings[scount][ccount]
* char strings[][ccount]
*/
void print_strings(size_t scount, size_t ccount, char (*strings)[ccount])
{
size_t i;
for (i = 0; i < scount; i++)
puts(strings[i]);
}
int main(void)
{
char s[4][20] = {"Example 1", "Example 2", "Example 3", "Example 4"};
print_strings(4, 20, s);
return 0;
}
編譯它不會產生任何錯誤並導致預期的輸出:
Example 1
Example 2
Example 3
Example 4