参数传递---关于数组的退化

Posted lanhaicode

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了参数传递---关于数组的退化相关的知识,希望对你有一定的参考价值。

数组的参数传递

在说二维数组前先回顾一下一维数组的参数传递,对二维数组的解引用、指针数组、数组指针不是很了解的可以先看一下这篇随笔:二维数组(解引用、指针数组、数组的指针)

一维数组作为实参传入函数时,接收的形参有两种形式

第一种形式:

 1 #include <stdio.h>
 2 
 3 void fun(char s[]);
 4 int main()
 5 {
 6     char num[10] = "Hello";
 7     fun(num);
 8 
 9     return 0;
10 }
11 
12 void fun(char s[])
13 {
14     puts(s);
15 }

 

第二种形式:

 1 #include <stdio.h>
 2 
 3 void fun(char s[]);
 4 int main()
 5 {
 6     char num[10] = "Hello";
 7     fun(num);
 8 
 9     return 0;
10 }
11 
12 void fun(char *s)
13 {
14     puts(s);
15 }

下面分析下第一种,因为当数组作为实参进行传递时会自动退化为指针(是一种隐式转换),所以传入的是一维数组num的首地址,即&num[0],作为接收的形参char s[]也会自动退化为char *类型的指针,所以数组在进行传递时

传递的是数组的地址而不是数组的元素,因为一维数组的形参是会自动退化为指针的,所以数组长度填不填都无所谓,形参数组的长度是大于实参的长度还是小于实参的长度都没有影响(既然的都退化了,指针又没有像数组长度这个概念所以填不填长度都无所谓了)

 

一维数组参数传递中的坑

 先上代码:

 1 #include <stdio.h>
 2 
 3 int main()
 4 {
 5     double num[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
 6     char **p;
 7 
 8     printf("%d
", sizeof(num));
 9     printf("%d
", sizeof(num+0));
10     printf("%d
", sizeof(*num));
11     printf("%d
", sizeof(&num));
12     printf("%d
", sizeof(p));
13 
14     return 0;
15 }

读者可以先想一下结果应该是什么,再往下看

运行结果如下:

技术图片

第一个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]类型的变量(一般不这么用,没什么意义至于为什么没意义下面会细说)

关于这种数组地址的地址,下面上代码实测一下

1 #include <stdio.h>
2 
3 int main()
4 {
5     double num[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
6     printf("%p
%p", num, &num);
7 
8     return 0;
9 }

 运行结果为:

技术图片

从运行结果可以看出,数组首地址的地址的值数组首地址的值是一模一样的,这也就是为什么上面说没什么意义的原因

第五个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 }

 

第二种:

 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 }

 二维数组在作为实参进行传递时也是会退化的,二维数组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:数组的首地址是常量,不可更改,指针保存的地址是变量,可以更改)

以上是关于参数传递---关于数组的退化的主要内容,如果未能解决你的问题,请参考以下文章

C 语言数组作为参数退化为指针问题 ( 问题描述 | 从编译器角度分析该问题 | 出于提高 C 语言执行效率角度考虑 | 数组作为参数的推荐方案 )

C语言学习笔记--数组参数和指针参数

第35课 数组参数和指针参数分析

数组参数和指针参数

20180513 实参 形参 数组

关于C语言中数组作为参数传递的疑惑~~