在C/C++中,字节对齐是一种内存分配的策略。
当分配内存时,编译器会自动调整数据结构的内存分布,使得数据成员的起始地址与其自然对齐边界相匹配。
字节对齐的作用和好处
计算机的内存是一块用于存储数据的空间,由一系列连续的存储单元组成。
规定8个bit为一组,称为byte,并且将byte作为内存寻址的最小单元。这意味着每个byte一个编号,这个编号就叫内存的地址。
理论上,任何类型的变量都可以从任意地址开始存放,实际上访问特定类型的变量通常需要从特定对齐的内存地址开始。
因为如果不对数据存储进行适当的对齐,可能会导致存取效率降低。
所以,各种数据类型需要按照一定的规则在内存中排列,而不是顺序地一个接一个排放,这种排列就是字节对齐。
字节对齐有助于提高内存访问速度,因为许多处理器都优化了对齐数据的访问,当然这可能会导致内存空间的浪费。
字节对齐规则
自然对齐边界
对于基本数据类型,其自然对其边界通常为其大小。
结构体对齐
结构体内部的每个成员都根据其自然对齐边界进行对齐。
这意味着,在成员之间可能插入填充字节。
结构体本身的总大小也会根据其最大对齐边界的成员进行对齐,以便在数组中正确的对齐。
联合体对齐
联合体的对其边界取决于其最大对其边界的成员。联合体的大小等于其最大大小的成员,因为联合体的所有成员共享相同的内存空间。
编译器指令
可以使用编译器指令更改默认的对齐规则,这个命令是全局生效的,减小数据结构的大小,但可能会降低访问性能。
对齐属性
在C++11或更高版本中,使用aligans
关键字为数据结构或变量指定对齐要求。这个命令是对某个类型或者对象生效的。
动态内存分配
大多数内存分配函数会自动分配足够对齐的内存,以满足任何数据类型的对其要求。
字节序
字节序是指在多字节数据类型(如整数、浮点数等)中,字节在内存中的存储顺序。
主要有两种字节序:大端字节序(Big-endian)和小端字节序(Little-endian)。
大端字节序(Big-endian)
高位字节存储在低地址处,低位字节存储在高地址处。例如,一个4字节的整数0x12345678,在大端字节序的系统中,内存布局如下(从左侧的低地址到右侧的高地址):
1 |
|
大端字节序是符合人类阅读习惯的顺序。
小端字节序(Little-endian)
低位字节存储在低地址处,高位字节存储在高地址处。
常见的大小端字节序
在计算机领域中,不同的系统、平台和协议使用不同的字节序。下面是一些常见情况的字节序:
网络传输
在网络传输过程中,通常使用大端字节序(Big-endian),也称为网络字节序,这是 TCP/IP 协议的规定,多字节数据在网络上传输时使用大端字节序。
因此,如果本地系统使用的是小端字节序,那么就需要在传输之前将其转换为大端字节序。
一般通过使用htonl()、htons()、ntohl()和ntohs()等函数来完成。
Linux
Linux 操作系统在不同的硬件平台上可能使用不同的字节序。例如,x86 和 x86_64(Intel 和 AMD 处理器)是小端字节序(Little-endian),而 PowerPC 和 SPARC 等其他架构可能使用大端字节序(Big-endian)。
所以具体的字节序取决于运行 Linux 的硬件平台。
Windows
Windows 操作系统主要运行在 x86 和 x86_64(Intel 和 AMD处理器)架构上,这些处理器使用小端字节序(Little-endian)。
Mac
一般使用 Intel 处理器或 Apple 自家的 M1 芯片(基于ARM架构),这些处理器都采用小端字节序(Little-endian)。