运行时为字符串分配内存:统计'2'后接'1'出现次数的程序问题
修复动态字符串分配与"21"统计逻辑的问题
看起来你在实现一个统计字符串中"21"出现次数的程序,不过原代码的动态字符串分配逻辑和一些细节存在问题,我来帮你梳理并修复这些问题:
原代码的核心问题
- 未重置的索引变量:
main函数里的i定义在测试用例循环外,每次处理新测试用例时不会重置为0,会导致后续字符串写入内存越界。 - 动态内存分配的错误:
- 初始
malloc(1*sizeof(char))仅分配1字节,写入第一个字符后就需要扩容,但realloc逻辑不完整,也没有检查分配是否成功。 - 未处理
realloc失败的情况(返回NULL),可能导致程序崩溃。
- 初始
- 输入缓冲区残留问题:
scanf("%d", &t)读取测试用例数量后,输入缓冲区会留下换行符,第一个getc(stdin)会读取到这个换行符,导致第一个测试用例的字符串为空或错误。 - 越界访问风险:
penalty_shoot函数中,当遍历到字符串最后一个字符时,s[i+1]会访问超出'\0'的内存,属于未定义行为。 - 内存泄漏:每个测试用例分配的字符串用完后未调用
free,会导致内存泄漏。
修复后的完整代码
#include <stdio.h> #include <stdlib.h> #include <string.h> int penalty_shoot(char* s) { int count = 0; // 遍历到倒数第二个字符即可,避免访问s[i+1]越界 for (int i = 0; s[i] != '\0' && s[i+1] != '\0'; i++) { if (s[i] == '2' && s[i+1] == '1') { count++; } } return count; } int main() { int t; // 读取测试用例数量后,清除输入缓冲区的换行符 if (scanf("%d", &t) != 1) { fprintf(stderr, "Failed to read test case count\n"); return 1; } // 清除scanf留下的换行符 while (getchar() != '\n'); while (t--) { char *str = NULL; size_t len = 0; int c; // 使用getline简化动态字符串读取(POSIX标准,Windows环境可替换为自定义逻辑) if (getline(&str, &len, stdin) == -1) { fprintf(stderr, "Failed to read string\n"); free(str); continue; } // 去掉getline读取到的换行符(如果存在) size_t str_len = strlen(str); if (str_len > 0 && str[str_len - 1] == '\n') { str[str_len - 1] = '\0'; } int result = penalty_shoot(str); printf("Count: %d\n", result); // 释放动态分配的内存,避免泄漏 free(str); } return 0; }
关键修复点解释
优化统计函数:
- 把遍历条件改为
s[i] != '\0' && s[i+1] != '\0',确保不会访问超出字符串范围的内存,规避未定义行为。 - 合并两个
if为一个逻辑判断,代码更简洁易读。
- 把遍历条件改为
修复输入处理:
- 在
scanf后添加while (getchar() != '\n');,清除输入缓冲区里残留的换行符,避免第一个测试用例读取到空字符串。 - 使用
getline函数(POSIX标准)自动处理动态字符串的分配和扩容,简化代码逻辑。如果你的环境不支持getline,可以用下面的手动实现版本替代。
- 在
内存管理优化:
- 每个测试用例结束后调用
free(str),释放动态分配的内存,避免内存泄漏。 - 检查
getline的返回值,处理读取失败的情况。
- 每个测试用例结束后调用
手动实现动态字符串读取(替代getline)
如果你的编译器不支持getline,可以用下面的手动扩容逻辑替换:
char *str = malloc(1 * sizeof(char)); if (str == NULL) { fprintf(stderr, "Memory allocation failed\n"); continue; } size_t current_size = 1; size_t index = 0; int c; // 读取字符,直到换行或EOF while ((c = getchar()) != '\n' && c != EOF) { // 检查是否需要扩容 if (index >= current_size - 1) { // 留一个位置给'\0' current_size *= 2; char *temp = realloc(str, current_size * sizeof(char)); if (temp == NULL) { fprintf(stderr, "Reallocation failed\n"); free(str); str = NULL; break; } str = temp; } str[index++] = (char)c; } if (str != NULL) { str[index] = '\0'; // 添加字符串结束符 int result = penalty_shoot(str); printf("Count: %d\n", result); free(str); }
这个手动版本中,每次内存不足时会扩容为原来的2倍,同时检查malloc和realloc的返回值确保分配成功,最后手动添加字符串结束符'\0'。
内容的提问来源于stack exchange,提问作者yogi
相关产品推荐
相关产品推荐

