C语言将点分十进制的IP字符串转成4个整数

最近在做lldp的snmp返回值时需要做这样的转换处理:C语言将点分十进制的IP字符串转成4个整数。

这里用两种方式:

  1. sscanf格式化处理
  2. 用 inet_aton函数将ip字符串转成32位的整形,然后再根据bit转成对应的4个整数。
  • man命令可以确认下sscanf和inet_aton的返回值,以确认处理成功还是失败。
  • 字节序问题:inet_aton后统一用网络字节序,避免出错。

sscanf

c 复制代码
#include <stdio.h>

// 1 succeed, 0 failed
int parse_ip_sscanf(const char *ip_str, int *a, int *b, int *c, int *d) {
    return sscanf(ip_str, "%d.%d.%d.%d", a, b, c, d) == 4;
}

int main() {
    // 定义点分十进制的 IP 字符串
    const char *ip_str = "192.168.1.1";

    // 定义变量存储解析结果
    int a, b, c, d;

	if (0 == parse_ip_sscanf(ip_str, &a, &b, &c, &d)) {
		printf("parse_ip_sscanf failed\n");
	} else {
		printf("parse done: %d %d %d %d\n", a, b, c, d);
	}
	return 0;
}

inet_aton + ntol

c 复制代码
#include <stdio.h>
#include <arpa/inet.h>

#define u8 unsigned char

// 1 succeed, 0 failed
int parse_ip_inet_aton(const char *ip_str, int *a, int *b, int *c, int *d) {
	u8 *p = NULL;
    in_addr_t ip_int;
    if (inet_aton(ip_str, (struct in_addr *)&ip_int) == 0) {
        return 0;  // 解析失败
    }
    p = (u8 *)&ip_int;
    *a = p[0];
    *b = p[1];
    *c = p[2];
    *d = p[3];
    return 1;  // 解析成功
}

int main() {
    // 定义点分十进制的 IP 字符串
    const char *ip_str = "192.168.1.1";

    // 定义变量存储解析结果
    int a, b, c, d;

	if (0 == parse_ip_inet_aton(ip_str, &a, &b, &c, &d)) {
		printf("parse_ip_sscanf failed\n");
	} else {
		printf("parse done: %d %d %d %d\n", a, b, c, d);
	}
	return 0;
}

哪个效率高点?

这里用的是固定的字符串跑的测试,不太严谨。。。可以考虑随机生成1~255的数字组成ip字符串,然后再跑下测试。

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <time.h>

#define TEST_COUNT 1000000  // 测试次数

// 使用 sscanf 解析 IP 地址
int parse_ip_sscanf(const char *ip_str, int *a, int *b, int *c, int *d) {
    return sscanf(ip_str, "%d.%d.%d.%d", a, b, c, d) == 4;
}

// 使用 inet_aton 解析 IP 地址
int parse_ip_inet_aton(const char *ip_str, int *a, int *b, int *c, int *d) {
    in_addr_t ip_int;
    if (inet_aton(ip_str, (struct in_addr *)&ip_int) == 0) {
        return 0;  // 解析失败
    }
    ip_int = ntohl(ip_int);
    *a = (ip_int >> 24) & 0xFF;
    *b = (ip_int >> 16) & 0xFF;
    *c = (ip_int >> 8) & 0xFF;
    *d = ip_int & 0xFF;
    return 1;  // 解析成功
}

int main() {
    // 定义点分十进制的 IP 字符串
    const char *ip_str = "192.168.1.1";

    // 定义变量存储解析结果
    int a, b, c, d;

    // 记录失败的次数
    int sscanf_fail_count = 0;
    int inet_aton_fail_count = 0;

    // 测试 sscanf 方式
    clock_t start = clock();
    for (int i = 0; i < TEST_COUNT; i++) {
        if (0 == parse_ip_sscanf(ip_str, &a, &b, &c, &d)) {
            sscanf_fail_count++;
        }
    }
    clock_t end = clock();
    double sscanf_time = (double)(end - start) / CLOCKS_PER_SEC;
    printf("sscanf 方式耗时: %.6f 秒\n", sscanf_time);
    printf("sscanf 方式失败次数: %d\n", sscanf_fail_count);

    // 测试 inet_aton 方式
    start = clock();
    for (int i = 0; i < TEST_COUNT; i++) {
        if (0 == parse_ip_inet_aton(ip_str, &a, &b, &c, &d)) {
            inet_aton_fail_count++;
        }
    }
    end = clock();
    double inet_aton_time = (double)(end - start) / CLOCKS_PER_SEC;
    printf("inet_aton 方式耗时: %.6f 秒\n", inet_aton_time);
    printf("inet_aton 方式失败次数: %d\n", inet_aton_fail_count);

    // 比较两种方式的效率
    if (sscanf_time < inet_aton_time) {
        printf("sscanf 方式更快,效率高出 %.2f 倍\n", inet_aton_time / sscanf_time);
    } else {
        printf("inet_aton 方式更快,效率高出 %.2f 倍\n", sscanf_time / inet_aton_time);
    }

    return 0;
}

