Big Endian 和 Little endian 有点混淆

Big Endian and Little endian little confusion

我正在从该站点阅读有关小端和大端表示的信息 http://www.geeksforgeeks.org/little-and-big-endian-mystery/

假设我们有一个数字 0x01234567,那么在小端存储为 (67)(45)(23)(01),在大端存储为 (01)(23)(45)( 67).

char *s= "ABCDEF"
int *p = (int *)s;
printf("%d",*(p+1)); // prints 17475 (value of DC)

在看到上面代码中的打印值后,字符串似乎存储为 (BA)(DC)(FE)。

为什么不像第一个例子那样从 LSB 到 MSB 存储为 (EF)(CD)(AB)?我认为字节顺序意味着多字节内的字节排序。因此,排序应该与第二种情况下的 "whole 2 bytes" 相关,而不是在这 2 个字节内,对吗?

在讨论 s 指向的 char const 数组中存储字节时,字节序不会发挥作用。如果您检查 *s 处的内存,您会发现字节 'a''b''c' ...,当被解释为小端字节序的 int 时system 但是它会被解释为 "DCBA"

记住每个 char 已经是一个字节,如果你有 char const * s = "0xfedcab09"; 并且你在小端系统上做了 printf("%d", *(int const *)s); 那么它会打印出任何 0x9abcdef 作为十进制。

使用 2 个字节 ints,这就是你在内存中的内容

memAddr  |  0  |  1  |  2  |  3  |  4  |  5  |  6   |
data     | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | '[=10=]' |
            ^ s points here
                        ^ p+1 points here

现在,您似乎正在使用 ASCII 编码,所以这就是您真正记忆中的内容

memAddr  |  0   |  1   |  2   |  3   |  4   |  5   |  6   |
data     | 0x41 | 0x42 | 0x43 | 0x44 | 0x45 | 0x46 | 0x00 |
            ^ s points here
                          ^ p+1 points here

所以对于小端机器,这意味着对于多字节类型,最低有效字节排在第一位。单个字节 char 没有字节顺序的概念。一个 ASCII 字符串只是一个 char 的字符串。这没有字节顺序。你的 ints 是 2 个字节。因此对于从内存位置 2 开始的 int,这个字节是最低位的,而地址 3 处的字节是最高位的。这意味着这里的数字,阅读人们通常阅读数字的方式,是 0x4443(17475 以 10 为基数,"DC" 作为 ASCII 字符串),因为内存位置 3 中的 0x44 比内存位置 2 中的 0x43 更重要。对于big endian,当然,这将被颠倒,数字将是 0x4344(17220 以 10 为基数,"CD" 作为 ASCII 字符串)。

编辑:

正在处理您的评论... c 字符串是 NUL 终止的 char 数组,这是绝对正确的。 Endianess 只适用于基本类型,short, int, long, long long 等("primitive types" 可能是不正确的命名法,知道的人可以指正我)。数组只是连续内存的一部分,其中 1 种或多种类型彼此直接相邻,存储 顺序 。整个数组没有字节顺序的概念,但是,字节顺序 do 适用于数组各个元素的基本类型。假设您有以下内容,假设 2 字节 ints:

int array[3];  // with 2 byte ints, this occupies 6 contiguous bytes in memory
array[0] = 0x1234;
array[1] = 0x5678;
array[2] = 0x9abc;

这是内存的样子:无论是大端还是小端机器,它都会是这样的

memAddr   |    0-1   |    2-3   |    4-5   |
data      | array[0] | array[1] | array[2] |

请注意,数组 元素 没有字节顺序的概念。无论元素是什么,这都是事实。元素可以是基本类型,structs,任何东西。数组中的第一个元素始终位于 array[0].

但是现在,如果我们查看数组中的实际内容,这就是字节顺序发挥作用的地方。对于小端机器,内存将如下所示:

memAddr   |  0   |  1   |  2   |  3   |  4   |  5   |
data      | 0x34 | 0x12 | 0x78 | 0x56 | 0xbc | 0x9a |
             ^______^      ^______^      ^______^
             array[0]      array[1]      array[2]

最低有效字节在前。大端机器看起来像这样:

