字节顺序是指多字节的值在硬件中的存储顺序. 一般分为大端(big-endian)和小端(little-endian).
大端: 先存储高字节(Most significant bit), 或者说, 高字节存储在低地址, 低字节存储在高地址.
小端: 先存储低字节(Least significant bit), 或者说, 低字节存储在低地址, 高字节存储在高地址.
以4字节的数字0x01020304为例, 起始地址为n, 请见下表:
大端 |
|
||||||||||
小端 |
|
大端或者小端是由CPU决定的, 请见下表:
大端 | 小端 | 其中之一 |
AVR32 FR-V H8300 PA-RISC S390 Motorola 680x0 SPARC |
Alpha CRIS Blackfin Intel 64 IA-32 (x86) MN10300 |
ARM SuperH (sh) M32R MIPS Xtensa PowerPC |
对于PowerPC, 可以被配置使用大端还是小端, Linux选择大端.
大端或者小端的存储方式, 对于写应用软件的程序员来讲, 是透明的, 无需关心. 而当两台存储方式不同的机器之间传输多字节的值时, 就要用同一种存储方式. 对于TCP/IP协议, 使用大端方式.(不是所有的通讯协议, 都使用大端, 例如呢? fix me!)
位操作像加减乘除一样, 跟数据的存储方式是没有关系的. 请见以下的测试代码及输出.
#include <stdio.h> union u { int i; char c[sizeof(int)]; }; void printU(union u* pu); int main() { union u foo; foo.i = 1; if (foo.c[0] == 1) printf("Little endian\n"); else printf("Big endian\n"); foo.i = 270; printU(&foo); printf("%d\n", foo.i / 256); printf("%d\n", foo.i >> 8); printf("%d\n", foo.i % 256); printf("%d\n", foo.i & 0xff); return 0; } void printU(union u* pu) { int j; for (j = 0; j < sizeof(int); ++j) printf("%.2x", pu->c[j]); printf("\n"); }
程序输出:
aix>xlc t.c aix>./a.out Big endian 0000010e 1 1 14 14
可以使用以下这段代码来检测计算机是大端还是小端:
#include <stdio.h> int main() { union { int i; char c[sizeof(int)]; } foo; foo.i = 1; if (foo.c[0] == 1) printf("Little endian\n"); else printf("Big endian\n"); int j; for (j = 0; j < sizeof(int); ++j) printf("%.2x", foo.c[j]); printf("\n"); return 0; }
如果foo.c[0]为1, 说明整型i的低字节存储在低地址, 所以是小端.(数组中, 地址&foo.[1]一定大于&foo.[0]吗? fix me!)
我的机器使用的CPU是intel, 分别在win7和ubuntu上测试, 输出均为:
Little endian 01000000
低字节的1存储在低地址&foo.c[0].
我找到另一台大端的机器, 输出为:
Big endian 00000001
低字节的1存储在高地址&foo.c[3]
首先写一个很简单的测试代码:
int main() { int i = 0x01020304; return 0; }
在intel内核的小端中, 使用gdb进行调试:
mjn@mjn-desktop:~/codes/c/test$ gcc -g byteorder2.c mjn@mjn-desktop:~/codes/c/test$ gdb ./a.out GNU gdb (GDB) 7.1-ubuntu Copyright (C) 2010 Free Software Foundation, Inc. ... Reading symbols from /home/mjn/codes/c/test/a.out...done. (gdb) start Temporary breakpoint 1 at 0x80483ba: file byteorder2.c, line 2. Starting program: /home/mjn/codes/c/test/a.out Temporary breakpoint 1, main () at byteorder2.c:2 2 int i = 0x01020304; (gdb) n 3 return 0; (gdb) x/4xb &i 0xbffff404: 0x04 0x03 0x02 0x01
gdb中的'x/4xb 地址'表示以16进制的形式显示从'地址'开始的4个字节的内容. 低字节的0x04在低地址, 所以是小端.
在大端的环境上, 使用dbx进行调试:
AIX5.3-mjn > xlc -g byteorder2.c AIX5.3-mjn > dbx ./a.out Type 'help' for help. reading symbolic information ... (dbx) stop in main [1] stop in main (dbx) run [1] stopped in main at line 2 2 int i = 0x01020304; (dbx) n stopped in main at line 3 3 return 0; (dbx) &i /4c 0x2ff22850: '^A' '^B' '^C' '^D' (dbx) &i /2x 0x2ff22850: 0102 0304 (dbx) &i /1X 0x2ff22850: 01020304
'&i /4c'表示以字符的形式显示地址&i的后面4个字节的内容. 其中'^A'的值为1, '^D'的值为4, 低字节0x04存储在高地址, 所以是大端.
References:
Bruce Blinn: Byte Order
维基百科: Endianness