我从操作系统的角度来分析一下可能的原因:
C语言最初是用来写操作系统的,所以C语言的设计应该要满足操作系统的需要:
数组:从操作系统的角度是一片连续的内存区域。
指针:一个变量,指向一个内存区域。
对于C语言来说可以有两种选择:
1. 所有东西都是指针,自然不存在退化问题;
2. 指针是指针,数组是数组,不做退化;
对于1来说,显然不现实,操作系统里有大量的结构体类型(表)的定义,比如段描述符:
如果所有东西都是指针,那么访问这一类的数据结构,定义就变得非常复杂,因为需要一个额外的空间去保存指针:
char arr[10];
相当于
struct { char * base; char data[10]; } arr;
对于早期的操作系统来说,这样设计绝对是一种内存的浪费。C语言的前身B语言(BCPL)里就没有数组的概念,所有的数组都是指针,所以从B语言到C语言是一种进步,对于编写操作系统来说更加方便。
那么第2条不做退化可不可以?
这种倒是可以,数组到指针,无非就是做一次类型转换:如果被调函数要的是指针,那么就定义个指针变量转换一下就行了。
但是如果被调函数要的是数组类型,但是调用者只有指针怎么办?C语言没有办法把指针转换成数组,这时候就要发明新的语法规则了。
那么为了避免这种困难(发明新规则),就需要把所有函数形参类型都尽量声明成指针,就没有这种困难了。那么既然所有函数形参都是指针,既然数组需要一个额外的临时变量转换成指针,为什么不干脆把所有数组都直接退化成指针呢?
C语言的数组名自身是不占用存储空间的,这样的设计也是尽可能的节约内存,如果设计一种可传参的“数组数据类型”,那么可能需要额外的空间来保存信息,这样的语言是不利于编写操作系统的。
从设计一个适合操作系统开发的编程语言来说,这样的设计在当年(内存不足)的时代里,是非常合适的。
所以,个人理解是:C语言这么设计是为了适应当时的操作系统开发而做的一些取舍。
不想让它退化的话可以包在struct里嘛
数组退化为指针,与参数传递的开销没有关系。
如果觉得有关系的话,思考一下为什么C++数组对象可以传指针而没有觉得开销过大?
指针跟数组的区别是数组有长度,指针没有长度。而数组退化为指针的本质是:数组它本来就是指针,换句话说「数组本来就没有记录其长度信息,所以只能是指针」。
在C语言标准中,数组的长度信息仅仅在编译期的上下文存在,运行时间无法获取一个数组的长度信息。
sizeof 它为什么是一个编译期关键字,而不是一个普通函数?因为 sizeof 的本质是让编译器查询这个变量是怎么声明的,然后根据其声明的方式来推导其长度,而这个变量本身,则并没有记录自己的长度。
而数组只有在自身的定义域,也就是定义那个数组的上下文中才可以通过编译器定位其长度。
一旦数组作为参数传递之后,要获得长度就必须依赖运行时间信息,而数组并没有在运行时间记录其长度信息,所以根本无法获取长度,它也就只能是一个指针。
所以:从编译时间的角度,数组在传递变量后退化为指针,从运行时间的角度,数组它一直都是指针,根本不存在退化,因为它根本没有记录长度信息。
换句话说,C语言数组它从来就不具备值语义,一直都是个指针,除了在当前定义域使用sizeof这个唯一例外的情况。其他情况它本来就是指针。
其实有其它设计方案可以实现更好用的数组:如果当初C语言设计数组的时候,是让数组本身将长度信息写进内存(就像一个数组对象一样),那么,哪怕是用指针传递,也是可以依然当作数组使用的,可惜,当初并没有这么设计,于是,以后也就没有办法再修改了。