-1

Say i have the following struct:

typedef struct MyStruct {

    unsigned short a; /* 16 bit unsigned integer*/
    unsigned short b; /* 16 bit unsigned integer*/
    unsigned long  c; /* 32 bit unsigned integer*/

}MY_STRUCT;

And some data array (the content only for demonstration):

unsigned short data[] = {0x0011, 0x1100, 0x0001, 0x0FFF }; 

Then i perform the folliwing:

MY_STRUCT *ms; 

ms = (MY_STRUCT *) data;

printf("a is: %X\n",(*ms).a);
printf("b is: %X\n",(*ms).b);
printf("c is: %X\n",(*ms).c);

I would expect the data to be read sequentially into ms, "left to right", in which case the output to be:

a is: 11
b is: 1100
c is: 10FFF

However what actually happens is:

a is: 11
b is: 1100
c is: FFF0001

Why does this happen? What behavior should i expect when casting arrays to structs this way?

4

3 回答 3

1

这是因为您正在执行这段代码的机器几乎没有字节序。这意味着它以相反的顺序存储其字节。

数字 0x4A3B2C1D 将存储为 0x1D 0x2C 0x3B 0x4A。

Intel x86 是一种小端架构。

a你的和b是正确的原因是因为你short在创建数据时存储了一个然后你shorts再次加载。对于 c,它有点不同。您存储 2 shorts,但随后您尝试将其加载为 long。您没有shorts存储它们,因为如果它们要组合成一个长的,那么处理器会存储它们,因此它们会被反转。

于 2013-10-07T07:36:42.167 回答
1

以这种方式将数组转换为结构时,我应该期待什么行为?

答案是,这取决于。欢迎来到 Endian-ness 的美妙世界: http ://en.wikipedia.org/wiki/Endianness

要点是,您假设数据以您期望人类阅读的方式存储。这是大端。但是,您可能使用的是 x86 机器,它是 little endian。这意味着最高有效数字位于 4 个字节的末尾,而不是开头。这就是为什么你的第二个空头出现在你的空头前半个之前。

使用此方法,您将在不同的架构上获得不同的结果。

于 2013-10-07T07:37:49.943 回答
0

正如其他人所解释的那样,结果取决于字节序。除此之外,您的代码是不安全的并且会调用未定义的行为。因为不能保证您可以从结构转换为短数组。

这是因为数据对齐。许多 CPU 更喜欢或要求在偶数地址上分配数据字节。例如,具有这种对齐要求的 32 位 CPU 会希望将数据存储在可被 4 整除的地址(地址对应于字节,4 字节 = 32 位)。

如果数据没有存储在这样的偶数地址上,就会出现错位,这将导致大多数主流 32/64 位 CPU(86、PowerPC、ARM 等)的 CPU 性能不佳,甚至可能导致代码无法执行(在极少数情况下,我认为某些 MIPS CPU 适用?)。

因此,在优化期间,编译器尝试将结构的所有成员存储在对齐的地址上。这是 C 标准允许的:编译器可以自由添加称为填充字节的东西,它本质上只是在结构成员之间分配的垃圾空间。

在您的示例中,用于 32 位大端 CPU 的编译器可以执行以下操作:

Address         Data
0x00000000      unsigned short a; MS byte
0x00000001      unsigned short b; LS byte
0x00000002      Padding byte
0x00000003      Padding byte
0x00000004      unsigned short b; MS byte
0x00000005      unsigned short b; LS byte
0x00000006      Padding byte
0x00000007      Padding byte
0x00000008      unsigned long  c; MS byte
0x00000009      unsigned long  c; 
0x0000000A      unsigned long  c; 
0x0000000B      unsigned long  c; LS byte

正如你所看到的,试图将这个内存块解释为一个数组short会给你带来问题,因为你最终会在数组中间得到填充字节。

如此正式地,在结构和数据数组之间进行转换是未定义的行为和不好的做法。但是有各种非标准扩展可以让你禁用结构填充,最常见的是#pragma pack. 如果您调用这样的非标准编译器设置,那么您的代码将在实践中工作。

于 2013-10-07T10:08:47.407 回答