跳转到内容

C 编程/stdio.h/gets

来自维基教科书,开放的书,开放的世界

gets 是 C 标准库中的一个函数,在头文件 stdio.h 中声明,它从标准输入读取一行并将其存储在调用者提供的缓冲区中。

强烈建议不要使用 gets。 它保留在 C89 和 C99 标准中以实现向后兼容性(但在 C99 的后期版本中正式弃用)。 它已从 C11 标准中删除[1],取而代之的是引入了范围检查替代方法 gets_s[2] 当链接使用 gets 的代码时,许多开发工具(例如 GNU ld)会发出警告。

它可以按如下方式实现(使用 getchar

char *
gets (char *s)
{
    char * ch = s;
    int k;
	
    /* until we read a newline */
    while ((k = getchar ()) != '\n') {

        if (k == EOF) {
            /* EOF at start of line or errors other than EOF return NULL */
            if (ch == s || !feof(stdin)) 
                return NULL;

            break;
        }
		
        /* character is stored at address, and pointer is incremented */
        *ch++ = k;
    }
		
    /* Null-terminating character added */
    *ch = '\0';
		
    /* return original pointer */
    return s; 
}

程序员必须知道 gets 将读取的字符数的最大限制,以便他可以确保缓冲区足够大。 在没有数据知识的情况下这是不可能的。 这种设计缺陷会导致错误,并为通过缓冲区溢出利用计算机安全打开了一个通道。 许多资料建议程序员在新的程序中永远不要使用 gets[3][4][5]

替代方案

[编辑 | 编辑源代码]

可以使用其他行输入函数代替 gets,以避免缓冲区溢出错误。 一个简单的替代方法是 fgets。 当用以下形式的代码替换代码时

char buffer[BUFFERSIZE];
gets(buffer);

用以下形式的代码

char buffer[BUFFERSIZE];
fgets(buffer, sizeof(buffer), stdin);

必须记住,fgets(buffer, sizeof(buffer), stdin) 调用不同于 gets(buffer),不仅在于缓冲区溢出保护,还在于 fgets(buffer, sizeof(buffer), stdin) 保留终止换行符(如果输入行以换行符终止),而 gets(buffer) 会将其丢弃。

The C Programming Language 的第一版没有使用 gets,而是描述了一个更安全的函数 getline(buffer, length),它不会溢出缓冲区,并且会返回有关读取了多少字节的有用信息(这将允许输入 NUL)或在错误或 EOF 时返回 -1。 不清楚为什么gets最终出现在 C 标准库中而不是这个函数。

POSIX-2008 定义了 getline(char **buffer, size_t *buffersize, FILE*),它根据需要重新分配缓冲区以容纳输入行(注意缓冲区和大小的额外间接级别)。[6]

C1X 提案有一个替换函数 gets_s(char* buffer, size_t n),如果该行不能容纳在 n-1 个字符中,则返回空字符串并消耗整个当前行。

安全使用

[编辑 | 编辑源代码]

安全使用 gets 需要程序员确保缓冲区溢出不会成为问题。 唯一可移植的方法是通过某种方式确保输入文件不能包含比缓冲区更长的行,例如通过确保该文件是由不能写入此类行的程序创建的。 还有许多其他相对复杂的方法可以防止缓冲区溢出,其可移植性程度各不相同。 一种可能性是使用保护页面来保护内存。 单独使用,这会将可利用的缓冲区溢出变成单纯的崩溃。 与异常处理程序(例如涉及 SIGSEGVsigaction 的处理程序)结合使用,保护页面可以允许优雅的错误处理。

  1. n1548,xiv 页
  2. n1548,K.3.5.4.1
  3. GNU. "行输入". GNU C 库. GNU. http://www.gnu.org/software/libc/manual/html_node/Line-Input.html#Line-Input. Retrieved 2008-08-02. "gets 函数非常危险,因为它不提供防止字符串 s 溢出的保护。 GNU 库仅出于兼容性原因包含它。 你应该始终使用 fgetsgetline 代替." (原文强调。)
  4. "为什么每个人都说不要使用 gets()?". comp.lang.c 常见问题解答. Retrieved 2008-08-02.
  5. "gets(3)". man. http://linux.die.net/man/3/gets. Retrieved 2008-08-02. "不要使用 gets()。 由于在事先不知道数据的情况下无法确定 gets() 将读取多少个字符,并且由于 gets() 会继续将字符存储到缓冲区的末尾,因此使用它非常危险。 它已被用于破坏计算机安全." 
  6. "getdelim". The Open Group.
华夏公益教科书