[英]Why can uninitialized pointer to int array be assigned variable's value?
[英]Why can a pointer not be assigned to an array in C?
我有以下代码:
int *pa;
int a[3] = {1, 2, 3};
为什么pa = a可以,但不允许a = pa?
主要的区别是a
类型仍然是数组,但是在执行pa=a;
时它会衰减为指针pa=a;
。 pa
现在将指向数组的第一个元素,而不是整个数组本身。 当您执行a=pa
它没有任何意义,因为您尝试指向一个将3个整数保存为只能指向单个整数的类型的数据类型。
注意:这纯粹是概念性的,这不是发生这种情况的实际原因。
我喜欢考虑像OOP和继承这样的指针分配。
想象一下int *
是一个通用对象。 现在,将int []
视为继承自int *
的对象。
如您所见,可以从int []
向下转换为int *
,但不能向上转换。
好吧,简单的答案就是语言定义根本不允许它-这是一种设计选择。
章节 :
6.5.16赋值运算符
...
约束条件
2赋值运算符的左值应为可修改的左值。
什么是可修改的左值?
6.3.2.1左值,数组和函数指示符
1 左值是具有对象类型或除void
之外的不完整类型的表达式; 53)如果左值在评估时未指定对象,则该行为未定义。 当说一个对象具有特定类型时,该类型由用于指定该对象的左值指定。 可修改的左值是不具有数组类型 ,不具有不完整类型,不具有const限定类型的左值 ,并且如果是结构或联合,则不具有任何成员(包括递归地包括任何成员)或const限定类型的所有包含的集合或联合的元素)。
...
53)名称“左值”最初来自赋值表达式E1 = E2
,其中要求左操作数E1
是(可修改的)左值。 最好将其表示为对象“定位器值”。 在本国际标准中有时称为“ rvalue”的内容称为“表达式的值”。
重点已添加。
C语言中的数组表达式与大多数其他表达式的处理方式有所不同。 丹尼斯·里奇(Dennis Ritchie)撰写的有关C语言开发的文章对此进行了解释:
NB存在如此短暂,以至于没有写完整的描述。 它提供了类型int
和char
,它们的数组以及指向它们的指针,它们以以下形式表示:
\n数组的语义仍然与B和BCPL中的完全一样:int i, j; char c, d; int iarray[10]; int ipointer[]; char carray[10]; char cpointer[];
\nint i, j; char c, d; int iarray[10]; int ipointer[]; char carray[10]; char cpointer[];
iarray
和carray
的声明创建了动态初始化的单元格,它们的值分别指向10个整数和字符序列的第一个。ipointer
和cpointer
的声明忽略了大小,以断言不应自动分配存储。 在过程中,语言对指针的解释与对数组变量的解释相同:指针声明创建了一个与数组声明不同的单元,唯一的不同在于,要求程序员分配引用,而不是让编译器分配空间和初始化单元格。
存储在与数组和指针名称绑定的单元中的值是相应存储区域的机器地址(以字节为单位)。 因此,通过指针的间接隐含没有运行时开销来将指针从字扩展到字节偏移。 另一方面,用于数组下标和指针算术的机器代码现在取决于数组或指针的类型:计算iarray[i]
或ipointer+i
意味着按引用对象的大小缩放加数i。
这些语义代表了从B的轻松过渡,我尝试了几个月。 当我尝试扩展类型表示法时,尤其是添加结构化(记录)类型时,问题变得显而易见。 看来,结构应该以一种直观的方式映射到机器的内存中,但是在包含数组的结构中,没有好地方可以存放包含数组基础的指针,也没有任何方便的方式来安排它初始化。 例如,早期的Unix系统的目录条目可能用C描述为我希望该结构不仅可以表征抽象对象,而且还可以描述可能从目录中读取的位的集合。 编译器可以在哪里隐藏指向语义所要求struct { int inumber; char name[14]; };
name
的指针? 即使更抽象地考虑结构,并且可以以某种方式隐藏指针的空间,当分配一个复杂的对象(也许是一个指定包含包含任意深度的结构的数组的结构)时,如何处理正确初始化这些指针的技术问题?
该解决方案构成了无类型BCPL和类型C之间的进化链中的关键跳跃。它消除了指针在存储中的实现,当在表达式中提及数组名称时,导致了指针的创建。 在今天的C语言中仍然存在的规则是,当数组类型的值出现在表达式中时,它们将转换为指向组成数组的第一个对象的指针。
尽管该语言的语义发生了根本性的变化,但本发明使大多数现有的B代码能够继续工作。 可以很容易地修复几个为数组名称分配新值以调整其起源的程序(在B和BCPL中可能,而在C中则毫无意义)。 更重要的是,新语言保留了对数组语义的连贯且可行的解释(如果不寻常的话),同时为更全面的类型结构开辟了道路。
这是一篇很好的文章,如果您对C的“为什么”感兴趣,则非常值得一读。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.