C语言使用%s读取连续字符串出现段错误,求非静态数组解决方案
字符串读取段错误问题解决(不使用静态字符数组)
问题描述
希望通过%s格式说明符从用户处获取字符串,并将其基地址存储在字符指针中。但连续读取两个或三个字符串时,第一个字符串能正常读取,从第二个开始出现段错误。测试代码如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char *s1, *s2, *s3; printf("Enter the first string: "); fflush(stdin); scanf("%s", s1); fflush(stdout); printf("%s\n", s1); printf("Enter the second string: "); fflush(stdin); scanf("%s", s2); fflush(stdout); printf("%s\n", s2); printf("Enter the third string: "); fflush(stdin); scanf("%s", s3); fflush(stdout); printf("%s\n", s3); return 0; }
问题根源
代码里的char *s1, *s2, *s3是未初始化的野指针,指向的内存地址随机且未被程序授权使用。第一次scanf写入时碰巧没触发错误,但后续写入会覆盖非法内存区域,直接引发段错误。另外,fflush(stdin)属于未定义行为,标准C中fflush仅对输出流有效。
解决方案(不使用静态字符数组)
1. 手动用malloc分配内存
先预估输入字符串的最大长度,用malloc分配对应大小的内存,用完后必须用free释放,避免内存泄漏。
修正代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_INPUT_LEN 100 // 根据实际需求调整最大长度 int main() { char *s1 = malloc(MAX_INPUT_LEN); char *s2 = malloc(MAX_INPUT_LEN); char *s3 = malloc(MAX_INPUT_LEN); // 检查内存分配是否成功 if (!s1 || !s2 || !s3) { perror("malloc failed"); return 1; } printf("Enter the first string: "); // 用%99s限制读取长度,防止缓冲区溢出 scanf("%99s", s1); printf("%s\n", s1); printf("Enter the second string: "); scanf("%99s", s2); printf("%s\n", s2); printf("Enter the third string: "); scanf("%99s", s3); printf("%s\n", s3); // 释放分配的内存 free(s1); free(s2); free(s3); return 0; }
2. 使用scanf的m修饰符(C11及以上标准)
%ms格式会让scanf自动分配足够内存存储输入字符串,无需手动预估长度,但要注意:
- 编译器需支持C11或更高版本
- 必须用
free释放scanf分配的内存
修正代码:
#include <stdio.h> #include <stdlib.h> int main() { char *s1 = NULL, *s2 = NULL, *s3 = NULL; printf("Enter the first string: "); // %ms自动分配内存,需传递指针的地址 if (scanf("%ms", &s1) != 1) { perror("scanf failed"); return 1; } printf("%s\n", s1); printf("Enter the second string: "); if (scanf("%ms", &s2) != 1) { perror("scanf failed"); free(s1); // 已分配的内存要提前释放 return 1; } printf("%s\n", s2); printf("Enter the third string: "); if (scanf("%ms", &s3) != 1) { perror("scanf failed"); free(s1); free(s2); return 1; } printf("%s\n", s3); // 释放所有动态分配的内存 free(s1); free(s2); free(s3); return 0; }
额外注意事项
- 禁止使用
fflush(stdin),若要清理输入缓冲区残留字符,可用getchar()循环读取直到换行。 - 使用
scanf读取字符串时,必须限制读取长度(如%Ns,N为缓冲区大小减1),防止缓冲区溢出。
内容的提问来源于stack exchange,提问作者Dewan Habib
相关产品推荐
相关产品推荐

