C 大整数转二进制

C Convert Large Integer To Binary

我将 IP 地址以例如 21211328 的形式输入到一个框中。

我想把这个整数转换成二进制形式。

我现在遇到的问题是我的函数根据示例输出 -62674752。

这显然是错误的,因为它不能为负数而且这不是二进制形式。

我使用的函数如下所示:

int toBinary(int decimalNo){
    if (decimalNo == 0) return 0;
    if (decimalNo == 1) return 1;                       /* optional */
    return (decimalNo % 2) + 10 * toBinary(decimalNo / 2);
}

我是这样使用的:

int num_converted = toBinary(iph->saddr); // convert it to binary
printk(KERN_INFO "BSADDR: %d", num_converted); // print the binary conversion to kernel for debugging
if ((num_converted & mask_array) == masked_sub){
        return NF_DROP;
}

这会在我的内核日志中返回不正确的输出,如上所示。

iph->saddr returns 21211328, and int num_converted = toBinary(iph->saddr); returns-62674752.

试试这个:

#include <stdio.h>

char *getBin( unsigned long val) {
    static char buf[33];
    int i;

    buf[32]='[=10=]';
    for(i=31; i>=0; i--){
        buf[i] = val & 1?'1':'0';
        val/=2;
    }
    return buf;
}

int main()
{
    unsigned long ip=0x80e2d301;    
    printf( " ip:%08lx, bin:%sB\n", (unsigned long)ip, getBin(ip) );
    return 0;
}

首先,IP 地址通常表示(供人类使用)为单个字节的四个字节(网络字节顺序中由点分隔的四个数字),而不是二进制序列。

你的地址 post (21211328) 表示地址 1.67.168.192 (我猜这是一个错误的表示---你需要考虑所有的ip地址都是网络字节顺序,大多数地址的重要字节在前 --- 192.168.67.1)

要成功解码一个 IP 地址,您首先要考虑您的机器是否会以正确的字节序处理它,或者您必须切换字节才能将其解释为数字。地址 192.168.67.1 的正确整数(十进制)将是 3,232,252,673。此数字不能表示为 signed int,因为它具有最高有效位,因此将表示为负数。

要解码 IP 地址,您首先必须使用 ntohl(3) 函数将从套接字接口获得的数字转换为主机字节顺序(字节顺序)。获得主机字节顺序的地址后,您必须将数字转换为 256 进制。这很容易,您可以使用以下代码片段完成:

unsigned long ip_address = ntohl(server_address.sin_addr.s_addr);
int i;

for (i = 24; i >= 0; i -= 8) {
    if (i < 24) printf("."); /* print the dot between the numbers */
    printf("%d",
        (ip_address >> i) & 0xff);
}

我将在下面解释:

表达式(ip_address >> i) & 0xff表示将位24到31右移i位(或24位,16位,8位地方和 0 地方)到右边。所以首先我们将 8 个最重要的位放在 0 到 7 的位置 (24..31 => 0..7),接下来我们将对它们旁边的位 (16..23 => 0..7) 执行此操作,依此类推直到最不重要的位位,根本没有移位 (0..7 => 0..7)。一旦我们得到了我们对位置 0..7 感兴趣的位,我们就用 0xff 屏蔽它们(这是一个在位位置 0..7 和 [=26= 中具有 1s 的值]s 其他地方)所以和位运算符 & 将仅保留我们已移动到固定位置 0..7 的位,并将屏蔽掉所有其他位。所以我们最终得到了一个介于 0 和 255 之间的数字,这就是我们正在打印的。

如果你确实想以二进制形式表示它们,你可以通过稍微修改上面的代码来做到这一点,只要考虑到不是用八位数字作为数字,而是用一位数字作为数字:

unsigned long ip_address = ntohl(server_address.sin_addr.s_addr);

for (i = 31; i >= 0; i--) {
    /* In this case, we don't print the dot between the numbers */
    printf("%d",
        (ip_address >> i) & 0x1);
}

这次的掩码是0x01,它只掩码了数字的最低有效位。

最后,我写了一个完整的程序来说明两种解码方式,另外还展示了ntohl(3)函数的使用:

ipaddr.c

/* 00001 */ #include <arpa/inet.h>
/* 00002 */ #include <stdio.h>

/* 00003 */ #ifndef DOTTED_DECIMAL
/* 00004 */ #define DOTTED_DECIMAL  1
/* 00005 */ #endif

/* 00006 */ #if DOTTED_DECIMAL
/* 00007 */ #   define NBITS    8
/* 00008 */ #   define SEP      "."
/* 00009 */ #else /* BINARY */
/* 00010 */ #   define NBITS 1
/* 00011 */ #   define SEP      ""
/* 00012 */ #endif

/* 00013 */ #define MASK    ((1 << NBITS) - 1)   /* 100..00 - 1 = 011..11, with NBITS `1` bits */

/* 00014 */ char *ip_formatted(long ip, char *sep, char *buff, size_t buffsz);

/* 00015 */ int main()
/* 00016 */ {
/* 00017 */     char line[1024];
/* 00018 */     unsigned long ip_netfmt = 21211328; /* this was the ip you posted (in network byte order) */
/* 00019 */     unsigned long ip_hostfmt = ntohl(ip_netfmt); /* this is the ip in host byte order */
/* 00020 */     printf("%lu => [%s]\n", ip_netfmt, ip_formatted(ip_netfmt, SEP, line, sizeof line));
/* 00021 */     printf("%lu => [%s]\n", ip_hostfmt, ip_formatted(ip_hostfmt, SEP, line, sizeof line));
/* 00022 */ }

/* 00023 */ char *ip_formatted(long ip, char *sep, char *buff, size_t buffsz)
/* 00024 */ {
/* 00025 */     size_t n;
/* 00026 */     char *s = buff;
/* 00027 */     int i;
/* 00028 */     for (i = 32 - NBITS; i >= 0; i -= NBITS) {
/* 00029 */         int digit = (ip >> i) & MASK;
/* 00030 */         n = snprintf(s, buffsz,
/* 00031 */             "%s%d",
/* 00032 */             i == 32 - NBITS ? "" : sep,
/* 00033 */             digit);
/* 00034 */         s += n; buffsz -= n;
/* 00035 */     }
/* 00036 */     return buff;
/* 00037 */ }

使用以下命令编译此代码:

$ cc -o ipaddr -DDOTTED_DECIMAL=1 ipaddr.c

查看点分十进制的输出,

$ cc -o ipaddr -DDOTTED_DECIMAL=0 ipaddr.c

查看二进制数字的输出。

(我包括了行号,所以可以使用代码的引用,并注释,所以你可以通过剪切和粘贴直接编译代码)