数组的参数传递--参数退化隐式转换
参数传递---关于数组的退化
数组的参数传递
在说二维数组前先回顾一下一维数组的参数传递,对二维数组的解引用、指针数组、数组指针不是很了解的可以先看一下这篇随笔:二维数组(解引用、指针数组、数组的指针)
一维数组作为实参传入函数时,接收的形参有两种形式
第一种形式:
#include <stdio.h>
void fun(char s[]);
int main()
{
char num[10] = "Hello";
fun(num);
return 0;
}
void fun(char s[])
{
puts(s);
}
第二种形式:
#include <stdio.h>
void fun(char s[]);
int main()
{
char num[10] = "Hello";
fun(num);
return 0;
}
void fun(char *s)
{
puts(s);
}
下面分析下第一种,因为当数组作为实参进行传递时会自动退化为指针(是一种隐式转换),所以传入的是一维数组num的首地址,即&num[0],作为接收的形参char s[]也会自动退化为char *类型的指针,所以数组在进行传递时
传递的是数组的地址而不是数组的元素,因为一维数组的形参是会自动退化为指针的,所以数组长度填不填都无所谓,形参数组的长度是大于实参的长度还是小于实参的长度都没有影响(既然的都退化了,指针又没有像数组长度这个概念所以填不填长度都无所谓了)
那么为什么C语言不允许直接传递数组的所有元素呢?
数组是一系列数据的集合,数据的数量没有限制,可能很少,也可能出乎意料的大,对它们进行内存拷贝有可能是一个漫长的过程,会严重拖慢程序的效率,为了防止技艺不佳的程序员写出低效的代码,C语言没有从语法上支持数据集合的直接赋值。
一维数组参数传递中的坑
先上代码:
#include <stdio.h>
int main()
{
double num[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
char **p;
printf("%d\n", sizeof(num));
printf("%d\n", sizeof(num+0));
printf("%d\n", sizeof(*num));
printf("%d\n", sizeof(&num));
printf("%d\n", sizeof(p));
return 0;
}
读者可以先想一下结果应该是什么,再往下看
运行结果如下:
第一个sizeof(num),大部分人应该都知道,应该是数组num的长度*类型所占字节数,结果为80(说明在使用sizeof时数组没有退化,也间接说明了sizeof不是函数而是关键字)
第二个sizeof(num+0),这个就带有一定的迷惑性了,num+0包含了一层隐式转化,转换后变成了char *类型,结果为4
第三个sizeof(*num),这比较好理解,就是sizeof(num[0]),结果为8
第四个sizeof(&num),num本身就是数组的首地址,也就是&num[0],再前面再加一个取地址符,也就是地址的地址,结果为4,注意&num的类型为double (*p)[10],如果要存储&num的值,
需要用double (*)[10]类型的变量(一般不这么用,没什么意义至于为什么没意义下面会细说)
关于这种数组地址的地址,下面上代码实测一下
#include <stdio.h>
int main()
{
double num[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
printf("%p\n%p", num, &num);
return 0;
}
运行结果为:
从运行结果可以看出,数组首地址的地址的值与数组首地址的值是一模一样的,这也就是为什么上面说没什么意义的原因
第五个sizeof(p),p是一个二级指针,和一级指针相同,都是占4个字节,结果为4,(读者可以尝试下无论多少级指针,只要编译器的环境是32位的占的字节数都是4)
二维数组
二维数组作为实参传入函数时,接收的形参形式有如下几种
第一种:
1 #include <stdio.h>
2
3 void fun(char p[][6]);
4
5 int main()
6 {
7 char ss[2][6] = {"hello", "hi"};
8
9 fun(ss);
10 return 0;
11 }
12
13 void fun(char p[][6])
14 {
15 puts(p[0]);
16 puts(p[1]);
17 }
第二种:
#include <stdio.h>
void fun(char p[][6]);
int main()
{
char ss[2][6] = {"hello", "hi"};
fun(ss);
return 0;
}
void fun(char (*p)[6])
{
puts(p[0]);
puts(p[1]);
}
二维数组在作为实参进行传递时也是会退化的,二维数组ss[2][6]会退化成ss(*)[6]类型数组的指针,同样如果形参也写成二维数组的形式,就像第一种那样char p[][6](注意第二维不能省略,不能省略是因为内存的寻址方式的特点所决定的),也会自动隐式转化成char (*p)[6]这样的形式
在C99中对指针的退化进行了说明:
Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type
“array of type” is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue.
上面这句话说的意思是, 数组在除了3种情况外, 其他时候都要"退化"成指向首元素的指针.
比如对 char s[10] = "Hello";
这3中例外情况是:
(1) sizeof(s)
(2) &s;
(3) 用来初始化s的"Hello";
(tips:数组的首地址是常量,不可更改,指针保存的地址是变量,可以更改)