为何C语言实现的两数之和程序偶尔返回异常结果?
先给你梳理几个关键的问题点,这些都是导致代码偶尔出现异常的根源:
1. 命令行参数的只读内存被修改(未定义行为)
你用strsep(&argv[1], &delim)来分割输入的数字字符串,但要注意:C语言中argv指向的命令行参数字符串是存储在只读数据段的,strsep会直接修改原字符串(把分隔符替换成\0),这属于对只读内存的写入操作,是未定义行为。这种操作的结果完全不确定——有时候可能看似正常,有时候会导致内存异常,进而影响后续的字符串分割逻辑,比如分割不完整导致nums数组元素缺失或错误,自然找不到目标索引。
解决办法:先把argv[1]的内容复制到一块可写内存里,再进行分割:
char* input_str = strdup(argv[1]); if (!input_str) { printf("Memory allocation failed.\n"); free(nums); return EXIT_FAILURE; } // 用input_str代替argv[1]执行分割逻辑 while ((charred_num = strsep(&input_str, &delim)) != NULL) { // ... 原有赋值和扩容逻辑 } free(input_str); // 最后释放复制的内存
2. 数组扩容时机错误导致越界访问
看你初始化nums的逻辑:
nums[nums_actual_size] = atoi(charred_num); ++nums_actual_size; if (nums_buffer_size <= nums_actual_size) { nums_buffer_size = nums_buffer_size * 2; nums = realloc(nums, sizeof(int) * nums_buffer_size); }
这里的问题是:当nums_actual_size等于nums_buffer_size时,你已经完成了nums[nums_actual_size]的赋值——但此时nums只分配了nums_buffer_size个int的空间,索引范围是0到nums_buffer_size-1,nums[nums_buffer_size]属于越界访问,这会破坏堆内存的结构,导致后续的内存操作(比如malloc/free)出现异常,进而让twosum函数的逻辑出错。
正确的扩容时机应该是在赋值之前检查,如果当前元素数量即将超过缓冲区大小,先扩容再赋值:
// 先检查是否需要扩容 if (nums_actual_size >= nums_buffer_size) { nums_buffer_size *= 2; int* temp = realloc(nums, sizeof(int) * nums_buffer_size); if (!temp) { // 处理realloc失败,避免内存泄漏 free(nums); printf("Memory reallocation failed.\n"); return EXIT_FAILURE; } nums = temp; } // 再执行赋值 nums[nums_actual_size] = atoi(charred_num); ++nums_actual_size;
另外,realloc可能返回NULL,直接把返回值赋值给nums会导致原内存地址丢失,造成内存泄漏,所以先存到临时变量里检查是否成功是更安全的做法。
3. 额外的健壮性建议:检查内存分配失败
你的代码里没有检查malloc和realloc的返回值,如果内存分配失败,后续操作会因为空指针导致程序崩溃。加上返回值检查可以让程序更稳定。
这两个核心问题都属于未定义行为,所以会出现“偶尔出错”的情况——未定义行为的表现是不确定的,有时候刚好内存布局没被破坏,程序就正常运行,有时候破坏了就会出现异常结果。修复这两个点之后,你的代码应该就能稳定运行了。
内容的提问来源于stack exchange,提问作者seafood258

