有几个问题:
出于效率原因,编译器在边界上对齐变量,该边界等于处理器的寄存器大小。即在 32 位系统上,这将在 32 位(4 字节)边界上。此外,结构将具有“间隙”,以便结构成员可以在 32 位边界上对齐。换句话说:结构没有“打包”紧密。试试这个:
#include <stdio.h>
typedef struct
{
char one[5]; /* 5 bytes */
unsigned int two; /* 4 bytes */
}
structure;
structure my_structure;
char array[] =
{
0x41, 0x42, 0x43, 0x44, 0x00, /* ABCD\0 */
0x00, 0xbc, 0x61, 0x4e /* 12345678 (base 10) */
};
int main(int argc, char **argv)
{
const int sizeStruct = sizeof(structure);
printf("sizeof(structure) = %d bytes\n", sizeStruct);
const int sizeArray = sizeof(array);
printf("sizeof(array) = %d bytes\n", sizeArray);
return 0;
}
你应该看到不同的尺寸。
您可以使用#pragma 或属性指令覆盖此行为。使用 gcc,您可以使用属性更改结构定义。例如,更改上面的代码以添加“打包”属性(需要 gcc):
typedef struct __attribute__((packed))
然后再次运行程序。现在大小应该是一样的。
注意:在某些处理器架构上,例如 ARMv4,32 位变量必须在 32 位边界上对齐,否则您的程序将无法运行(出现异常)。阅读“对齐”和“打包”编译指示或属性的编译器文档。
下一个问题是字节顺序。试试这个:
printf("0x%08X\n", 12345678);
十六进制的 12345678 是 0x00BC614E。从您的示例和您获得的输出中,我可以看出您的平台是“小端”。在“小端”系统中,数字0x00BC614E
存储为从最低有效字节开始的字节序列,例如0x4E, 0x61, 0xBC, 0x00
. 所以改变你的数组定义:
char array[] =
{
0x41, 0x42, 0x43, 0x44, 0x00, /* ABCD\0 */
0x4E, 0x61, 0xBC, 0x00, /* 12345678 (base 10) */
};
现在您的程序将打印 12345678。
另请注意,您应该使用 %u 打印无符号整数。
复制 char 字符串可能会引起许多蠕虫,尤其是当您必须允许使用不同的编码(例如 Unicode)时。至少,您需要确保您的复制目标缓冲区不会溢出。
修改后的代码:
#include <stdio.h>
#include <string.h>
typedef struct
{
char one[5]; /* 5 bytes */
unsigned int two; /* 4 bytes */
}
structure;
structure my_structure;
char array[] =
{
0x41, 0x42, 0x43, 0x44, 0x00, /* ABCD\0 */
0x4E, 0x61, 0xBC, 0x00, /* 12345678 (base 10) */
};
int main()
{
// copy string as a byte array
memcpy(&my_structure.one, &array[0], sizeof(my_structure.one));
// copy uint
my_structure.two = *((unsigned int *)(&array[5]));
printf("%s\n", my_structure.one);
printf("%u\n", my_structure.two);
return 0;
}
最后,依赖打包数据结构通常是个坏主意,因为这会使将代码移植到不同的平台变得困难。但是,有时您需要打包/解包协议数据包。在那些特殊情况下,使用每种数据类型的一对函数手动打包/解包每个项目通常是最好和最便携的。
我将把字节序问题留给另一个话题。:-)