You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言中使用strtok处理IP范围时跳过第二个令牌的问题排查

C语言中使用strtok处理IP范围时跳过第二个令牌的问题排查

嗨,我来帮你揪出问题所在!

核心原因:strtok的静态状态被嵌套调用覆盖了

strtok函数是非线程安全的,它内部用一个静态变量来记录当前解析的位置。当你在splitOnDash里用strtok分割'-',然后调用splitOnDot时,splitOnDot里的strtok会重置并覆盖这个静态状态——相当于把之前处理IP范围的进度给“擦除”了。

具体流程是这样的:

  1. splitOnDash中第一次调用strtok(ipStr, "-"),拿到第一个IP段,此时strtok的静态状态指向ipStr中'-'的位置。
  2. 你复制这个IP段到fullIpCopy,然后调用splitOnDot(fullIpCopy)。
  3. splitOnDot里的strtok(fullIpCopy, ".")会把strtok的内部状态改成处理fullIpCopy的位置,原来处理ipStr的进度完全丢失。
  4. 回到splitOnDash再调用strtok(NULL, "-")时,strtok会从splitOnDot最后处理的位置(也就是fullIpCopy的末尾)找'-',自然找不到,返回NULL,导致第二个IP段根本没被处理。

额外的小bug

还有个容易忽略的错误:splitOnDot里的if(endptr == ipStr)应该写成if(endptr == ipTokens),因为endptr是相对于当前令牌ipTokens的指针,不是整个ipStr的,这个错误会导致一些合法IP被误判为无效。

另外,你定义的char fullIpCopy [] = "tempString";数组大小只有11,而IPv4最长是15个字符(比如255.255.255.255),复制时会出现缓冲区溢出,得改成足够大的数组,比如char fullIpCopy[16];。

解决方案:用可重入的strtok_r(推荐)

POSIX标准提供了strtok_r,它需要你手动传入一个指针来保存解析状态,不同的解析上下文不会互相干扰。

修改后的splitOnDot:

// 接收不含'-'的字符串,按'.'分割、转整数并验证
int splitOnDot(char* ipStr){
    
    char *saveptr; // 保存strtok_r的解析状态
    char *ipTokens = strtok_r(ipStr, ".", &saveptr);

    int xxxCount = 0;

    while(ipTokens){
    
        char *endptr;
        long int xxxIp;

        xxxIp = strtol(ipTokens, &endptr, 10);

        if(endptr == ipTokens){ // 修正判断对象
            return 0; 
        }
        else if (*endptr != '\0'){
            return 0; 
        }
        else{
            if(xxxIp >= 0 && xxxIp <= 255){
                xxxCount += 1;
            } 
            else{
                return 0;
            }
        } 

        ipTokens = strtok_r(NULL, ".", &saveptr);
    }

    return (xxxCount == 4) ? 1 : 0;
}

修改后的splitOnDash:

// 接收'x.x.x.x-x.x.x.x'格式的字符串,分割并验证两个IP
int splitOnDash(char* ipStr){

    char *dashSaveptr;
    char *fullIp = strtok_r(ipStr, "-", &dashSaveptr);
    char fullIpCopy[16]; // 足够容纳最长IPv4字符串

    while(fullIp){
    
        strncpy(fullIpCopy, fullIp, sizeof(fullIpCopy)-1);
        fullIpCopy[sizeof(fullIpCopy)-1] = '\0'; // 确保字符串结束符
        int validIp = splitOnDot(fullIpCopy);

        if(validIp == 0){
            return 0;
        } 

        fullIp = strtok_r(NULL, "-", &dashSaveptr);
    } 
    
    return 1; 
}

备选方案:手动解析字符串(无需strtok)

如果你的环境不支持strtok_r,可以用strchr手动查找分隔符,避免依赖strtok的静态状态:

// 手动分割IP段验证
int splitOnDot(char* ipStr){
    char *ptr = ipStr;
    int count = 0;
    while(count < 4){
        char *dotPos = strchr(ptr, '.');
        long int val;
        char *endptr;
        char temp[4]; // 最多3位数字的缓冲区

        if(dotPos == NULL){
            // 处理最后一段
            val = strtol(ptr, &endptr, 10);
            if(*endptr != '\0') return 0;
        } else {
            size_t len = dotPos - ptr;
            if(len > 3) return 0; // 数字长度超过3位,非法
            strncpy(temp, ptr, len);
            temp[len] = '\0';
            val = strtol(temp, &endptr, 10);
            if(*endptr != '\0') return 0;
            ptr = dotPos + 1;
        }

        if(val < 0 || val > 255) return 0;
        count++;
    }
    return (*ptr == '\0') ? 1 : 0; // 确保没有多余字符
}

// 手动分割IP范围验证
int splitOnDash(char* ipStr){
    char *dashPos = strchr(ipStr, '-');
    if(dashPos == NULL){
        // 没有'-',直接验证单个IP
        return splitOnDot(ipStr);
    }

    // 处理第一个IP
    char firstIp[16];
    size_t firstLen = dashPos - ipStr;
    if(firstLen >= sizeof(firstIp)) return 0;
    strncpy(firstIp, ipStr, firstLen);
    firstIp[firstLen] = '\0';
    if(!splitOnDot(firstIp)) return 0;

    // 处理第二个IP
    char secondIp[16];
    strncpy(secondIp, dashPos+1, sizeof(secondIp)-1);
    secondIp[sizeof(secondIp)-1] = '\0';
    return splitOnDot(secondIp);
}

备注:内容来源于stack exchange,提问作者Aman H

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 10:28:08