我正在为IP地址空间排序前缀的“孩子”。例如,8.8.8.0 / 24是IP地址空间中8.8.8.0/23的子节点。我很困惑为什么以下两个操作在我的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。
我在这里缺少什么?不应该对big endian的工作方式一样吗?
编辑:不重复,因为我确实理解了字节顺序如何影响数字存储方式的差异,但我显然遗漏了这些按位运算符。问题更多的是与bitwise相关而不是endianness - endianness就是为了培养一个例子
答案 0 :(得分:1)
x86是小端。小端的二进制数字是
data Edge v = Edge {source :: v, target :: v}
deriving (Show,Eq,Ord)
data Graph v = Graph {nodes :: Set v, edges :: Set (Edge v)}
deriving Show
instance Arbitrary v => Arbitrary (Edge v) where
arbitrary = do s <- arbitrary
t <- arbitrary
return $ Edge {source = s, target = t}
instance (Ord v, Arbitrary v) => Arbitrary (Graph v) where
arbitrary = aux `suchThat` validGraph
where aux = do lNodes <- arbitrary
lEdges <- arbitrary
return $ Graph {nodes = fromList lNodes, edges = fromList lEdges}
如果您将此位向左移9位,则变为......
|10000000|00000000|00000000|00000000
大多数人在考虑二元时认为数字1表示如下,有些人|00000000|01000000|00000000|00000000
这是大端,但它不是......
think
|00000000|00000000|00000000|00000001
的大端表示是
1
试试此代码
|00000000|00000000|00000000|10000000
这是输出
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
void print_bin(uint64_t num, size_t bytes) {
int i = 0;
for(i = bytes * 8; i > 0; i--) {
(i % 8 == 0) ? printf("|") : 1;
(num & 1) ? printf("1") : printf("0");
num >>= 1;
}
printf("\n");
}
int main(void) {
in_addr_t left = inet_addr("8.8.8.0");
in_addr_t right = inet_addr("8.8.9.0");
in_addr_t left_h = htonl(left);
in_addr_t right_h = htonl(right);
in_addr_t left_s = left << 9;
in_addr_t right_s = right >> 9;
assert(left != right);
printf("left != right\n");
print_bin(left, 4);
print_bin(right, 4);
printf("Big Endian if on x86\n");
print_bin(left_s, 4);
print_bin(right_s, 4);
printf("Little Endian if on x86\n");
print_bin(left_h, 4);
print_bin(right_h, 4);
return 0;
}
答案 1 :(得分:1)
Big Endian和Little Endian的问题并不为机器所知。
C中的类型不包含此类信息,因为它是硬件问题,而不是类型相关的信息。
机器假设所有多字节数字都按照它的本地字节顺序排列(在x86上,这通常是小端)。
出于这个原因,总是使用局部字节序假设执行位移。
您无法在Little Endian计算机上正确应用位移到Big Endian数字。
你甚至无法在Little Endian机器上将Big Endian号码打印到屏幕上而不会得到有趣的结果。
这就是为什么@Harry的答案非常酷,它会打印出每一个字,避开问题。
维基百科有一个article about Endianness,其中包含更多详细信息。
应该注意,Endianness实际上是指机器将其字节存储在内存中的方式。
例如,如果数字是字符串,则Endianness会引用一个问题:首先出现哪个“字母”(字节)?
某些机器会存储“Hello”,有些会存储“olleH”(仅限数字,在实际字符串中,字节总是正确排序)。
请注意,虽然字节的顺序是相反的,但每个字节的所有位都以相同的方式排序,因此每个字节都保留了它的值。
当发生位移时,它总是根据机器的字节排序系统发生,因为这是它的CPU和存储器存储的设计方式。