【问题标题】:Integer to IP Address - C整数到 IP 地址 - C
【发布时间】:2025-12-31 13:55:17
【问题描述】:

我正在准备一个测验,我强烈怀疑我可能负责实现这样的功能。基本上,给定一个网络表示法的 IP 地址,我们如何将它从 32 位整数转换为点分十进制表示法的字符串(类似于 155.247.182.83)......?显然我们也不能使用任何类型的 inet 函数......我很难过!

【问题讨论】:

  • 如果你正在编写代码——你应该使用 inet 函数——如果我进行了测试,我会让你因为重新发明工作测试代码而失败:)
  • @Mark 如果你在 Windows 上,你不能依赖 inet 函数......他们设法将它们绑定到 winsock 库中,以便 inet_ntoa,这应该只是做基本的小游戏,可能会失败。

标签: c string ip-address


【解决方案1】:

这是一个简单的方法:(ip >> 8)(ip >> 16)(ip >> 24) 将第 2、3 和第 4 个字节移动到低位字节,而 & 0xFF 在每个步骤中隔离最低有效字节.

void print_ip(unsigned int ip)
{
    unsigned char bytes[4];
    bytes[0] = ip & 0xFF;
    bytes[1] = (ip >> 8) & 0xFF;
    bytes[2] = (ip >> 16) & 0xFF;
    bytes[3] = (ip >> 24) & 0xFF;   
    printf("%d.%d.%d.%d\n", bytes[3], bytes[2], bytes[1], bytes[0]);        
}

第一步有一个隐含的bytes[0] = (ip >> 0) & 0xFF;

使用snprintf() 将其打印为字符串。

【讨论】:

  • 那么等等,让我问问你的想法……那我该怎么走相反的路呢?如果我有字符串,我怎么能取回 int?
  • 要从字符串返回到 int,您需要解析字符串。即没有简单的位黑客方式。
  • 这里有个大问题,你为什么要向后打印?
  • @ytpillai bytes[3] 包含最高有效字节(例如 127.0.0.1 中的 127),应首先打印。同样bytes[0] 包含最低有效字节
  • 我试过这个方法。我想,我们需要把它写下来。我通过调制解调器检查了设备的 IP 地址。如果我像你一样打印,看起来像反向。这是ip:192.168.43.182如果我使用你的方法,看起来像:182.43.168.192
【解决方案2】:
void ul2chardec(char*pcIP, unsigned long ulIPN){
int i; int k=0; char c0, c1;
for (i = 0; i<4; i++){
    c0 = ((((ulIPN & (0xff << ((3 - i) * 8))) >> ((3 - i) * 8))) / 100) + 0x30;
    if (c0 != '0'){ *(pcIP + k) = c0; k++; }
    c1 = (((((ulIPN & (0xff << ((3 - i) * 8))) >> ((3 - i) * 8))) % 100) / 10) + 0x30;
    if (!(c1 =='0' && c0=='0')){ *(pcIP + k) = c1; k++; }
    *(pcIP +k) = (((((ulIPN & (0xff << ((3 - i) * 8)))) >> ((3 - i) * 8))) % 10) + 0x30;
    k++;
    if (i<3){ *(pcIP + k) = '.'; k++;}
}
*(pcIP + k) = 0; // pcIP should be x10 bytes

}

