误解阵列衰变
使用多维数组,指针数组等的代码中的一个常见问题是 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