【问题标题】:segfault on fgets... sometimesfgets 上的段错误...有时
【发布时间】:2012-03-27 13:36:11
【问题描述】:

我从 fgets 得到一个分段,但只是有时。这是别人的代码,我不懂makefile,所以我用printf语句调试...我把它放在main函数的两个部分:(我只创建变量*f和line一次,但我两次 fopen 和 fclose 文件。)

FILE *f = NULL;
char line[1000];
if ((f=fopen(filename,"r+"))==NULL)
{
     printf("Error opening file\n");
     f=0;//...handle error... //(usually just call abort() or return -1
}
//f = rfopen(fname, "r+");
printf("f from eval_args: %d, filename %s\n",f,filename);
printf("trying to read from file...\n");
printf("%s\n",fgets(line, sizeof (line), f));
printf("...succeeded\n");
fclose(f);

这给了我两个不同的输出:

f from eval_args: 4609600, filename /correct/path/to/file
trying to read from file...
 100

...succeeded

f prior to entering density profile: 4609600, filename /correct/path/to/file
trying to read from file...
Segmentation fault (core dumped)

我检查文件是否正确打开,并防止读取超出“行”的字符。我在一个论坛上看到文件名的长度不应超过 49 个字符...但是 a) 这是一个奇怪的限制 b) 为什么它第一次起作用?

有人知道我还能检查什么吗?

【问题讨论】:

  • 遇到 NULL 字符串时 printf() 不打印 (null) 吗?
  • 您能否发布一个可重现该问题的小型可编译程序?
  • 首先你应该把fgets作为一个单独的语句,然后你应该使用调试器来帮助你定位和检查错误。
  • 感谢@Joachim 的建议,我将 fgets 放在了自己的行上,并且段错误肯定存在。现在我必须弄清楚为什么这个低级函数会为相同的输入提供不可重现的行为......
  • @H2CO3,从一个快速的谷歌搜索,它似乎没有很好的定义,但对我来说, printf("%s \n",NULL) 也会导致一个段错误,所以我可能有两个之前的问题。

标签: c segmentation-fault fgets


【解决方案1】:

如果不是很麻烦,你可以,但猜你知道,做类似的事情;

可以美化一点,但是

#include <stdio.h>
#include <stdarg.h>

#define MAX_LINE    1024

void dbg_fprnt(FILE *fh, char *fmt, ...)
{
    char buf[MAX_LINE];
    char inf[MAX_LINE] = {0};
    va_list args;

    if (fmt && *fmt) {
        va_start(args, fmt);
        va_end(args);
        vsprintf(inf, fmt, args);
    }

    buf[0] = '\n';
    buf[1] = '\0';
    if (ferror(fh)) {
        fprintf(stderr, " * ERR, ferror() --- \n");
    } else if (fh == NULL) {
        fprintf(stderr,
            " * DBG PRNT ERR;; Trying to print from NULL ---\n");
    /* else if (and so forth) */
    } else {
        if (fgets(buf, MAX_LINE, fh) == NULL) {
            perror(" * ERR DBG PRNTF FGETS, --");
        }
    }
    printf("%-15s FC:: %s", inf, buf);
}

int main(void)
{
    char *fn = "lorem_ipsum";
    FILE *fh;

    if ((fh = fopen(fn, "r")) == NULL) {
        fprintf(stderr,
            "Unable to open '%s' for read.\n",
            fn);
        return 1;
    }

    setbuf(stdout, NULL);

    dbg_fprnt(fh, "SOME LINE: %d", 123);
    dbg_fprnt(fh, "%s", "SASA");
    dbg_fprnt(fh, "");
    dbg_fprnt(fh, NULL);
    dbg_fprnt(fh, "%s %d !", "Woot", 33);
    dbg_fprnt(fh, "@%d :::", __LINE__);
    fclose(fh);
    dbg_fprnt(fh, "@%d :::", __LINE__);


    return 0;
}

样本输出:

./fe
SOME LINE: 123  FC:: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod 
SASA            FC:: tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
                FC:: quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo 
                FC:: consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse 
Woot 33 !       FC:: cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non 
@52 :::         FC:: proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
 * ERR DBG PRNTF FGETS, --: Bad file descriptor
@54 :::         FC:: 

【讨论】:

  • 哇,谢谢,我确实学到了一点。我得到的输出与您略有不同,但这可能无关紧要。特别是因为它不适用于 fgets。对我来说,输出的最后 3 行如下所示:@62 ::: FC:: proident, sunt in culpa qui officia deserunt mollit anim id est laborum.* ERR DBG PRNTF FGETS, --: Bad file number@64 ::: FC::
  • @craq: __LINE__ , __FILE__ 等是 your 文件中的预处理器宏和 yield linenumber 等。所以在 my 文件中,fclose(fh) 之前的“dbg_fprnt(fh, "@%d :::", __LINE__);”在第 52 行,在 your 文件中它在第 62 行。
【解决方案2】:

根据您的描述,可能的原因可能是第二个fgets 不成功,所以它返回NULL,导致段错误。那么为什么第二个fgets 失败了?你可以看看...

参考fgets规范:

成功时,函数返回相同的 str 参数。 如果遇到 End-of-File 并且没有读取任何字符,则 str 的内容保持不变并返回一个空指针。 如果发生错误,则返回空指针。 使用 ferror 或 feof 检查是否发生错误或到达文件结尾。 http://www.cplusplus.com/reference/clibrary/cstdio/fgets/

所以一般来说直接使用fgets的返回值并不是一个好习惯。

【讨论】:

  • 感谢您的提示,我现在将 fgets 和 printf 调用放在不同的行上,而且 fgets 函数似乎根本没有返回。 (在它甚至给出 NULL 输出之前的段错误。)
  • @craq - 确保在调用 fgets 之前没有无意中关闭或覆盖文件句柄 f
  • @John - 故意关闭它,覆盖它并再次打开它应该没问题,对吧?
【解决方案3】:

从您的打印输出中:

f from eval_args: 4609600, filename /correct/path/to/file
trying to read from file...
 100

...succeeded

f prior to entering density profile: 4609600, filename /correct/path/to/file
trying to read from file...
Segmentation fault (core dumped)

您有“输入密度配置文件之前的 f”而不是“来自 eval_args 的 f”,但是您提供的源中不存在“输入密度配置文件之前的 f”,我认为您没有运行相同的代码。

对于注释“//...处理错误...//(通常只是调用 abort() 或返回 -1”)没有代码可以处理它,所以它仍然可以关闭和 coredump。

来自 fgets 的返回描述:

成功完成后,fgets() 将返回 s。如果流处于文件结尾,则应设置流的文件结束指示符,并且 fgets() 应返回空指针。如果发生读取错误,则应设置流的错误指示符,fgets() 应返回空指针,并应设置 errno 以指示错误。

因此,EOF 或任何不好的东西都会使 printf 核心转储。

我的建议是:

    FILE *f = fopen(filename,"r+");
    if (f) {
        char line[1000];
        printf("f from eval_args: %p, filename %s\n",f,filename);
        printf("trying to read from file...\n");
        while(fgets(line, sizeof (line), f)) printf("%s",line);
        fclose(f);
    }
    else printf("Error opening file\n");

【讨论】:

  • 是的,我更改了字符串输出,以便可以轻松判断输出的来源。是的,不处理错误是懒惰的,但如果是这种情况,我至少会因为 printf 语句而知道。重申一下,是 fgets 调用,而不是产生段错误的 printf。我现在有 char *dummy; dummy=fgets(line, sizeof (line)-1, f); printf("fgets has returned\n"); ... 并且在打印该行之前发生了段错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-19
  • 2016-01-15
  • 1970-01-01
  • 2013-01-03
  • 1970-01-01
相关资源
最近更新 更多