【讨论】:

    【解决方案3】:

    您实际上可以使用 inet 函数。观察。

    main.c:

    #include <arpa/inet.h>
    
    main() {
        uint32_t ip = 2110443574;
        struct in_addr ip_addr;
        ip_addr.s_addr = ip;
        printf("The IP address is %s\n", inet_ntoa(ip_addr));
    }
    

    gcc main.c -ansi; ./a.out的结果是

    IP 地址是 54.208.202.125

    请注意,评论者说这不适用于 Windows。

    【讨论】:

    • 这显然是最好的答案。为什么没有绿勾?无需重新发明*。
    • Wont work on Winodows. Since author didnt 指定平台我个人不喜欢这个答案。
    • 如果您的应用程序是多线程的,请确保您的 inet_ntoa() 是线程感知的。至少,GNU Libc 将返回的静态字符串缓冲区线程声明为本地的。 sourceware.org/git/?p=glibc.git;f=inet/…
    • 我会提到inet_ntoa() 使用little-endian 顺序。这就是为什么它可以返回结果,这是您所不期望的。在上面的例子中2110443574 等于125.202.208.54,但是函数颠倒了字节顺序,结果我们得到54.208.202.125。正如另一个答案中提到的,这个函数的重点不是产生一个“可读”的整数,而是设置一个准备好通过网络发送的 32 位数量。
    【解决方案4】:

    我的减法替代解决方案:)

    void convert( unsigned int addr )
    {    
    unsigned int num[OCTET], 
                next_addr[OCTET];
    
    int bits = 8;
    unsigned int shift_bits;
    int i;
    
    next_addr[0] = addr; 
    shift_bits -= bits;  
    num[0] = next_addr[0] >> shift_bits;
    
    for ( i = 0; i < OCTET-1; i ++ )
    {       
        next_addr[i + 1] = next_addr[i] - ( num[i] << shift_bits ); // next subaddr
        shift_bits -= bits; // next shift
        num[i + 1] = next_addr[i + 1] >> shift_bits; // octet
    }
    
    printf( "%d.%d.%d.%d\n", num[0], num[1], num[2], num[3] );
    }
    

    【讨论】:

      【解决方案5】:

      从字符串到整数再返回

      const char * s_ip = "192.168.0.5";
      unsigned int ip;
      unsigned char * c_ip = (unsigned char *)&ip;
      sscanf(s_ip, "%hhu.%hhu.%hhu.%hhu", &c_ip[3], &c_ip[2], &c_ip[1], &c_ip[0]);
      printf("%u.%u.%u.%u", ((ip & 0xff000000) >> 24), ((ip & 0x00ff0000) >> 16), ((ip & 0x0000ff00) >> 8), (ip & 0x000000ff));
      

      %hhu 指示 sscanf 读入 unsigned char 指针; (Reading small int with scanf)


      inet_ntoa 来自 glibc

      char *
      inet_ntoa (struct in_addr in)
      {
      unsigned char *bytes = (unsigned char *) &in;
      __snprintf (buffer, sizeof (buffer), "%d.%d.%d.%d",
      bytes[0], bytes[1], bytes[2], bytes[3]);
      return buffer;
      }
      

      【讨论】:

        【解决方案6】:
        #include "stdio.h"
        
        void print_ip(int ip) {
           unsigned char bytes[4];
           int i;
           for(i=0; i<4; i++) {
              bytes[i] = (ip >> i*8) & 0xFF;
           }
           printf("%d.%d.%d.%d\n", bytes[3], bytes[2], bytes[1], bytes[0]);
        }
        
        int main() {
           int ip = 0xDEADBEEF;
           print_ip(ip);   
        }
        

        【讨论】:

          【解决方案7】:

          提示:将 32 位整数分解为 4 个 8 位整数,并打印出来。

          类似的东西(未编译,YMMV):

          int i = 0xDEADBEEF; // some 32-bit integer
          printf("%i.%i.%i.%i",
                    (i >> 24) & 0xFF,
                    (i >> 16) & 0xFF,
                    (i >> 8) & 0xFF,
                    i & 0xFF);
          

          【讨论】:

          • 呃...这不能编译,是不是缺少函数调用?而且移位计数都是错误的,他们似乎认为两个十六进制数字等于四位。此外,最好先移位后掩码,如 iWerner。
          • 就像我说的,这是一个快速的答案。修复了位移。
          • 天哪!永远不要写像 0xDEADBEEF 这样的东西,除非你想自己调试几个小时等。见*.com/questions/1556672/…
          • 1) 这只是一个例子,2) 代码现在已经编译和测试了
          【解决方案8】:

          如果传递一个要填充的字符串缓冲区并且我知道缓冲区足够大(即至少 16 个字符长),我会这样做:

          sprintf(buffer, "%d.%d.%d.%d",
            (ip >> 24) & 0xFF,
            (ip >> 16) & 0xFF,
            (ip >>  8) & 0xFF,
            (ip      ) & 0xFF);
          

          这会比先创建一个字节数组稍微快一些,而且我认为它更具可读性。我通常会使用 snprintf,但 IP 地址的长度不能超过 16 个字符,包括终止的 null。

          或者,如果我被要求提供一个返回 char* 的函数:

          char* IPAddressToString(int ip)
          {
            char[] result = new char[16];
          
            sprintf(result, "%d.%d.%d.%d",
              (ip >> 24) & 0xFF,
              (ip >> 16) & 0xFF,
              (ip >>  8) & 0xFF,
              (ip      ) & 0xFF);
          
            return result;
          }
          

          【讨论】:

            【解决方案9】:

            另一种方法:

            union IP {
                unsigned int ip;
                struct {
                  unsigned char d;
                  unsigned char c;
                  unsigned char b;
                  unsigned char a;
                } ip2;
            };
            
            ...
            char  ips[20];
            IP ip;
            ip.ip = 0xAABBCCDD;
            
            sprintf(ips, "%x.%x.%x.%x", ip.ip2.a, ip.ip2.b, ip.ip2.c, ip.ip2.d);
            printf("%s\n", ips);
            

            【讨论】:

            • 这不是便携式的;它取决于 sizeof(unsigned) 为 4(与其他一些答案一样)和 int 中的字节顺序。
            • @janm,当然它不是可移植的。如果需要可移植的代码,我们可以定义替代的 IP union 来处理更多的 cpu 架构,而无需修改 实际 代码。
            • 移位方法是可移植的,不需要修改实际代码。为什么要介绍一堆有更短、更清晰、更便携的特例呢?表现?在现代编译器上,如果您可以测量差异,我会感到惊讶(尽管我没有检查过)。你可能想要 %d 而不是 %x。