弃用gets(),改用fgets()就能解决问题了?!

首先自问自答,错!fgets()里面同样有陷阱!


其实http://www.cplusplus.com/reference/clibrary/cstdio/gets/说得很清楚了

Notice that gets does not behave exactly as fgets does with stdin as argument: 

First, the ending newline character is not included withgets while with fgets it is. 

Second, gets does not let you specify a limit on how many characters are to be read, so you must be careful with the size of the array pointed by str to avoid buffer overflows.


先来看看gets()的问题(对应于英文的Second,很多人很熟悉了吧):

也许老谭没有讲,也许K&R C没有讲。但是只要你愿意再深入C一点,你翻开《CSAPP》,你翻开《C专家编程》

里面都会很严肃地告诉你,gets()函数有重大隐患,曾引起了无数的内存溢出攻击。

因为gets不对输入长度进行检查和限制,如果输入长度超出了字符数组,gets会向内存继续写入,产生意想不到的问题。


好的,这个时候这些书,或者大神们都会告诉你,用fgets()替代gets()。

那么是不是简单限制字数就完了呢?情况不是这样的。


再说gets()的烦人之处(对应于英文的First):

当下的很多命令行系统都是采取行扫描方式,一次回车,送一次输入。

通常来说这个回车符都是不会读取到变量里的。可是fgets是个例外。


做一个简单打开文件示例时这个问题凸显了出来:

#include <stdio.h>

int main()
{
    FILE *fp;
    char name[40];

    fgets(name, 40, stdin);

    if ( (fp = fopen(name, "rb")) == NULL)
    {
        perror("open file");
        return -1;
    }

    fclose(fp);
    return 0;
}

我的目录下有一个叫test.s的文件。但是我输入之后却出现了错误信息open file: No such file or directory。

printf打印name字符串没有发现问题。输出是“test.s”。

于是用GDB调试这段代码,在跟踪name内容时,发现了问题:

(gdb) s
test.s
10     if ( (fp = fopen(name, "rb")) == NULL)
1: name = "test.s\n\000\261\205\004\bP\204\004\b\000\000\000\000\333;\026\000$\263*\000\364\257*\000U<\026\000\231\205\004\b"

果然\n一并被吞了进去。


修改如下:

#include <stdio.h>

int main()
{
    FILE *fp;
    char name[40], buf[40];

    fgets(buf, 40, stdin);
    memcpy(name, buf, strlen(buf) - 1);

    if ( (fp = fopen(name, "rb")) == NULL)
    {
        perror("open file");
        return -1;
    }

    fclose(fp);
    return 0;
}


(如果你又更好的办法去\n,欢迎留言)

(完)



你可能感兴趣的:(File,null,buffer,character,FP,newline)