当我尝试用GCC编译使用gets()函数的C代码时,我得到这个警告:

(.text+0x34):警告:' gets'函数是危险的,不应该使用。

我记得这与堆栈保护和安全性有关,但我不确定确切的原因。

我如何删除这个警告,为什么使用gets()会有这样的警告?

如果gets()是如此危险,那么为什么我们不能删除它?


当前回答

您不应该使用gets,因为它无法阻止缓冲区溢出。如果用户输入的数据超过了缓冲区的容量,您很可能会出现损坏或更糟的情况。

事实上,ISO实际上已经从C标准中删除了get(从C11开始,尽管它在C99中被弃用),考虑到他们对向后兼容性的高度评价,这应该表明该函数有多糟糕。

正确的做法是将fgets函数与stdin文件句柄一起使用,因为您可以限制从用户读取的字符。

但这也有它的问题,如:

用户输入的额外字符将在下次读取。 没有用户输入过多数据的快速通知。

为此,几乎每一个C程序员在他们职业生涯的某个阶段都会编写一个更有用的fget包装器。这是我的:

#include <stdio.h>
#include <string.h>

#define OK       0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
    int ch, extra;

    // Get line with buffer overrun protection.
    if (prmpt != NULL) {
        printf ("%s", prmpt);
        fflush (stdout);
    }
    if (fgets (buff, sz, stdin) == NULL)
        return NO_INPUT;

    // If it was too long, there'll be no newline. In that case, we flush
    // to end of line so that excess doesn't affect the next call.
    if (buff[strlen(buff)-1] != '\n') {
        extra = 0;
        while (((ch = getchar()) != '\n') && (ch != EOF))
            extra = 1;
        return (extra == 1) ? TOO_LONG : OK;
    }

    // Otherwise remove newline and give string back to caller.
    buff[strlen(buff)-1] = '\0';
    return OK;
}

用一些测试代码:

// Test program for getLine().

int main (void) {
    int rc;
    char buff[10];

    rc = getLine ("Enter string> ", buff, sizeof(buff));
    if (rc == NO_INPUT) {
        printf ("No input\n");
        return 1;
    }

    if (rc == TOO_LONG) {
        printf ("Input too long\n");
        return 1;
    }

    printf ("OK [%s]\n", buff);

    return 0;
}

它提供了与fgets相同的保护,它可以防止缓冲区溢出,但它也通知调用者发生了什么,并清除多余的字符,以便它们不会影响您的下一个输入操作。

随心所欲地使用它,我在此发布它的“做你该死的想做的事情”许可证:-)

其他回答

您不应该使用gets,因为它无法阻止缓冲区溢出。如果用户输入的数据超过了缓冲区的容量,您很可能会出现损坏或更糟的情况。

事实上,ISO实际上已经从C标准中删除了get(从C11开始,尽管它在C99中被弃用),考虑到他们对向后兼容性的高度评价,这应该表明该函数有多糟糕。

正确的做法是将fgets函数与stdin文件句柄一起使用,因为您可以限制从用户读取的字符。

但这也有它的问题,如:

用户输入的额外字符将在下次读取。 没有用户输入过多数据的快速通知。

为此,几乎每一个C程序员在他们职业生涯的某个阶段都会编写一个更有用的fget包装器。这是我的:

#include <stdio.h>
#include <string.h>

#define OK       0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
    int ch, extra;

    // Get line with buffer overrun protection.
    if (prmpt != NULL) {
        printf ("%s", prmpt);
        fflush (stdout);
    }
    if (fgets (buff, sz, stdin) == NULL)
        return NO_INPUT;

    // If it was too long, there'll be no newline. In that case, we flush
    // to end of line so that excess doesn't affect the next call.
    if (buff[strlen(buff)-1] != '\n') {
        extra = 0;
        while (((ch = getchar()) != '\n') && (ch != EOF))
            extra = 1;
        return (extra == 1) ? TOO_LONG : OK;
    }

    // Otherwise remove newline and give string back to caller.
    buff[strlen(buff)-1] = '\0';
    return OK;
}

用一些测试代码:

// Test program for getLine().

int main (void) {
    int rc;
    char buff[10];

    rc = getLine ("Enter string> ", buff, sizeof(buff));
    if (rc == NO_INPUT) {
        printf ("No input\n");
        return 1;
    }

    if (rc == TOO_LONG) {
        printf ("Input too long\n");
        return 1;
    }

    printf ("OK [%s]\n", buff);

    return 0;
}

它提供了与fgets相同的保护,它可以防止缓冲区溢出,但它也通知调用者发生了什么,并清除多余的字符,以便它们不会影响您的下一个输入操作。

随心所欲地使用它,我在此发布它的“做你该死的想做的事情”许可证:-)

为了安全地使用get,您必须确切地知道将要读取多少字符,以便使缓冲区足够大。只有当您确切地知道要读取哪些数据时,您才会知道这一点。

您希望使用具有签名的fgets,而不是使用gets

char* fgets(char *string, int length, FILE * stream);

(fgets,如果它读取了整行,将在字符串中留下'\n';你得自己处理。)

直到1999年的ISO C标准,gets仍然是语言的官方部分,但在2011年的标准中被正式删除。大多数C实现仍然支持它,但至少gcc会对任何使用它的代码发出警告。

我最近在comp.lang.c的一篇USENET文章中读到,gets()将从标准中删除。哦吼

你会很高兴知道 委员会刚刚投票(一致通过) 将gets()从 还有草案。

C语言的gets函数是危险的,是一个代价非常高昂的错误。Tony Hoare在他的演讲“Null References: The Billion Dollar Mistake”中特别提到了这一点:

http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare

整个小时都值得一看,但他从30分钟开始的评论观点在39分钟左右受到了批评。

希望这能激发你对整个演讲的兴趣,让你注意到我们如何需要语言中更正式的正确性证明,以及语言设计者如何应该为他们语言中的错误而受到指责,而不是程序员。这似乎是糟糕语言的设计者打着“程序员自由”的幌子把责任推给程序员的全部可疑原因。

gets() is dangerous because it is possible for the user to crash the program by typing too much into the prompt. It can't detect the end of available memory, so if you allocate an amount of memory too small for the purpose, it can cause a seg fault and crash. Sometimes it seems very unlikely that a user will type 1000 letters into a prompt meant for a person's name, but as programmers, we need to make our programs bulletproof. (it may also be a security risk if a user can crash a system program by sending too much data).

Fgets()允许您指定从标准输入缓冲区中取出多少字符,以便它们不会溢出变量。