在某些情况下,C 语言中的数组会被隐式转换为指针类型,使得程序员对数组的操作更加灵活。在函数参数传递中,隐式转换总是在发生。比如:
1 | void foo1(int *b) { printf("%lu\n", sizeof(b)); } |
在 main 函数中,a 是一个数组。我们可能会认为在 foo2 中 b 的类型是整型数组类型(输出为 20),而实际上,无论是在 foo1 还是 foo2 中,b 的类型均为指向整型的指针类型,即输出的结果均为指针类型的大小(8 个字节)。
实参:Actual Parameter
形参:Formal Parameter
参数传递的隐式转换规则:
- 若实参为一维数组,则形参是指向数组所存元素类型的指针;
- 若实参为多维数组,则形参是指向一维或多维数组的指针;
- 转换并不是递归的,数组的数组会被改成为
数组的指针
,而不是指针の指针
;而三维数组char ho[2][3][4]
会被转换为char (*)[3][4]
类型。
为了进一步说明参数传递时发生的隐式转换,我们先了解一下以下几个类型以及它们之间的关系。
指针数组:char *c[10]
指针の指针:char **c
指针数组
本身是一个一维数组,存的是字符指针。指针の指针
本身是一个指针,指向的还是指针。注意它和二维数组
的区别,可以说两者是毫无关系的。1
2char abc[3][4];
char **pp = abc; // 会给出警告,类型不兼容。
二维数组:char c[8][10]
数组指针(行指针):char (*c)[10]
二维数组
本身是一个数组,存的还是数组。数组指针(行指针)
本身是一个指针,指向的是数组,一般和二维数组
一起使用。我们要注意它和指针数组
在写法上的区别(多了括号,先做*
单目运算)。
接着,我们看一下隐式转换如何被运用到上述类型中。
图中,数组的数组(即二维数组)被隐式转换为 char (*)[10]
,称为 数组指针
或 行指针
。指针数组 char *c[10]
被隐式转换为 char **c
,称为 指针の指针
。若实参本身是指针,则无需进行隐式转换。
最后,举两个例子来验证一下参数传递中是否发生了隐式转换。
例子 1
main 函数的 argv 参数被隐式转换为
char **
指针类型。1
2
3
4// A
int main(int argc, char *argv[]) {
printf("%lu\n", sizeof(argv));
}1
2
3
4// B
int main(int argc, char **argv) {
printf("%lu\n", sizeof(argv));
}程序 A 会出现 warning:
1
warning: sizeof on array function parameter will return size of 'char *' instead of 'char []' [-Wsizeof-array-argument]
两个程序输出都为 8,即指针类型的大小,而不是 argv 数组的大小,因为 argv 已经被转换为一个指针了。
例子 2
1
2
3
4
5
6
7
8
9
10
11
12
13void foo(int (*fnum)[5]) {
printf("foo: %lu\n", sizeof(fnum));
printf("foo: %lu\n", sizeof(&fnum));
fnum++;
}
int main() {
int num[3][5];
printf("main: %lu\n", sizeof(num));
printf("main: %lu\n", sizeof(&num));
foo(num);
return 0;
}输出:
1
2
3
4main: 60
main: 8
foo: 8
foo: 8可以看到,在 foo 函数里,sizeof(fnum) 的大小是 8,而不等于 main 函数里的 60。因此,fnum 不再是数组,而是一个指针。如果将
int (*fnum)[5]
改成int fnum[][5]
,fnum 仍然是一个指针。注意:如果将
int (*fnum)[5]
改成int (*fnum)[]
,不会有警告和报错。如果此时在添加指针运算fnum++
,则编译器会报错,因为编译器不知道指针运算中偏移量是多少。1
error: arithmetic on a pointer to an incomplete type 'int []'
此外,将
int fnum[][5]
改成int fnum[][]
也会有报错。1
error: array has incomplete element type 'int []'
总结:C is a nightmare.
参考:
- C 语言指针的指针和二维数组的区别?
- 二维数组和指向指针的指针
- Expert C Programming