以下是有问题的节目:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct x{
int i1;
int i2;
char ch1[8];
char ch2[8];
};
struct y{
long long int f1;
char f2[18];
};
int main(void)
{
struct x * myX;
struct y * myY;
myX = malloc(sizeof(struct x));
myX->i1 = 4096;
myX->i2 = 4096;
strcpy(myX->ch1,"Stephen ");
strcpy(myX->ch2,"Goddard");
myY = (struct y *) myX;
printf("myY->f1 = %llx\n", myY->f1);
printf("myY->f2 = %s\n", myY->f2);
}
这是输出:
myY -> f1 = 100000001000
myY -> f2 = Stephen Goddard
我的问题是这个类型转换是如何工作的?我不明白f1的输出。 4096是二进制的100000000000和十六进制的1000,所以它将i1和i2组合成一个long long int?如果是这样,为什么它以这种方式结合?正在构建的结构是否只是寻找类似的变量并尝试将它们全部塞入一个变量中?这个过程如何运作?任何帮助将不胜感激!
答案 0 :(得分:1)
此代码导致未定义的行为。 C有一个名为 strict aliasing 的规则。换句话说,标准没有定义如果您通过int
表达式写入并通过long long
表达式读取会发生什么。
传统上大多数编译器“只是这样做”,即它会将两个int
写入这两个内存位置,然后从同一位置读取long long
的足够字节,并且希望这一切都有意义(它通常会这样做,因为现代系统是这样的,每个可能的位模式都代表一个有效的数字)。
然而,一些编译器完全优化了步骤,因为他们知道严格的别名规则。
进一步的考虑是在i1
和i2
等之间可能存在填充,但是您可以通过使用offsetof
宏或{{1}来验证没有填充在继续之前检查。
要以明确定义的方式做你想做的事,你可以使用sizeof
或使用联合,例如:
memcpy
如果你的系统有unpadded 2的补码(所有现代系统都有),并且你的结构成员之间没有填充,32位int,64位long long和big-endian int,则4096是{{ 1}}十六进制,所以8字节的块是union xy
{
struct x x;
struct y y;
};
union xy *ptr = malloc(sizeof *ptr);
ptr->x.i1 = 4096;
ptr->x.i2 = 4096;
printf("%llx\n", (unsigned long long)ptr->y.f1);
,正如您在输出中看到的00 00 10 00
。实际上它在小端也是如此。
注意,00 00 10 00 00 00 10 00
用于打印0x100000001000
,它会导致未定义的行为与签名的%llx
一起使用,但是我知道的所有编译器都会“正常工作”。为了严格正确,我添加了一个演员阵容。 (考虑将结构成员更改为unsigned long long
和long long
以避免符号位出现问题。)