C语言gets()、fgets()和gets_s()读取字符串
在C语言中,读取字符串是一个常见的操作,有几种不同的函数可以用来完成这个任务,其中包括 gets()、fgets() 和 gets_s()。这些函数虽然都用于读取字符串,但它们在安全性和使用方式上有着显著的差异。
gets() 函数
gets() 函数是最简单的字符串输入函数,但也是最不安全的。gets() 会从标准输入(通常是键盘)读取字符,直到遇到换行符或文件结束符。然而,gets() 不会检查输入的长度是否超过目标数组的大小,这可能导致缓冲区溢出,是一个严重的安全隐患。
以下是 gets() 的使用示例:
#include <stdio.h> int main() { char str[10]; printf("请输入一个字符串:"); gets(str); printf("你输入的是:%s\n", str); return 0; }
输出结果可能如下:
请输入一个字符串:Hello, World! 你输入的是:Hello, World!
在这个例子中,如果用户输入的字符串超过 9 个字符(不包括空字符),就会发生缓冲区溢出。由于这个安全问题,gets() 函数在 C11 标准中已被废弃,现代 C 编译器通常会对其使用发出警告。
fgets() 函数
fgets() 是一个更安全的替代方案,它允许指定要读取的最大字符数,从而防止缓冲区溢出。fgets() 函数的原型如下:
char *fgets(char *str, int n, FILE *stream);
这里,str 是目标字符数组,n 是要读取的最大字符数(包括空字符),stream 是输入流(如果从标准输入读取,使用 stdin)。
以下是 fgets() 的使用示例:
#include <stdio.h> int main() { char str[10]; printf("请输入一个字符串:"); fgets(str, sizeof(str), stdin); printf("你输入的是:%s", str); return 0; }
输出结果可能如下:
请输入一个字符串:Hello, World! 你输入的是:Hello, Wo
注意,fgets() 会保留输入中的换行符(如果读取到的话)。如果输入超过指定的长度,fgets() 会截断输入,确保不会发生缓冲区溢出。
gets_s() 函数
gets_s() 是 C11 标准引入的一个安全版本的 gets(),它的行为类似于 fgets(),但专门用于从标准输入读取。gets_s() 的原型如下:
char *gets_s(char *str, rsize_t n);
这里,str 是目标字符数组,n 是数组的大小。gets_s() 会读取直到遇到换行符、文件结束符或达到 n-1 个字符。另外,gets_s() 总是在字符串末尾添加结束符\0
。
gets_s() 只是 C11 标准的可选部分或者扩展部分,并不是所有的编译器都必须实现它,所以使用之前请亲自测试,确保该函数有效。
以下是 gets_s() 的使用示例:
#include <stdio.h> int main() { char str[10]; printf("请输入一个字符串:"); if (gets_s(str, sizeof(str)) == NULL) { printf("输入错误\n"); return 1; } printf("你输入的是:%s\n", str); return 0; }
输出结果可能如下:
请输入一个字符串:Hello, World! 输入错误
如果输入超过指定长度,gets_s() 会清空输入缓冲区,将 str 的第一个字符设置为空字符,并返回 NULL。这提供了一种错误处理机制,使程序能够对输入错误做出响应。
对比和总结
让我们比较这三个函数的主要特点:
- 安全性:gets() 最不安全,fgets() 和 gets_s() 都提供了缓冲区溢出保护。
- 可移植性:gets() 在许多现代编译器中已被废弃,fgets() 广泛支持且可移植,gets_s() 是 C11 标准的可选部分,并不是所有编译器都支持。
- 使用便利性:gets() 最简单,但不安全。fgets() 需要指定输入流,而 gets_s() 专门用于标准输入。
- 错误处理:gets_s() 提供了最强大的错误处理机制。
考虑到安全性和可移植性,fgets() 通常是最佳选择,它既安全又被广泛支持。如果你的编译器支持 gets_s(),并且只需要从标准输入读取,gets_s() 也是一个很好的选择。无论如何,都应该避免使用 gets(),因为它存在严重的安全隐患。