Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/arrays/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 将数组强制转换为结构时的数据顺序_C_Arrays_Casting_Struct - Fatal编程技术网

C 将数组强制转换为结构时的数据顺序

C 将数组强制转换为结构时的数据顺序,c,arrays,casting,struct,C,Arrays,Casting,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; 和一些数据数组(内容仅供演示): 然后我做了一个动作: MY_STRUCT *ms; m

假设我有以下结构:

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;
和一些数据数组(内容仅供演示):

然后我做了一个动作:

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);
我希望数据按顺序读入ms,“从左到右”,在这种情况下,输出为:

a is: 11
b is: 1100
c is: 10FFF
然而,实际发生的情况是:

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

为什么会发生这种情况?当以这种方式将数组强制转换为结构时,我应该期望什么行为?

这是因为您正在执行这段代码的机器具有很小的端字节顺序。这意味着它以相反的顺序存储字节

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

英特尔x86是一个小小的endian架构

您的
a
b
之所以正确,是因为您在创建数据时存储了
short
,然后再次加载
short
。对于c,它有点不同。您存储了2条
短裤
,但随后尝试将其作为长码加载。您没有存储
短路
,因为如果它们长时间合并,处理器会存储它们,以便反转

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

答案是,视情况而定。欢迎来到Endian ness的精彩世界:

要点是,您假设数据是以预期人类阅读的方式存储的。这是big endian。但是,您可能在x86机器上,这是little endian。这意味着最高有效位在4字节的末尾,而不是开始。这就是为什么你的下半部卖空会在上半部卖空之前出现


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

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

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

如果数据未存储在这样一个偶数地址上,则数据未对齐,这将导致大多数主流32/64位CPU(86、PowerPC、ARM等)的CPU性能不佳,或者代码甚至无法执行(罕见的情况下,我认为某些MIPS CPU适用?)

因此,在优化过程中,编译器尝试将结构的所有成员存储在对齐的地址上。这是C标准所允许的:编译器可以自由添加称为padding bytes的内容,这实际上只是在结构成员之间分配的垃圾空间

在您的示例中,32位big-endian 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

如您所见,试图将这个内存块解释为“代码>短的数组会给您带来麻烦,因为最终会在数组中间填充字节。


因此,从形式上讲,在结构和数据数组之间转换是未定义的行为和糟糕的做法。但是有各种非标准扩展允许您禁用结构填充,最常见的是。如果您调用这样一个非标准的编译器设置,那么您的代码将在实践中工作

像x86这样的小端机器以相反的顺序存储多字节类型。幸运的是,编译器没有32位对齐主要成员;不大。endianness传统上用于描述首先存储的内容(“最后一个”就是这样的结果)。即,小尾端表示首先存储最低有效字节;big-endian表示最重要的字节首先存储。我刚刚实现并编辑了它。这个故事的寓意是不要试图在凌晨2:40回答这个问题。哈哈。这仍然给了我两个小时。但是,如果您使用8位或16位CPU,它们可能没有对齐要求,因此没有结构填充。但是C标准是通用的,与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