本文共 1282 字,大约阅读时间需要 4 分钟。
数组与指针是很多C语言初学者非常容易犯糊涂的地方,让人琢磨不透。有人总结出“数组退化成指针”的种种情况,试图揭示数组与指针的习性。然而,这样的“退化”现象为什么存在呢?
数组名与指针都是用于指代某一块内存空间。作为内存空间,它至少有两个属性,一是“首地址”、二是“空间大小”。 首地址: 数组名与指针都直接记录着内存空间“首地址”这个属性。 对于数组名,是由编译器来记录这个“首地址”值的,它是一个不变量; 对于指针,则是分配了一块大小为一个机器字长的内存空间,用来记录这个“首地址”; 空间大小: 相对于“首地址”,“空间大小”这个属性是隐含的。 数组名对应的元素大小及元素个数在其初始化时就确定了,于是数组名指代的这块内存空间的大小也就确定了。对于数组名,编译器在记录其首地址的同时,也记录着它的“空间大小”。 指针的类型隐含了元素的大小,元素个数为一个,于是“空间大小”也就确定了。(如果需要访问后面的元素,则需要增加指针的地址值,以指向后面的空间。)对于指针,编译器记录着它的“空间大小”,这在指针被定义时就已经确定了。 数组名指代的空间“首地址”是个不变量,像立即数一样,没有存储它的空间,所以它不能被赋值。 数组名指代的“空间大小”也是个不变量,没有存储它的空间,它也不能被赋值。 指针则有存储自己的空间,所以它可以被赋值。可以被指针赋值,也可以被数组名赋值。指针所拥有的这个存储空间用于存放“首地址”,所以,赋值操作能赋予指针的内容就只是“首地址”。 然而,指针没有存储“空间大小”的空间,所以指针指代的“空间大小”也是个不变量,“空间大小”在指针赋值过程中无法传递。 void型的指针比较特殊,它不隐含“空间大小”这个属性,仅仅记录了“首地址”。它不能完整地描述一块内存空间,所以,不能通过void型指针来读写内存空间。 void型的指针必须先赋值给其他类型的指针,才能通过后者来实现对应内存空间的读写。 void型的指针也可以通过强制转换成其他类型的指针来实现对应内存空间的读写。强制转换可以想象成将指针赋值一个临时指针,再由这个临时指针来实现内存空间的读写。 实际上,“数组”之所以“退化成指针”,就是因为指针赋值过程中无法传递“空间大小”。不仅数组赋值给指针会“退化”,指针赋值给指针也会“退化”(或者说“进化”)。 然而,在C语言指针模型中,为什么就不支持在赋值过程中传递“空间大小”这个属性呢? 内存空间(包含“首地址”和“空间大小”两层含义)可以说是高级语言的一种抽象。 而在低级语言中(CPU指令逻辑),则没有空间的概念。CPU指令只接收“首地址”,不接收“空间大小”。“空间大小”是通过程序逻辑(使用存取宽度是多少的指令?指令执行多少次?)来控制的。 C语言保留了低级语言的这个特性,并没有对“内存空间”这个概念做完整的封装。 但是,同时,又还是做了一定的封装。前面提到,编译器记录了数组名和指针所对应的“空间大小”,这些不变量在编译后被体现成了表示“空间大小”的程序逻辑。 这种不完全的封装,或许就是数组与指针之迷惑性的源头所在。转载地址:http://kfhbx.baihongyu.com/