type
status
date
slug
summary
tags
category
icon
password
在子函数形参中传递数组名时只会传递数组的首地址,而不是整个数组。
1.子函数形参中传递数组的本质
先看一段代码:
我们都知道,参数的传递是把实参的副本传递给形参,但数组较例外。
在子函数形参中传递数组名时只会传递数组的首地址,而不是整个数组(毕竟假如数组有1万个元素,全部拷贝一份给形参实在是过于浪费内存空间),函数在后面需要用到数组元素时再根据首地址和下标去找。
当实参将
arr
数组首元素地址传递到函数时,形参用指针进行接收,想使用:sizeof(arr)/sizeof(arr[0])
来求数组大小是行不通的,因为此时的sizeof(arr)
并不是整个数组的大小了,表示的是求这个arr
指针的字节大小。因此sizeof(arr)/sizeof(arr[0])
这个表达式在函数内部会等价sizeof(int*)/sizeof(int)
,它计算的是指针大小与单个int
元素大小的比值,而不是数组的元素数量。
综上,想要规避掉这个错误,我们一般都是在主函数/调用的地方中求出数组的具体长度,然后将长度作为参数传递给子函数。
那么这个时候有的同学就会问了,两次传入的都是数组的首地址,为什么主函数中就可以,自定义函数中就不行呢?
2.数组名不完全等同于指针
再看一段代码:
也就是说数组名在某些情况下是不等于指针的,只是在一些情况下会退化为指针。
首先我们要知道,单纯的数组名,不是指针。
数组名是一个标识符,它标识出我们之前申请的一连串内存空间,而且这个空间内的元素类型是相同的——即数组名代表的是一个内存块及这个内存块中的元素类型 。
只是在大多数情况下数组名会“退化”(C标准使用的decay和converted这两个词)为指向第一个元素的指针。 而指针不是一种聚合类的数据结构,它保存着某一种类型的对象的地址(void*除外),也说它指向这个对象。我们可以通过这个地址访问这个对象。用一个图来解释,其中a代表了整个我们声明的内存块,p仅仅指向了一个char类型的对象。
于是我们翻阅C99标准可知:
数组名只有在
sizeof
运算符
- 取址
&
运算符 - &数组名,这里的数组名表示整个数组,取出的是整个数组的地址
int arr[]={1,2,3,4,5};
&arr+1 | arr+1 =&arr[0]+1 |
&arr 是指向整个数组的指针,因此,如果我们将&arr 移动1个位置,它将移动一整个数组的地址,指向下一个包含5个元素的块。 | arr 是指向数组第一个元素的指针。因此,如果我们将arr 移动1个位置,它将指向第二个元素。 |
如果数组基地址为 00D5F940 ,则&arr+1 将为00D5F940+(5*4) ,即00D5F954 | 如果数组基地址是 00D5F940 ,则arr+1 将是00D5F940+4 ,即00D5F944 |
- 字符串常量初始化的数组
Str[]=“abcdef”
这三种情况下不会发生退化(array decay)
其余情况下调用数组名,都会退化成指向数组首地址的指针
而指针是用来记录另一个对象的地址,所以指针的内存大小就等于计算机内部地址总线的宽度。
对一个地址来取大小呢,如果是32位系统的话即为4,如果是64位系统的话为8,所以呢,在函数中sizeof获取的是指针的长度而不是数组的长度
指针变量的sizeof值与指针所指的对象没有任何关系。
- 结论:也就是说在c语言中,数组名在函数的调用中退化成了一个指针,对函数的参数使用sizeof,sizeof获取的结果就是指针的大小,而不是数组本身的大小。
3.练习
写一个函数,实现一个整形有序数组的二分查找
🤗 总结归纳
数组名在函数的调用中退化成了一个指针,传递时只会传递数组的首地址,对函数的参数使用sizeof,sizeof获取的结果就是指针的大小,而不是数组本身的大小。
📎 参考文章
- 作者:江牧
- 链接:https://lawyerjiang.top/article/key/c/3
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章