使用fgets()统计字符串字符出现次数出现垃圾值,gets()却正常的原因
fgets读取输入输出垃圾值,gets正常的原因及修复方法
问题重现
以下是统计字符串元素出现次数的代码,使用fgets时会输出垃圾值,改用gets则结果正常:
#include<stdio.h> #include<string.h> #include<ctype.h> #include<stdlib.h> int main() { char c[1005]; fgets(c, 1005, stdin); int cnt[26] = {0}; for (int i = 0; i < strlen(c); i++) { cnt[c[i] - 'a']++; } for (int i = 0; i < strlen(c); i++) { if(cnt[c[i]-'a'] != 0) { printf("%c %d\n", c[i], cnt[c[i] - 'a']); cnt[c[i] - 'a'] = 0; } } return 0; }
使用fgets时的输出:
baaaabca b 2 a 5 c 1 32767 -------------------------------- Process exited after 8.61 seconds with return value 0 Press any key to continue . . . _
原因分析
- fgets与gets的核心区别:fgets会将输入时按下的换行符
\n一并读取到字符串中,而gets会自动丢弃末尾的换行符。 - 非法数组访问导致垃圾值:当代码处理到换行符时,
'\n' - 'a'的计算结果为负数(ASCII值:'\n'是10,'a'是97,10-97=-87),这会触发未定义行为——访问cnt数组的负数索引,读取到内存中的随机垃圾数据(也就是输出里的32767)。 - gets因为自动去掉了换行符,循环不会处理到这个非法字符,所以结果正常。
修复方案
方案1:移除fgets读取的换行符
在调用fgets后,手动去掉字符串末尾的换行符:
#include<stdio.h> #include<string.h> #include<ctype.h> #include<stdlib.h> int main() { char c[1005]; fgets(c, 1005, stdin); // 移除换行符 size_t len = strlen(c); if (len > 0 && c[len-1] == '\n') { c[len-1] = '\0'; } int cnt[26] = {0}; for (int i = 0; i < strlen(c); i++) { cnt[c[i] - 'a']++; } for (int i = 0; i < strlen(c); i++) { if(cnt[c[i]-'a'] != 0) { printf("%c %d\n", c[i], cnt[c[i] - 'a']); cnt[c[i] - 'a'] = 0; } } return 0; }
方案2:仅处理合法的小写字母
在统计和输出时,先判断字符是否为小写字母,避免处理换行符或其他非法字符:
#include<stdio.h> #include<string.h> #include<ctype.h> #include<stdlib.h> int main() { char c[1005]; fgets(c, 1005, stdin); int cnt[26] = {0}; for (int i = 0; i < strlen(c); i++) { // 仅统计小写字母 if (islower(c[i])) { cnt[c[i] - 'a']++; } } for (int i = 0; i < strlen(c); i++) { if (islower(c[i]) && cnt[c[i]-'a'] != 0) { printf("%c %d\n", c[i], cnt[c[i] - 'a']); cnt[c[i] - 'a'] = 0; } } return 0; }
注意:gets函数存在严重的缓冲区溢出风险,已被C标准废弃,不建议使用,优先选择fgets并配合上述修复方案。
内容的提问来源于stack exchange,提问作者Nghi
相关产品推荐
相关产品推荐

