我试图理解以下陈述在C99标准中的含义(C99; ISO / IEC 9899:1999 6.5 / 7)
对象的存储值只能由左值访问 具有以下类型之一的表达式73)或88):
(其他与问题无关的陈述)
包含上述内容之一的聚合或联合类型 其成员之间的类型(包括,递归地,成员 subaggregate或contains union)
请考虑以下事项:
typedef struct MyComplex {
float real;
float imag;
} MyComplex;
MyComplex *carray = malloc(sizeof(*carray) * 10);
float *as_floats = (float *)carray;
这是否合法,因为MyComplex
结构包含float
指针的兼容as_floats
类型?
另一种方式呢?即:
float *farray = malloc(sizeof(*farray) * 10);
MyComplex *as_complex = (MyComplex *)farray;
在这两种情况下,最终,我们在这里处理的所有内容都是float
,所以也许可以吗?我只是不确定。
我问,因为我正在处理遗留的代码库,它在所有地方都有这种东西,到目前为止,一切似乎都很好。但我觉得我们在这里玩火。试着弄清楚我是否需要在编译器命令行上禁用严格别名。
答案 0 :(得分:3)
6.7.2.1中的注释13(我相信)标准condones案例#1的所有版本都是明确的。你很少会得到更明确的答案! 我的重点
在结构对象中,非位字段成员和单位 哪些位字段驻留的地址按顺序增加 他们被宣布。 适当地指向结构对象的指针 已转换,指向其初始成员(或者如果该成员是 位字段,然后到它所在的单元,反之亦然。 结构对象中可能有未命名的填充,但不在其中 开始。
http://port70.net/~nsz/c/c99/n1256.html#6.7.2.1
YES!您可以构建一个结构来直接访问其第一个成员!
为什么有人认为比&(carray->real)
更好不清楚。但这绝对是合法的。
正如另一位评论者所指出,我在之前的一个问题中讨论过案例#2。
Aliasing Arrays through structs
结论是,案例#2是访问成员real
的好方法。
此外,如果结构没有内部填充(取决于平台),您甚至可以通过imag
访问数组的第二个成员。
我说平台依赖,但其他调查没有提供这样的结构 包括填充的已知平台。
答案 1 :(得分:1)
我尝试使用这种投射方法重现一些问题。 一个问题是当struct使用位字段和另一个时 - 当有编译器指令强制结构成员对齐到某个大小时:
// issue 1 : problem with bitFields
typedef struct MyComplex_1 {
unsigned int real : 1;
unsigned int imag : 2;
} MyComplex_1;
// issue 2 : problem with members forced alignment
typedef struct MyComplex_2 {
unsigned int real;
unsigned int imag;
} __attribute__ ((aligned (64))) MyComplex_2; // GCC compiler directive
int main()
{
MyComplex_1 arr_1[] = {{1, 2}, {1, 2}};
MyComplex_2 arr_2[] = {{1, 2}, {1, 2}};
int i;
// for bitfield issue
for (i=0; i<2; i++)
printf("struct (%d,%d)\n", arr_1[i].real, arr_1[i].imag);
for (i=0; i<4; i++)
printf("var (%d)\n", ((unsigned int*)arr_1)[i]);
// for struct member forced alignement issue
for (i=0; i<2; i++)
printf("struct (%d,%d)\n", arr_2[i].real, arr_2[i].imag);
for (i=0; i<4; i++)
printf("var (%d)\n", ((unsigned int*)arr_2)[i]);
return 0;
}
可能会有更多问题,但在我看来这件事有风险。
答案 2 :(得分:0)
我同意你玩火,但不是因为你引用的标准部分。
任何对象,包括作为聚合类型(数组或结构)对象组件的对象,都以地址和类型为特征。在给出声明的情况下,考虑访问这些形式(对于0 <= n <10):
/* 1 */
carray[n].real
/* 2 */
asfloats[n * 2]
标准说上面的两种形式都是允许访问float
对象的方法,但它没有说明它们是否都引用相同的对象,或后者是否引用任何对象。他们可能都被允许,可能甚至意味着相同的东西,但这取决于struct MyComplex
的实现定义表示(除了{{1}时}为零,这两个总是被允许和等价的。)
例如,如果n
具有四字节表示,并且编译器恰好填充float
表示为16字节的倍数,那么类型(2)的一些访问(旨在<{1}}成员通过struct
)将无效。
访问real
成员会更糟糕:如果编译器选择在8字节边界上对齐每个成员,那么也可以使用它,因此需要在float *
之间填充和imag
。
另请注意,填充不一定是实现的固定特征。许多编译器提供了影响其填充决策的选项,因此您使用的代码是否已定义行为可能取决于您的编译器选项。