C语言 大字节序和小字节序有点混淆

lymgl2op  于 2022-12-02  发布在  其他
关注(0)|答案(4)|浏览(198)

我在这个网站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)那样存储呢?我认为endianess意味着多字节内的字节排序。所以排序应该是关于“整个2字节”,就像第二个例子那样,而不是在这2字节内,对吗?

biswetbf

biswetbf1#

使用2字节int,这是内存中的内容

memAddr  |  0  |  1  |  2  |  3  |  4  |  5  |  6   |
data     | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | '\0' |
            ^ 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是2个字节。所以对于一个从内存位置2开始的int,这个字节是最低有效的。地址3的数字是最重要的,这意味着这里的数字,按照人们通常读数字的方式,是0x4443(17475为基数10,“DC”为ASCII字符串),因为内存位置3中的0x44比内存位置2中的0x43更重要。当然,对于大端序,这将反过来,并且该数字将是0x4344(17220以10为基数,“CD”作为ASCII字符串)。
编辑:
一个c字符串是一个以NUL结尾的char数组,这是绝对正确的。字节序只适用于基本类型,short, int, long, long long,等等。(“原始类型”可能是不正确的命名法,了解的人可以纠正我)数组只是连续内存的一部分,其中1个或多个类型直接彼此相邻出现,这个数组中的所有元素都是按顺序存储的。整个数组中没有字节存储顺序的概念,但是字节存储顺序
适用于数组中各个元素的基本类型。假设您有以下内容,假定为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] |

注意,数组 elements 没有字节序的概念。无论元素是什么,这都是正确的。元素可以是基本类型,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 都受endiance的影响(因为它是一个基本类型的数组..如果它是一个structs的数组,struct成员就不会受某种endiance反转的影响,endiance只适用于基本类型)。然而,无论是在big endian机器上还是在little endian机器上,数组元素的顺序都是相同的。
回到字符串,字符串就是一个以NUL结尾的字符数组。char是单字节的,所以只有一种方法来排序它们。请看下面的代码:

char word[] = "hey";

这是你记忆中的内容:

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

在本例中,word数组中的每个元素都是一个字节,并且只有一种方法来对单个项进行排序,因此无论是在小端还是大端机器上,内存中都将包含以下内容:

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

Endianess只适用于多字节原语类型。我强烈建议在调试器中查看这一点。所有流行的IDE都有内存视图窗口,或者使用gdb可以print out memory。在gdb中,可以将内存打印为字节、半字(2个字节),字(4字节),巨字(8个字节)等等。在小字节序机器上,如果你把字符串打印成字节,你会看到字母是按顺序排列的。打印成半字,你会看到每两个字母“颠倒”,打印成字,每4个字母“颠倒”一次,等等。在大端序机器上,它都会以相同的“可读”顺序打印出来。

qmb5sa22

qmb5sa222#

2022年编辑

处理器不知道它在处理什么类型的数据,文本还是数字,所有的东西在内存中都是数字。
(特殊的CPU指令处理浮点数,期望4 / 8...字节符合某种标准,但无论如何,在内存中,它们只是8位字节,值从0到255,就像其他所有东西一样)
传统上

the letter 'A' is represented by the hex number 0x41 (65 decimal)
...     
the letter 'F' is represented by the hex number 0x46 (70 decimal)

文本“ABCDEF”在内存中显示为(十六进制)(在C中,编译器在“F”后面添加一个字节0)

----- addresses ----->
|41|42|43|44|45|46|00|
----------------------

在一个2字节的int小端系统上,在i变量中阅读p+1(您的示例),

---- addresses --->
|41|42|43|44|45|46|
|p + 0|p + 1|p + 2|        
-------------------

因为它是小端系统i值为

0x4443 (17475 in decimal, the value you saw)

读取的第一个字节是int的最低有效字节(0x 43),下一个字节是最高有效字节(MSB,0x 44)。
请注意,在big-endian系统上,它将是

0x4344 (17220 in decimal)

现在,也许你想在内存中存储数字0xABCDEF(十六进制,十进制11259375),我们至少需要一个32位的int来将这个值存储在一个变量中;假设编译器long类型有32位。

long l = 0xABCDEF;

在内存中(小端序),数字的存储方式如下

------------>
|EF|CD|AB|00|
-------------

请注意,还有一个“尾随”0,但在这种情况下,它是该数字的MSB,在32位上为0。
在这种情况下,您会找到您所期望的(EF)(CD)(AB),因为编译器将代码字0xABCDEF视为一个数字。在另一种情况下,编译器将文本“ABCDEF”视为一个字符串。

2017年原始答案

字符串之间似乎有点混淆

1)  "ABCDEF"

而以十六进制表示的数字11,259,375是

2)  0xABCDEF

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

  • 字节顺序 *

1.字符'A',然后是'B'等按顺序写入存储器'A'为0x 41,'B'为0x 42...如果
1.这是一个多字节整数,其字节顺序取决于体系结构。假设该数是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)是0x 4443或“DC”(字符),将0x44(“D”)作为MSB,将0x43(“C”)作为LSB,表明您的体系结构是小端字节序。
在内存中写入一个字符串(按顺序),并以int的形式阅读其中的几个字符,得到一个取决于字节序的数字。

h79rfbju

h79rfbju3#

字节序在讨论s指向的字符常量数组中存储字节时并不起作用。如果检查*s处的内存,您会发现字节'a''b''c' ...,在小字节序系统中被解释为int时,它会被解释为"DCBA"
请记住,每个char已经是一个字节,如果你有char const * s = "0xfedcab09";,并且你在一个小端系统上做了一个printf("%d", *(int const *)s);,那么它将打印为0x9abcdef在十进制中的任何结果。

bkhjykvo

bkhjykvo4#

此处出现的混淆是由于 * 符号 *。

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

在***字符串***中,每个字母占用一个完整字节(char)。

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

然而,***number***ABCDEF的十六进制表示是不同的,每个数字(“0”..“9”和“A”...“F”)仅表示四位,或半个字节。因此,number0xABCDEF是字节序列

0xAB 0xCD 0xEF

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

  • 小字节序:最低有效字节优先

int x = { 0xEF, 0xCD, 0xAB };

  • Big Endian:最高有效字节优先

int x = { 0xAB, 0xCD, 0xEF }

  • 混合字节序:〈其他随机排序〉

int x = { 0xEF, 0x00, 0xCD, 0xAB }

相关问题