字节顺序(byte order)

概念

字节顺序是指多字节的值在硬件中的存储顺序. 一般分为大端(big-endian)和小端(little-endian).

大端: 先存储高字节(Most significant bit), 或者说, 高字节存储在低地址, 低字节存储在高地址.

小端: 先存储低字节(Least significant bit), 或者说, 低字节存储在低地址, 高字节存储在高地址.

以4字节的数字0x01020304为例, 起始地址为n, 请见下表:

大端
地址
n 0x01
n+1 0x02
n+2 0x03
n+3 0x04
小端
地址
n 0x04
n+1 0x03
n+2 0x02
n+3 0x01

大端或者小端是由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 

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 

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]

在debug中查看内存

首先写一个很简单的测试代码:

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

你可能感兴趣的:(C,Network,Programmiing,Operating,System)