在 C 中,“它工作正常”还不够好。因为你的编译器被允许这样做:
struct newData
{
int x;
char padding1[523];
int y;
char padding2[364];
int z;
char padding3[251];
};
当然,这是一个极端的例子。但是你明白了。不能保证您的循环会正常工作,因为不能保证 struct newData 等同于 int[3]。
所以不,这在一般情况下是不可能的,因为在特定情况下并不总是可能的!
现在,您可能会想:“这是什么白痴决定的?!”好吧,我不能告诉你,但我可以告诉你原因。计算机彼此非常不同,如果您希望代码快速运行,那么编译器必须能够选择如何编译代码。这是一个例子:
处理器 8 有一条指令来获取单个字节,并将它们放入寄存器中:
GETBYTE addr, reg
这个结构很好用:
struct some_bytes {
char age;
char data;
char stuff;
}
struct some_bytes可以愉快的占用3个字节,而且代码速度很快。但是处理器 16 呢?它没有GETBYTE,但它有有GETWORD:
GETWORD even_addr, reghl
这个只接受偶数地址,读取两个字节;一个进入寄存器的“高”部分,一个进入寄存器的“低”部分。为了使代码更快,编译器必须这样做:
struct some_bytes {
char age;
char pad1;
char data;
char pad2;
char stuff;
char pad3;
}
这意味着代码可以运行得更快,但这也意味着你的循环将无法工作。不过没关系,因为它叫做“未定义的行为”;允许编译器假设它永远不会发生,如果确实发生了,则行为是未定义的。
事实上,您已经遇到过这种行为!您的特定编译器正在这样做:
struct newData
{
int x;
int pad1;
int y;
int pad2;
int z;
int pad3;
};
因为您的特定编译器将long int 定义为int 长度的两倍,所以您可以这样做:
| x | pad | y | pad | z | pad |
| long no.1 | long no.2 | long no.3 |
| int | | int | | int |
从我不稳定的图表可以看出,该代码是不稳定的。它可能在其他任何地方都行不通。更糟糕的是,如果你的编译器很聪明,它可以做到这一点:
for (int i=0; i<3; i++)
{
printf("%d \n", *(addr+i));
}
嗯...addr 来自data2,data1 是指向struct newData 的指针。 C 规范说只有指向结构开头的指针才会被取消引用,所以我可以假设 i 在这个循环中始终是 0!
for (int i=0; i<3 && i == 0; i++)
{
printf("%d \n", *(addr+i));
}
这意味着它只运行一次!万岁!
printf("%d \n", *(addr + 0));
我需要编译的是这个:
int main()
{
printf("%d \n", 10);
}
哇,程序员会很高兴我能大大加快这段代码的速度!
你不会高兴的。事实上,你会得到意想不到的行为,并且无法找出原因。但是如果您编写的代码没有未定义行为,并且您的编译器也做了类似的事情,您会很高兴。所以它保持不变。