自定义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
相关产品推荐
相关产品推荐

