我以为我真的理解这一点,并重新阅读标准(ISO 9899:1990)只是证实了我明显错误的理解,所以现在我问这里.
以下程序崩溃:
#include#include typedef struct { int array[3]; } type1_t; typedef struct { int *ptr; } type2_t; type1_t my_test = { {1, 2, 3} }; int main(int argc, char *argv[]) { (void)argc; (void)argv; type1_t *type1_p = &my_test; type2_t *type2_p = (type2_t *) &my_test; printf("offsetof(type1_t, array) = %lu\n", offsetof(type1_t, array)); // 0 printf("my_test.array[0] = %d\n", my_test.array[0]); printf("type1_p->array[0] = %d\n", type1_p->array[0]); printf("type2_p->ptr[0] = %d\n", type2_p->ptr[0]); // this line crashes return 0; }
比较表达式my_test.array[0]
并type2_p->ptr[0]
根据我对标准的解释:
6.3.2.1数组下标
"下标运算符[]的定义是E1 [E2]与(*((E1)+(E2)))相同."
应用这个给出:
my_test.array[0] (*((E1)+(E2))) (*((my_test.array)+(0))) (*(my_test.array+0)) (*(my_test.array)) (*my_test.array) *my_test.array type2_p->ptr[0] *((E1)+(E2))) (*((type2_p->ptr)+(0))) (*(type2_p->ptr+0)) (*(type2_p->ptr)) (*type2_p->ptr) *type2_p->ptr
type2_p->ptr
有类型"指向int的指针",值是起始地址my_test
.*type2_p->ptr
因此,求值为整数对象,其存储位于相同的地址my_test
.
进一步:
6.2.2.1左值,数组和函数指示符
"除非它是sizeof运算符或一元&运算符的操作数,...,具有类型的左值
array of type
转换为一个表达式,其类型pointer to type
指向数组对象的初始元素,而不是左值."
my_test.array
具有类型"array of int"并且如上所述转换为"指向int的指针",其中第一个元素的地址作为值.*my_test.array
因此,求值为整数对象,其存储位于与数组中第一个元素相同的地址.
最后
6.5.2.1结构和联合说明符
指向适当转换的结构对象的指针指向其初始成员...,反之亦然.在结构对象中可能存在未命名的填充,但不是在其开始处,以实现适当的对齐.
由于第一个成员type1_t
是数组,因此该type1_t
对象的起始地址和整个对象与上述相同.因此,我的理解是*type2_p->ptr
求值为一个整数,其存储位于与数组中第一个元素相同的地址,因此与之相同*my_test.array
.
但事实并非如此,因为程序在solaris,cygwin和linux上使用gcc版本2.95.3,3.4.4和4.3.2一直崩溃,因此任何环境问题都是完全不可能的.
我的推理在哪里错了/我什么不明白?如何声明type2_t使ptr指向数组的第一个成员?
如果我忽略了你的分析中的任何内容,请原谅我.但我认为所有这一切的基本错误都是错误的假设
type2_p-> ptr的类型为"指向int的指针",值为my_test的起始地址.
没有任何东西可以使它具有这种价值.相反,它很可能是指向某个地方
0x00000001
因为你要做的是将构成整数数组的字节解释为指针.然后你添加一些东西和下标.
另外,我非常怀疑你对其他结构的转换实际上是有效的(因为,保证可以工作).如果它们都是联合的成员,您可以强制转换然后读取任一结构的公共初始序列.但他们不在你的榜样.您也可以转换为指向第一个成员的指针.例如:
typedef struct { int array[3]; } type1_t; type1_t f = { { 1, 2, 3 } }; int main(void) { int (*arrayp)[3] = (int(*)[3])&f; (*arrayp)[0] = 3; assert(f.array[0] == 3); return 0; }
数组是一种存储.从语法上讲,它用作指针,但在物理上,该结构中没有"指针"变量 - 只有三个整数.另一方面,int指针是存储在struct中的实际数据类型.因此,当你执行强制转换时,你可能正在使ptr取得数组中第一个元素的值,即1.
*我不确定这实际上是定义的行为,但至少它是如何在大多数常见系统上运行的.