memAddr   |  0   |  1   |  2   |  3   |  4   |  5   |
data      | 0x12 | 0x34 | 0x56 | 0x78 | 0x9a | 0xbc |
             ^______^      ^______^      ^______^
             array[0]      array[1]      array[2]

注意数组的每个元素的 contents 是字节序的(因为它是原始类型的数组..如果它是 structs 的数组, struct 成员不会受到某种字节序反转的影响,字节序仅适用于基元)。但是无论是大端还是小端,数组元素的顺序都是一样的

回到您的字符串,字符串只是一个以 NUL 结尾的字符数组。 chars 是单个字节,因此只有一种方法可以对它们进行排序。考虑代码:

char word[] = "hey";

这是你记忆中的内容:

memAddr   |    0    |    1    |    2    |    3    |
data      | word[0] | word[1] | word[2] | word[3] |
                  equals NUL terminator '[=17=]' ^

就在这种情况下,word 数组的每个元素都是一个字节,并且只有一种方法可以对单个项目进行排序,所以无论是在小端还是大端机器上,这就是你记忆中会有:

memAddr   |  0   |  1   |  2   |  3   |
data      | 0x68 | 0x65 | 0x79 | 0x00 |

Endianess 仅适用于多字节原始类型。我强烈建议在调试器中四处查看以查看实际操作。所有流行的 IDE 都有内存视图 windows,或者 gdb 你可以 print out memory。在 gdb 中,您可以将内存打印为字节、半字(2 字节)、字(4 字节)、巨型字(8 字节)等。在小端机器上,如果您将字符串打印为字节,您将按顺序查看字母。打印为半字,你会看到每 2 个字母 "reversed",打印为单词,每 4 个字母 "reversed",等等。在大端机器上,它会以相同的方式打印出来 "readable"订单。

这里出现的混淆是由于符号

字符串“ABCDEF”可以用多种方式解释(和存储)。

在一个字符串中每个字母占一个完整字节(char).

char s[] = { 'A', 'B', 'C', 'D', 'E', 'F', 0 };

但是数字的十六进制表示ABCDEF是不同的,每个数字('0'..'9'和'A'..'F') 只代表四位,或半个字节。因此,number 0xABCDEF 是字节序列

0xAB 0xCD 0xEF

这就是 endianness 成为问题的地方:

  • Little Endian:最低有效字节优先
    int x = { 0xEF, 0xCD, 0xAB };
  • Big Endian:最高有效字节在前
    int x = { 0xAB, 0xCD, 0xEF }
  • 混合字节序:<其他随机排序>
    int x = { 0xEF, 0x00, 0xCD, 0xAB }

这串字符好像有点混乱

1)  "ABCDEF"

11259375用十六进制表示是

2)  0xABCDEF

第一种情况,每个字母占一个字节。
在第二种情况下,我们有六个十六进制数字;一个十六进制数字占4位,所以一个字节需要两个数字。

Endianness 明智的,以防万一
1) 字符 'A',然后 'B' 等依次写入内存。 'A'为0x41,'B'为0x42...万一
2) 这是一个多字节整数,字节顺序取决于体系结构。假设数字是 4 个字节,大端弧将存储在内存中(十六进制)00 AB CD EF; little-endian 将按以下顺序存储:EF CD AB 00

大端

A  B  C  D  E  F
41 42 43 44 45 46   [ text ]
00 AB CD EF         [ integer ] 
----(addresses)---->

小端

----(addresses)---->
A  B  C  D  E  F
41 42 43 44 45 46   [ text ]
EF CD AB 00         [ integer ]

你的情况

char *s= "ABCDEF";     // text
int *p = (int *)s;     //
printf("%d",*(p+1));   // *(p+1) is p[1]

由于您的实现有 sizeof(int) == 2,打印的数字 (17475) 是 0x4443,或 'DC'(字符),具有 0x44('D')作为 MSB和 0x43 ('C') 因为 LSB 表明你的架构是小端的。

在内存中(顺序地)写入一串字符并读取其中的几个字符作为 int 给出一个取决于字节顺序的数字。是的,在这种情况下,字节顺序很重要