在C语言的标准输入输出库中,scanf()和gets()都用于从标准输入读取数据,但两者的设计目标和行为逻辑存在明显差异,适用场景也各不相同。

基本功能定义
scanf()是格式化输入函数,支持按照指定的格式字符串读取不同类型的数据,包括整数、浮点数、字符、字符串等,读取规则由格式控制符决定。
gets()是专门用于读取字符串的函数,它会从标准输入读取字符直到遇到换行符,然后将换行符之前的字符存储到目标数组中,并且会自动在字符串末尾添加空字符' '。
输入终止规则差异
scanf()读取字符串时,默认以空白字符(空格、制表符、换行符)作为输入的终止标志,遇到空白字符就会停止读取当前字符串,剩余的空白字符会留在输入缓冲区中。
gets()读取字符串时,只会将换行符作为终止标志,空格、制表符等空白字符都会被当作普通字符读取到字符串中,读取完成后会自动丢弃换行符,不会将其存入数组也不会留在缓冲区。
代码示例对比
以下代码分别使用scanf()和gets()读取包含空格的字符串,观察两者的读取结果:
#include <stdio.h>
int main() {
char str1[100];
char str2[100];
printf("使用scanf读取字符串:");
scanf("%s", str1); // 输入"hello world"
printf("scanf读取结果:%sn", str1);
// 清空输入缓冲区残留的换行符,避免影响后续读取
while (getchar() != 'n');
printf("使用gets读取字符串:");
gets(str2); // 输入"hello world"
printf("gets读取结果:%sn", str2);
return 0;
}
运行后输入hello world,scanf()只会读取到hello,而gets()会完整读取hello world。
缓冲区处理差异
scanf()读取完成后,未读取的字符(包括终止的空白字符)会保留在输入缓冲区中,如果不做处理,后续的输入函数可能会直接读取到这些残留字符,导致输入异常。
gets()读取完成后,只会丢弃终止的换行符,不会保留其他未读取的字符,因为gets()会一直读取直到遇到换行符,所以正常情况下不会有残留字符留在缓冲区。
安全性差异
scanf()读取字符串时,如果指定了宽度(如%10s),可以避免读取的字符超过数组长度,防止缓冲区溢出。如果没有指定宽度,同样存在溢出风险。
gets()没有长度限制参数,无论目标数组有多大,都会一直读取直到遇到换行符,如果输入的字符长度超过数组容量,就会发生缓冲区溢出,这是gets()被C11标准废弃的核心原因,实际开发中不建议使用。
返回值差异
scanf()返回成功读取并赋值的变量个数,如果读取失败或者遇到文件结束符,会返回EOF。
gets()读取成功时返回指向目标字符串的指针,读取失败或者遇到文件结束符时返回NULL。
返回值示例
#include <stdio.h>
int main() {
char str[100];
int ret_scanf;
char *ret_gets;
printf("测试scanf返回值:");
ret_scanf = scanf("%s", str);
printf("scanf返回值:%dn", ret_scanf); // 成功读取返回1
while (getchar() != 'n');
printf("测试gets返回值:");
ret_gets = gets(str);
if (ret_gets != NULL) {
printf("gets返回值:%sn", ret_gets); // 成功返回字符串本身
}
return 0;
}
使用建议
- 如果需要读取包含空格的完整字符串,且能确保输入长度不超过数组容量,可以使用
fgets()替代gets(),fgets()支持指定最大读取长度,更安全。 - 如果读取的是无空格的短字符串,或者需要同时读取多种类型的数据,使用scanf()并指定合适的格式控制符和宽度。
- 避免使用gets()函数,防止缓冲区溢出导致程序异常。
注意:在代码中使用scanf()读取字符串时,建议指定宽度,例如%99s对应长度为100的字符数组,预留一个位置给字符串结束符' '。