C语言 gets() 函数用于从标准输入(通常是键盘)读取一行字符串。gets() 的主要作用是读取用户输入的一行文本,并将其存储到指定的字符数组中。
这个函数在早期的C语言编程中经常被使用,但由于其存在严重的安全隐患,现在已经不推荐使用。
gets() 函数的核心作用是从标准输入读取字符,直到遇到换行符\n或文件结束符EOF为止。gets() 会将读取到的字符存储到指定的字符数组中,并在字符串末尾自动添加空字符\0作为结束标志。这个函数会丢弃输入中的换行符,不将其存储在目标数组中。
举个例子,假设用户输入了 "Hello, World!",gets() 函数会读取这个字符串并将其存储到指定的数组中,最终数组中的内容将是 "Hello, World!\0"。
然而,gets() 函数的一个重大缺陷是它不会检查目标数组的大小,这意味着如果用户输入的字符串长度超过了数组的容量,gets() 函数会继续往数组后面的内存空间写入数据,导致缓冲区溢出。这种行为可能会覆盖其他变量的值,甚至可能被恶意利用来执行未经授权的代码。
gets() 函数的用法
gets() 函数的位于
char *gets(char *str);
gets() 函数只接受一个参数,也就是 str。str 一个指向 char 类型的指针,用于存储读取到的字符串,这个指针通常指向一个预先分配好的字符数组。
gets() 函数的返回值有两种情况:
如果成功读取了字符串,函数返回与参数 str 相同的指针。
如果在读取过程中遇到了错误或到达了文件末尾(EOF),函数返回 NULL。
下面是一个使用 gets() 函数的简单示例:
#include
int main() {
char name[50];
printf("请输入你的名字: ");
gets(name);
printf("你好,%s!\n", name);
return 0;
}
在这个例子中,我们声明了一个可以存储 50 个字符的数组 name。gets() 函数从标准输入读取用户输入的字符串,并将其存储在 name 数组中。然后,我们使用 printf() 函数打印欢迎消息。
运行这段代码,可能会得到如下输出:
请输入你的名字: John Doe
你好,John Doe!
gets() 函数的安全问题
尽管 gets() 函数使用简单,但它存在严重的安全隐患,如果用户输入的字符串长度超过了数组的容量,就会发生缓冲区溢出。例如,在上面的示例中,如果用户输入超过 49 个字符(考虑到结尾的空字符),就会导致缓冲区溢出。
考虑以下情况:
#include
int main() {
char name[10];
int important_data = 42;
printf("请输入你的名字: ");
gets(name);
printf("你好,%s!\n", name);
printf("重要数据: %d\n", important_data);
return 0;
}
如果用户输入一个长度超过 9 的字符串,例如 "abcdefghijklmnop",gets() 函数会继续往 name 数组后面的内存空间写入数据,可能会覆盖 important_data 的值。
由于 gets() 函数的安全问题,现代C语言编程中已经不再推荐使用它。gets() 的替代方案包括:
fgets():这个函数允许指定最大读取字符数,可以有效防止缓冲区溢出。
gets_s():安全版的 gets(),最早由微软的 Visual C++ 编译器所支持,它要求指定缓冲区大小,从而防止缓冲区溢出。
scanf():使用格式化输入函数,可以限制输入的字符数。
getline():这是一个 POSIX 函数,可以动态分配内存来存储输入的字符串。
下面是使用 fgets() 函数的安全版本示例:
#include
#include
int main() {
char name[50];
printf("请输入你的名字: ");
if (fgets(name, sizeof(name), stdin) != NULL) {
name[strcspn(name, "\n")] = 0; // 移除换行符
printf("你好,%s!\n", name);
}
return 0;
}
在这个例子中,fgets() 函数限制了最大读取字符数,有效防止了缓冲区溢出。同时,我们使用 strcspn() 函数来移除 fgets() 可能读取的换行符。
下面是使用 gets_s() 的示例代码:
#include
#define MAX_LENGTH 100
int main() {
char input[MAX_LENGTH];
if (gets_s(input, sizeof(input)) != NULL) {
printf("您输入的是:%s\n", input);
} else {
printf("读取输入时发生错误\n");
}
return 0;
}
值得注意的是,gets_s() 函数并不是所有编译器都支持,因为它只是 C11 标准的可选部分或者扩展部分,而不是核心部分。如果你的编译器不支持 gets_s(),可以考虑使用其他替代方案。
总结
gets() 函数虽然使用简单,但由于其固有的安全风险,在现代C语言编程中应该避免使用。作为替代,应该使用更安全的函数如 fgets()、scanf()、getline() 或者 gets_s()。这些函数提供了更好的输入控制,可以有效防止缓冲区溢出等安全问题。