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 个字节 int
s,这就是你在内存中的内容
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
的字符串。这没有字节顺序。你的 int
s 是 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 字节 int
s:
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
结尾的字符数组。 char
s 是单个字节,因此只有一种方法可以对它们进行排序。考虑代码:
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
给出一个取决于字节顺序的数字。是的,在这种情况下,字节顺序很重要。
我正在从该站点阅读有关小端和大端表示的信息 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 个字节 int
s,这就是你在内存中的内容
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
的字符串。这没有字节顺序。你的 int
s 是 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 字节 int
s:
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
结尾的字符数组。 char
s 是单个字节,因此只有一种方法可以对它们进行排序。考虑代码:
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
给出一个取决于字节顺序的数字。是的,在这种情况下,字节顺序很重要。