我正在对IP地址空间的前缀“子项”进行排序。例如,8.8.8.0/24是8.8.8.0/23IP地址空间中www.example.com的子项。我不明白为什么以下两个操作在我的x86小端系统上提供不同的结果
一点背景信息:A /24表示32位IPv4地址的前24位已“定义”。这意味着8.8.8.0/24包含8.8.8.0 - 8.8.8.255。同样,对于未定义的每一位,地址空间量将加倍。8.8.8.0/23将仅定义前23位,因此,实际地址空间从8.8.8.0到8.8.9.255,或者是/24大小的两倍。
现在我的困惑在于以下的位移位
inet_addr("8.8.8.0") << (32 - 23) produces 269488128
inet_addr("8.8.9.0") << (32 - 23) produces 303042560
inet_addr生成一个大字节序数字。但是,当将其转换为小字节序时-
htonl(inet_addr("8.8.8.0")) >> 9 produces 263172
htonl(inet_addr("8.8.9.0")) >> 9 produces 263172
这是预期的结果。丢弃最后9位将意味着8.8.9.0在8.8.8.0理论上等于www.example.com。
我错过了什么?big endian不也应该一样吗?
编辑:不重复,因为我确实理解字节序对数字存储方式的影响,但我显然忽略了这些位操作符的某些方面。问题更多地与位操作符有关,而不是字节序-字节序只是为了提供一个示例
3条答案
按热度按时间kx5bkwkv1#
x86是小端字节序。小端字节序的二进制数字1是
如果你把它左移9位,它变成...
在小端机器中,0xDEADBEEF作为一系列从低到高地址的字节输出,实际上会输出
EFBEADDE
,请参见https://www.codeproject.com/Articles/4804/Basic-concepts-on-Endianness
以及
https://www.gnu-pascal.de/gpc/Endianness.html.
大多数人在思考二进制时认为数字1表示如下(包括我)和一些人
think
这是大端,但它不是...在下面的代码中,我用littleendian输出了0xDEADBEEF,因为我的机器是x86,我使用了htonl函数将其转换为网络字节顺序,注意网络字节顺序定义为Big Endian。
当我打印出1的大端值,即
htonl(1)
时,1
的大端表示为试试这个代码
这是输出
kxkpmulp2#
大字节序和小字节序的问题机器并不真正知道。
C中的类型不包含这样的信息,因为这是硬件问题,而不是类型相关的问题。
机器假设所有的多字节数都是按照本地字节序排序的(在x86上,通常是小字节序)。
因此,总是使用本地字节序假设来执行位移位。
您无法在小端计算机上正确地将位移位应用于大端数字。
你甚至不能在小端机器上打印一个大端数字到屏幕上而不得到一个有趣的结果。
这就是为什么“哈里的回答如此酷,它打印出每一个比特,绕过了问题。”
维基百科有一个article about Endianness,里面有更多的细节。
应该注意的是,字节序实际上是指机器在内存中存储字节的方式。
例如,如果数字是字符串,字节序将引用以下问题:哪个“字母”(字节)会先出现?
有些机器存储“Hello”,有些存储“olleH”(仅对于数字,在实际字符串中,字节总是正确排序的)。
请注意,虽然字节的顺序颠倒了,但每个字节的所有位都以相同的方式排序,因此每个字节都保留了它的值。
当发生位移位时,它总是根据机器的字节排序系统发生,因为这是它的CPU和内存存储的设计方式。
qv7cva1a3#
公认的答案提供了一个很好的示例程序。然而,我认为这个示例有点误导。
1
的小端字节序位串打印为:我在我的x86 pc上运行了这段代码,我认为结果是可靠的,但这并不意味着
1
的值就像上面打印的那样存储在little-endian机器中。根据
print_bin
的代码,num每次右移一位,并打印最低有效位。此外,right shift
运算符总是从most significant bit (MSB)
移到least significant bit (LSB)
。最后,无论位顺序如何,
print_bin(1, 4)
的结果总是与1
的人工写入位表示相反,即:例如,位串可以是:
在这个例子中,位顺序与字节顺序不同,但是
print_bin(1,4)
的结果是相同的。**换句话说,在little-endian机器中,打印的位串并不一定意味着反转位顺序。**我在this blog中进一步讨论了这一点。