Little-Endian
在几乎所有的机器上,多字节对象都被存储为连续的字节序列。与之相应的就是连续字节的排序方式。
内存地址是线性增长的(从虚拟内存的角度看)。同样,连续字节的排布也是线性的。
所以就有了两种方案:字节的排布方向和内存的增长方向一致,或者相反。
具体内容可以查看Wiki。
0x00 小端序
当内存的地址增长方向和字节的排布顺序一致时,称为小端序。
简单来说,就是”高高低低” – 字节的高位被放置在内存的高地址处,字节的低位被放置在内存的低地址处。
虽然道理很简单,但是在实际测试中事实和预期并不相同:
- int型(4字节或8字节)的确按照小端序,比如0x11223344,0x11的地址比0x44高。
- char类型只有一个字节(ASCII编码时),所以理所当然的就没有高低之分了。
- string类型(
char *
或char[]
)却表现相反,比如字符串”ABCD”,A的地址比D的地址低。
再考虑更实际的场景:如果使用结构体呢?如果我仅仅是malloc一段内存呢?
假设malloc了一个内存页大小的内存,里面的内容又是按什么顺序排列的呢?
答案很简单:未知
0x01 最小分割单位
回忆最开始提到的,大端序和小端序都是为了存储多字节对象设计的方案。
所以考虑字节序的时候,并不应该以内存片段为中心,而是应该是字节对象。
在C语言中,字节对象对应的就是各种变量类型:int, char, size_t, …
这些变量类型是字节序的最小分割单位。如果一个类型是几种类型复合而成,变量与变量之间的顺序由编译器决定(一般是按顺序排列,即第一个变量位于低地址处),变量自身的存储方式由字节序决定。
int和char都是最小的分割单位,所以在实际测试中,0x11223344中0x11的确位于高地址处。
char[]
类型则是复合类型,本质上是由一堆char类型复合而成(字符数组),所以”ABCD”中字符A实际是数组中的第一个变量,根据编译器的决定,放置在低地址处。
更经典的例子是int[]
类型。
假设一个int类型为4字节。如果有变量int foo[2]
,并且foo[0] = 0x11223344
,foo[1] = 0x55667788
,那么在内存中变量foo的实际情况如下:
0 0x44
1 0x33
2 0x22
3 0x11
4 0x88
5 0x77
6 0x66
7 0x55
0x4433221188776655
至于malloc的内存,完全由写入方式决定。
结构体也是一样的道理。