/*
sscanf 方式耗时: 0.104025 秒
sscanf 方式失败次数: 0
inet_aton 方式耗时: 0.027499 秒
inet_aton 方式失败次数: 0
inet_aton 方式更快,效率高出 3.78 倍
*/

修改ip随机生成一百万次,测试:

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <time.h>

#define TEST_COUNT 1000000  // 测试次数

// 生成一个随机的合法 IP 地址字符串
void generate_random_ip(char *ip_str) {
    sprintf(ip_str, "%d.%d.%d.%d",
            rand() % 256, rand() % 256, rand() % 256, rand() % 256);
}

// 使用 sscanf 解析 IP 地址
int parse_ip_sscanf(const char *ip_str, int *a, int *b, int *c, int *d) {
    return sscanf(ip_str, "%d.%d.%d.%d", a, b, c, d) == 4;
}

// 使用 inet_aton 解析 IP 地址
int parse_ip_inet_aton(const char *ip_str, int *a, int *b, int *c, int *d) {
    in_addr_t ip_int;
    if (inet_aton(ip_str, (struct in_addr *)&ip_int) == 0) {
        return 0;  // 解析失败
    }
    ip_int = ntohl(ip_int);
    *a = (ip_int >> 24) & 0xFF;
    *b = (ip_int >> 16) & 0xFF;
    *c = (ip_int >> 8) & 0xFF;
    *d = ip_int & 0xFF;
    return 1;  // 解析成功
}

int main() {
    // 初始化随机数种子
    srand(time(NULL));

    // 定义变量存储解析结果
    int a, b, c, d;

    // 记录失败的次数
    int sscanf_fail_count = 0;
    int inet_aton_fail_count = 0;

    // 动态分配堆空间存储 IP 地址数组
    char (*ip_array)[16] = malloc(TEST_COUNT * sizeof(*ip_array));
    if (ip_array == NULL) {
        printf("内存分配失败!\n");
        return 1;
    }

	// 随机生成 IP 地址数组
    for (int i = 0; i < TEST_COUNT; i++) {
        generate_random_ip(ip_array[i]);
    }

    // 测试 sscanf 方式
    clock_t start = clock();
    for (int i = 0; i < TEST_COUNT; i++) {
        if (!parse_ip_sscanf(ip_array[i], &a, &b, &c, &d)) {
            sscanf_fail_count++;
        }
    }
    clock_t end = clock();
    double sscanf_time = (double)(end - start) / CLOCKS_PER_SEC;
    printf("sscanf 方式耗时: %.6f 秒\n", sscanf_time);
    printf("sscanf 方式失败次数: %d\n", sscanf_fail_count);

    // 测试 inet_aton 方式
    start = clock();
    for (int i = 0; i < TEST_COUNT; i++) {
        if (!parse_ip_inet_aton(ip_array[i], &a, &b, &c, &d)) {
            inet_aton_fail_count++;
        }
    }
    end = clock();
    double inet_aton_time = (double)(end - start) / CLOCKS_PER_SEC;
    printf("inet_aton 方式耗时: %.6f 秒\n", inet_aton_time);
    printf("inet_aton 方式失败次数: %d\n", inet_aton_fail_count);

    // 比较两种方式的效率
    if (sscanf_time < inet_aton_time) {
        printf("sscanf 方式更快,效率高出 %.2f 倍\n", inet_aton_time / sscanf_time);
    } else {
        printf("inet_aton 方式更快,效率高出 %.2f 倍\n", sscanf_time / inet_aton_time);
    }

    return 0;
}

/*
sscanf 方式耗时: 0.116505 秒
sscanf 方式失败次数: 0
inet_aton 方式耗时: 0.043936 秒
inet_aton 方式失败次数: 0
inet_aton 方式更快,效率高出 2.65 倍
*/
相关推荐
Novproxy_Z9 小时前
商用代理避坑:如何识别商家拿机房 IP 冒充住宅 IP
java·网络·tcp/ip
垆边人似月.9 小时前
RDMA 文件传输踩坑(五):READY 发送成功后,为什么出现 Work Request Flushed Error?
服务器·网络·tcp/ip
qetfw10 小时前
UOS DHCP Relay:跨网段分配与 IP、DNS、网关验证
linux·服务器·网络·tcp/ip·uos
尹人入圣10 小时前
目前热门的IP驱动产业新场景新工具
网络·python·网络协议·tcp/ip
·present·11 小时前
CSAPP(Bomb lab)
c语言·c++
叼烟扛炮12 小时前
Linux 学习笔记(四十一):TCP 并发演进——从单进程到多线程服务器
linux·运维·服务器·网络·笔记·学习·tcp/ip
叼烟扛炮13 小时前
Linux 学习笔记(四十三):网络编程进阶——自定义协议解决 TCP 粘包与守护进程
linux·开发语言·网络·笔记·tcp/ip
wuminyu16 小时前
Java字节码学习总结:字节码如何加载、解析和执行
java·linux·c语言·jvm·c++
图灵-M19 小时前
【数据结构】静态 & 动态顺序表
c语言·数据结构·笔记
lazy H19 小时前
【计算机网络】第 15 篇:Java BIO、NIO 与高并发网络通信——从 Socket 理解 I/O 模型
网络·后端·tcp/ip·计算机网络·nio·408