2016-04-23 153 views
0

我正在排序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的两倍。

我现在有点混乱与以下bitshifts

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。

我在这里错过了什么?它不应该在大型排序中工作吗?

编辑:不是重复的,因为我的确了解endianness如何影响数字存储方式的区别,但我明显错过了这些按位运算符。问题是更多的是按位而不是endianness - endianness只是在那里培养一个例子

+0

可能的重复[简单的按位操作的小尾数整数,在大端机?](http://stackoverflow.com/questions/5642981/simple-bitwise-manipulation-for-little-endian-integer- in-big-endian-machine) –

+0

这4个输出中的哪一个出乎意料?你期望什么价值? –

+0

前两个产出是意料之外的。我期望前两个输出相等,就像第三个和第四个输出相等 – dreadiscool

回答

2

x86是little endian。在小尾数二进制数字1是

|10000000|00000000|00000000|00000000 

如果移位这个东西变成了9位向左......

|00000000|01000000|00000000|00000000 

大多数人在二进制想,如果认为这个数字1表示为下面还有一些人think这是Big Endian,但它不是...

|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; 
} 

这是输出

left != right 
|00010000|00010000|00010000|00000000 
|00010000|00010000|10010000|00000000 
Big Endian if on x86 
|00000000|00001000|00001000|00001000 
|00100001|00100000|00000000|00000000 
Little Endian if on x86 
|00000000|00010000|00010000|00010000 
|00000000|10010000|00010000|00010000 
+0

如果downvoters会解释原因,那真的很有帮助。我认为代码显示了发生了什么。 – Harry

+2

Harry进一步深入了解为什么会出现这种情况。 –

+1

我认为这很棒,并赞成它 – dreadiscool

1

大endian和little endian的问题是不是真的知道机器。

C中的类型不包含这样的信息,因为它是硬件问题,而不是类型相关的。该机器假设所有多字节数字都是按照本地端序排列的(在x86上,这通常是小端)。

由于这个原因,移位总是使用局部端点假设来执行。

您不能正确应用位移到Little Endian机器上的Big Endian编号。

你甚至不能在Little Endian机器上的屏幕上打印Big Endian数字而不会得到一个有趣的结果。

这就是为什么@哈利的答案非常酷,它打印出每一点,绕过这个问题。

维基百科有一个article about Endianness与更多的细节。

应该指出的是,Endianness实际上是指机器将其字节存储在内存中的方式。

例如,如果数字是字符串,则Endianness会引用问题:哪个“字母”(字节)会先出现?

有些机器会存储“Hello”,有些会存储“olleH”(仅用于数字,在实际字符串中,字节总是正确排序)。

请注意,尽管字节顺序相反,但每个字节的所有位都按相同方式排序,因此每个字节都保留其值。

当发生位移时,它总是按照机器的字节排序系统发生,因为这是CPU和存储器存储的设计方式。