当我尝试用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会对任何使用它的代码发出警告。

其他回答

fgets。

从stdin读取:

char string[512];

fgets(string, sizeof(string), stdin); /* no buffer overflows here, you're safe! */

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

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

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

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

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

简单地说,gets() (can)是危险的,因为用户可能会输入比变量有足够空间存储的内容更大的内容。第一个答案是关于fgets()以及为什么它更安全。

额外的信息:

从man 3得到的Linux Ubuntu你会看到(强调):

描述 永远不要使用这个函数。

并且,从cppreference.com wiki这里(https://en.cppreference.com/w/c/io/gets)你会看到:Notes从不使用gets()。

笔记 gets()函数不执行边界检查,因此这个函数非常容易受到缓冲区溢出攻击。它不能安全使用(除非程序运行在限制stdin上可以显示内容的环境中)。因此,该函数在C99标准的第三个勘误表中已被弃用,并在C11标准中被完全删除。Fgets()和gets_s()是推荐的替换。 永远不要使用gets()。

如您所见,该函数在C11或更高版本中已完全弃用并被删除。

请使用fgets()或gets_s()。

下面是我使用fgets()的演示,带有完整的错误检查:

从read_stdin_fgets_basic_input_from_user.c:

#include <errno.h>   // `errno`
#include <stdio.h>   // `printf()`, `fgets()`
#include <stdlib.h>  // `exit()`
#include <string.h>  // `strerror()`

// int main(int argc, char *argv[])  // alternative prototype
int main()
{
    char buf[10];

    // NEVER USE `gets()`! USE `fgets()` BELOW INSTEAD!

    // USE THIS!: `fgets()`: "file get string", which reads until either EOF is
    // reached, OR a newline (`\n`) is found, keeping the newline char in
    // `buf`.
    // For `feof()` and `ferror()`, see:
    // 1. https://en.cppreference.com/w/c/io/feof
    // 1. https://en.cppreference.com/w/c/io/ferror
    printf("Enter up to %zu chars: ", sizeof(buf) - 1); // - 1 to save room
                                                        // for null terminator
    char* retval = fgets(buf, sizeof(buf), stdin);
    if (feof(stdin))
    {
        // Check for `EOF`, which means "End of File was reached".
        // - This doesn't really make sense on `stdin` I think, but it is a good
        //   check to have when reading from a regular file with `fgets
        //   ()`. Keep it here regardless, just in case.
        printf("EOF (End of File) reached.\n");
    }
    if (ferror(stdin))
    {
        printf("Error indicator set. IO error when reading from file "
               "`stdin`.\n");
    }
    if (retval == NULL)
    {
        printf("ERROR in %s(): fgets() failed; errno = %i: %s\n",
            __func__, errno, strerror(errno));

        exit(EXIT_FAILURE);
    }

    size_t num_chars_written = strlen(buf) + 1; // + 1 for null terminator
    if (num_chars_written >= sizeof(buf))
    {
        printf("Warning: user input may have been truncated! All %zu chars "
               "were written into buffer.\n", num_chars_written);
    }
    printf("You entered \"%s\".\n", buf);


    return 0;
}

示例运行和输出:

eRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=c17 read_stdin_fgets_basic_input_from_user.c -o bin/a && bin/a
Enter up to 9 chars: hello world!
Warning: user input may have been truncated! All 10 chars were written into buffer.
You entered "hello wor".

eRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=c17 read_stdin_fgets_basic_input_from_user.c -o bin/a && bin/a
Enter up to 9 chars: hey
You entered "hey
".

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()允许您指定从标准输入缓冲区中取出多少字符,以便它们不会溢出变量。