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

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

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

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

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


当前回答

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

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

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

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

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

其他回答

如果不破坏API,就不能删除API函数。如果您这样做,许多应用程序将不再编译或运行。

这就是一篇参考文章给出的原因:

读取溢出的行 由s指向的数组结果为 未定义的行为。fgets()的使用 建议。

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

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

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

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

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

因为gets在从stdin获取字节并将它们放到某个地方时不会做任何检查。举个简单的例子:

char array1[] = "12345";
char array2[] = "67890";

gets(array1);

首先,你可以输入你想要的字符数,get不会关心它。其次,字节超过数组的大小(在本例中为array1)将覆盖它们在内存中找到的任何内容,因为gets将写入它们。在前面的例子中,这意味着如果你输入"abcdefghijklmnopqrts"可能,不可预知的,它也会覆盖array2或其他东西。

该函数是不安全的,因为它假定输入一致。永远不要用它!

您不应该使用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相同的保护,它可以防止缓冲区溢出,但它也通知调用者发生了什么,并清除多余的字符,以便它们不会影响您的下一个输入操作。

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

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分钟左右受到了批评。

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