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

自定义strtoi函数编译时问题:静态字符数组适配异常排查

问题根源:缺少C风格字符串的终止符\0

你的问题核心在于**std::strlen的工作机制**:它会从传入的指针位置开始,逐个读取字节,直到碰到\0(空字符)才停止计数。而你定义的静态和动态char数组都没有显式添加这个终止符,导致strlen的行为完全是未定义的——它会越界读取内存,直到碰巧找到一个\0为止。

为什么静态数组出错,动态数组“看似正常”?

静态数组(栈分配)

比如你的char pos[3] {'1','2','3'};:

  • 数组大小是3,三个位置都被字符填满了,没有多余空间存放\0。
  • strlen(pos)会从pos[0]开始,一直往内存后面读,直到碰到某个\0。栈上的内存是连续的,后面可能是其他变量的内存、栈帧的元数据等,这些内容是不确定的,所以计算出的len值完全随机。
  • 你的循环逻辑依赖这个错误的len,比如如果len远大于3,循环会读到非数字字符,直接返回0,这就是第一个测试用例返回0的原因。

而neg[4] {'-','1','2','3'};返回-123纯粹是巧合——刚好这个数组后面的内存里有一个\0,让strlen算出了正确的长度4,但这是不可靠的,换个编译环境或调整代码顺序就可能出错。

动态数组(堆分配)

new char[3] {'1','2','3'};同样没有\0,但你测试时返回正确也是巧合:

  • 堆内存分配时,分配的区域后面可能刚好遗留了之前释放的内存中的\0,让strlen误打误撞得到了正确的长度。
  • 这依然是未定义行为,不能依赖这种“运气”。

如何修改函数兼容两种场景?

有两种可靠的解决方案:

方案1:保证传入的是标准C风格字符串(带\0)

修改你的数组初始化代码,显式添加\0终止符:

// 静态数组:多分配一个位置放\0
char pos[4] {'1','2','3','\0'};
char neg[5] {'-','1','2','3','\0'};

// 动态数组:同样多分配一个位置
char* pos = new char[4] {'1','2','3','\0'};
char* neg = new char[5] {'-','1','2','3','\0'};

这种情况下你的原函数可以正常工作,但需要调用者严格保证字符串是以\0终止的。

方案2:修改函数,让调用者传入字符串长度(更安全)

放弃依赖strlen,改为让调用者传入数组的实际长度,彻底避免越界问题。同时建议替换std::pow——因为它返回double,当数字较大时会有精度丢失的风险,改用整数乘法更可靠:

int strtoi(char* ch, int len) {
    int sum = 0;
    int is_negative = (ch[0] == '-') ? 1 : 0;
    
    // 从有效数字位开始计算
    for (int i = is_negative; i < len; ++i) {
        // 检查是否为数字
        if (ch[i] < '0' || ch[i] > '9') {
            return 0;
        }
        // 整数乘法累加,避免pow的精度问题
        sum = sum * 10 + (ch[i] - '0');
    }
    
    return is_negative ? -sum : sum;
}

// 调用方式
int main() {
    char pos[3] {'1','2','3'};
    std::cout << strtoi(pos, 3) << std::endl; // 传入长度3
    
    char neg[4] {'-','1','2','3'};
    std::cout << strtoi(neg, 4) << std::endl; // 传入长度4
    
    char* dyn_pos = new char[3] {'1','2','3'};
    std::cout << strtoi(dyn_pos, 3) << std::endl;
    delete[] dyn_pos;
    
    char* dyn_neg = new char[4] {'-','1','2','3'};
    std::cout << strtoi(dyn_neg, 4) << std::endl;
    delete[] dyn_neg;
    
    return 0;
}

补充:静态与动态数组的内存分配差异

  • 静态数组:编译期确定大小,分配在栈上。栈内存是连续的,但栈上的内容会被其他变量、函数调用等覆盖,没有\0的话,strlen会读到不属于当前数组的内存。
  • 动态数组:运行期通过new在堆上分配内存。堆内存的内容在分配前可能是之前程序使用过的遗留数据,碰巧有\0就是你测试用例2“正常”的原因,但这完全不可控。

内容的提问来源于stack exchange,提问作者Scheir

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 20:17:27