我试过四处寻找,但却找不到太多关于二进制文字和字节序的内容。二进制文字是little-endian,big-endian还是其他什么东西(比如匹配目标平台)?
例如,0b0111
的十进制值是多少?是7吗?平台具体?别的什么? 编辑:我选择了一个7的错误值,因为它在一个字节内表示。尽管如此,这个问题仍得到了充分的回答。
一些背景:基本上我试图找出最低有效位的值是什么,并用二进制文字掩盖它似乎是一个很好的方法...但只有在有一些保证的情况下端标记。
答案 0 :(得分:71)
简短回答:没有一个。用你在纸上书写的方式写出数字。
答案很长:
除非您真的试图将其结束(例如使用指针技巧),否则Endianness永远不会直接暴露在代码中。 0b0111
是7,它与十六进制相同,写作
int i = 0xAA77;
在某些平台上,并不意味着0x77AA
,因为那是荒谬的。 32位整数中缺少的额外0会在哪里?他们会在前面填充,然后整个事情翻到0x77AA0000
,还是会在之后添加?如果是这种情况,我不知道会有人期待什么。
关键是C ++没有对机器的字节序做出任何假设*,如果使用基元和它提供的文字编写代码,机器之间的行为将是相同的(除非你开始绕过类型系统,您可能需要这样做。)
要解决您的更新问题:数字将是您写出来的方式。这些位不会重新排序或任何此类事项,最重要的位在左侧,最低位在右侧。
这里似乎存在关于什么是字节序的误解。字节顺序指的是字节在内存中的排序方式以及它们必须如何解释。如果我给你的号码为“4172”并说“如果这是四千七百二十二,那么会有什么样的结尾”你无法给出答案,因为这个问题没有意义。 (一些人认为左边最大的数字意味着大端,但没有内存地址,字节顺序的问题是不负责任或相关的)。这只是一个数字,没有要解释的字节,没有内存地址。假设4字节整数表示,与其对应的字节为:
low address ----> high address
Big endian: 00 00 10 4c
Little endian: 4c 10 00 00
所以,给出其中任何一个并告诉“这是计算机4172的内部表示”,你可以确定它的小端还是大端。
所以现在考虑你的二进制文字0b0111
这4位代表一个nybble,可以存储为
low ---> high
Big endian: 00 00 00 07
Little endian: 07 00 00 00
但是你不必关心,因为这也是由硬件处理的,语言规定编译器从左到右读取,最重要的位到最低位
字节顺序与个别位无关。假设一个字节是8位,如果我递给你0b00000111
并说“是这个小端还是大端?”再次你不能说因为你只有一个字节(没有地址)。字节顺序与字节中的位顺序无关,它指的是整个字节相对于地址的顺序(当然,除非你有一位字节)。
您无需关心计算机内部使用的内容。 0b0111
只需节省您编写
unsigned int mask = 7 // only keep the lowest 3 bits
写作
unsigned int mask = 0b0111;
无需评论解释数字的重要性。
*在c ++ 20中,您可以使用std::endian检查字节顺序。
答案 1 :(得分:40)
所有整数文字(包括二进制文字)的解释方式与我们通常读取的数字相同(最左边的数字最重要)。
C ++标准保证对文字的相同解释,而不必关心您所处的特定环境。因此,您不必在此上下文中关注字节序。< / p>
0b0111
的例子总是等于七。
C ++标准在数字文字方面不使用字节顺序。相反,它只是简单地描述了文字具有一致的解释,并且解释是你期望的解释。
C ++标准版 - 整数字面意思 - 2.14.2 - 第1段
整数文字是一个没有句号或数字的数字序列 指数部分,带有可选的分隔单引号,被忽略 在确定其价值时。整数文字可以具有前缀 指定其基数和指定其类型的后缀。 词汇 数字序列的第一个数字是最重要的。一个 二进制整数文字(基数为2)以0b或0B开头,由...组成 一个二进制数字序列。八进制整数文字(基数为八) 从数字0开始,由一系列八进制数字组成。 十进制整数文字(十进制)以0以外的数字开头 并由一系列十进制数字组成。十六进制整数 literal(base 16)以0x或0X开头,由一个序列组成 十六进制数字,包括十进制数字和 字母a到f和A到F,十进制值十到十 十五。 [例子:12号可以写成12,014,0XC或 0b1100。文字1048576,1'048'576,0X100000,0x10'0000,和 0'004'000'000都具有相同的值。 - 结束例子]
术语endian和endianness是指用于的惯例 当存储这些字节时,解释构成数据字的字节 在计算机内存中。
Big-endian系统存储单词中最重要的字节 最小地址和最低有效字节存储在 最大地址(也见最重要的位)。小端 相反,系统将最低有效字节存储在最小的字节中 地址。
关于字节序的一个例子是考虑十进制数是多少 书面和读取到位值表示法。假设一个书写系统 数字从左到右书写,最左边的位置是 类似于所使用的最小内存地址,最右边 位置最大。例如,第一百二十三 是写的1 2 3,最左边的数百个地方。 任何阅读的人 这个数字也知道最左边的数字是最大的位置 值。这是日常遵循的大端会议的一个例子 寿命。强>
在这种情况下,我们将整数文字的数字视为“单词的字节”,并将该字视为文字本身。此外,文字中最左侧的字符被认为具有最小的地址。
对于文字1234
,数字一,二,三和四是“单词的字节”,1234
是“单词”。使用二进制文字0b0111
,数字为零,一,一和一是“单词的字节”,单词为0111
。
这种考虑使我们能够理解C ++语言环境中的字节顺序,并表明整数文字类似于“big-endian”。
答案 2 :(得分:10)
您错过了源代码中编写的字节序和目标代码中表示的字节序之间的区别。每个答案都不足为奇:源代码文字是bigendian,因为这是人类阅读它们的方式,在目标代码中它们被编写但是目标读取它们。
由于字节是定义内存访问的最小单位,我不相信甚至可以将字节顺序归结为字节中任何内部位的表示 - 这是发现更大数字的字节序的唯一方法(无论是故意还是意外)是通过分段从存储中访问它们,并且字节根据定义是最小的可访问存储单元。
答案 3 :(得分:7)
C / C ++语言不关心多字节整数的字节顺序。 C / C ++编译器可以。编译器会解析您的源代码并为特定目标平台生成机器代码。通常,编译器以与存储整数相同的方式存储整数文字;这样目标CPU的指令将直接支持在内存中读取和写入它们。
编译器会处理目标平台之间的差异,因此您不必这样做。
你唯一需要担心字节顺序的时候是与其他具有不同字节顺序的系统共享二进制值。然后你将逐字节读取二进制数据,并将内存中的字节排列在正确的位置。命令运行代码的系统。
答案 4 :(得分:3)
答案 5 :(得分:0)
Endianness是实现定义的。该标准保证每个对象都有一个对象表示形式为char
和unsigned char
的数组,您可以通过调用memcpy()
或memcmp()
来处理它。在C ++ 17中,reinterpret_cast
指向或指向任何对象类型(不是指向void
的指针,指向函数的指针,或nullptr
)到指针的指针是合法的。 char
,unsigned char
或std::byte
,它们是任何对象类型的有效别名。
当人们谈论“字节序”时,人们的意思是该对象表示中的字节顺序。例如,如果您声明unsigned char int_bytes[sizeof(int)] = {1};
和int i;
,那么memcpy( &i, int_bytes, sizeof(i));
是否会获得0x01,0x01000000,0x0100,0x0100000000000000或其他内容?答案是:是的。有实际的实现可以产生这些结果,并且它们都符合标准。原因是编译器可以使用CPU的本机格式。
当程序需要通过Internet发送或接收数据时,这通常会出现,其中所有标准都定义数据应该以big-endian顺序传输,在像x86这样的小端CPU上。因此,一些网络库指定特定参数和结构字段是否应以主机或网络字节顺序存储。
这种语言让你可以通过随意地对象对象的位来对自己进行拍摄,但它可能会为你提供一个陷阱表示,如果你以后尝试使用它会导致未定义的行为。 (这可能意味着,例如,重写一个虚函数表以注入任意代码。)<type_traits>
标头有几个模板来测试使用对象表示做事是否安全。如果类型为memcpy( &dest, &src, sizeof(dest) )
,则可以使用is_trivially_copyable
将一个对象复制到同一类型的另一个对象上。如果is_trivially_move_constructible
,您可以复制到正确对齐的未初始化内存。您可以测试两个相同类型的对象是否与memcmp( &a, &b, sizeof(a) )
相同,并通过对类型为has_unique_object_representations
的对象表示中的字节应用散列函数来正确地散列对象。整数类型没有陷阱表示,依此类推。但是,在大多数情况下,如果您正在对字节序很重要的对象表示进行操作,那么您告诉编译器假设您知道自己在做什么,而且您的代码将无法移植。
正如其他人所提到的,二进制文字首先用最高位数写成,如十进制,八进制或十六进制文字。这与字节顺序不同,不会影响您是否需要从Internet读取的TCP标头中调用端口号上的ntohs()
。
答案 6 :(得分:-6)
您可能希望将C或C ++或任何其他语言视为本质上的小端(考虑按位运算符的工作原理)。如果底层硬件是大端,编译器确保数据存储在大端(对于其他字节序也是如此),但是你的位操作就好像数据是小端的一样。要记住的是,就语言而言,数据是小端的。当您将数据从一种类型转换为另一种类型时,会出现与字节顺序相关的问题。只要你不这样做就很好。
我被问及“C / C ++语言本质上是小端”的说法,因此我提供了一个例子,许多人都知道它是如何工作的,但在这里我就去了。
typedef union
{
struct {
int a:1;
int reserved:31;
} bits;
unsigned int value;
} u;
u test;
test.bits.a = 1;
test.bits.reserved = 0;
printf("After bits assignment, test.value = 0x%08X\n", test.value);
test.value = 0x00000001;
printf("After value assignment, test.value = 0x%08X\n", test.value);
小端系统的输出:
After bits assignment, test.value = 0x00000001
After value assignment, test.value = 0x00000001
大端系统的输出:
After bits assignment, test.value = 0x80000000
After value assignment, test.value = 0x00000001
所以,如果你不知道处理器的字节顺序,那么一切都在哪里出来?在小端系统!因此,我说C / C ++语言本质上是一个小端。