数组名称是指针,函数名称也是指针,但结构名称不是指针。我试图了解这种差异背后是否有一些逻辑推理,或者它只是 C 语言的随机语法?
3 回答
数组或函数的实例只有一个,所以我们可以指向它们。但是,结构可以有很多实例,所以我们不能指向结构,但可以指向结构实例。
数组很奇怪。它们的行为与其他类型不同。
C 派生自一种名为 B 1的早期语言,它维护一个指向数组第一个元素的单独指针。鉴于声明
auto a[10];
你会在内存中得到如下内容:
+–––+
a: | | ––+
+–––+ |
... |
+–––––+
|
v
+–––+
| | a[0]
+–––+
| | a[1]
+–––+
...
+–––+
| | a[9]
+–––+
数组下标操作a[i]
被定义为*(a+i)
- 给定存储在的起始地址a
,从该地址偏移i
元素(不是字节)并取消引用结果。
在设计 C 时,Ritchie 想保留 B 的数组语义,但他不想保留指向第一个元素的单独指针,所以他去掉了它 - 相反,他创建了最终标准化的规则如下:
6.3.2.1 左值、数组和函数指示符C 2011 在线草案
...
3 除非它是运算sizeof
符、运算符_Alignof
或一元&
运算符的操作数,或者是用于初始化数组的字符串字面量,否则为 '' 类型的表达式'' 类型的数组被转换为类型为 ''类型的指针的表达式,该指针指向数组对象的初始元素,而不是左值。如果数组对象具有register
存储类,则行为未定义。
当你在 C 中声明一个数组时
int a[10];
你在内存中得到了这个:
+---+
a: | | a[0]
+---+
| | a[1]
+---+
...
+---+
| | a[9]
+---+
没有为单独的指针对象分配空间。下标操作a[i]
仍然定义为*(a + i)
,只是在这种情况下,表达式a
从数组类型转换为指针类型作为计算的一部分。
这很重要——数组名a
不是指针。相反,表达式 a
会根据需要从数组类型转换为指针类型。
函数也有类似的规则:
4函数指示符是具有函数类型的表达式。除非它是运算符的操作数、同上。sizeof
运算_Alignof
符65)或一元运算&
符,否则类型为“函数返回类型”的函数指示符将转换为类型为“指向函数返回类型的指针”的表达式。
65) 因为没有发生这种转换,所以 sizeof 或 _Alignof 运算符的操作数仍然是函数指示符,违反了 6.5.3.4 中的约束。
结构类型不像数组那样工作 - 成员不是基于与基地址的数字偏移量来访问的。有一种完全不同的机制在起作用,因此struct foo
表达式不会像数组表达式或函数指示符那样“衰减”为指针类型。
- 如果你真的感兴趣,你可以阅读 Ritchie 在这篇文章中自己开发 C 的记录。
我认为主要原因是与数组相反的结构(和联合)类型的对象具有赋值运算符。
所以例如你可以写
struct A a1;
struct A a2;
a1 = a2;
如果结构类型的对象会衰减为指针,则此分配将没有意义。