2012-03-29 45 views
0

我有以下代码打开了一个输入和输出文件:奇怪ç行为具有的fopen和测试NULL

if ((source_file_ptr = fopen(source_filename, "rb")) == NULL) { 
    error("unable to open input file"); 
} 
if ((output_file_ptr = fopen(output_filename, "wb")) == NULL) { 
    error("unable to open output file"); 
} 

这是goood简单的办法赶上在打开的文件中的错误。

但是,在对我的程序进行一些编辑后,程序现在崩溃而不是捕获无效的输出文件。

我试了几件事情没有成功,但有趣的是,当我试试这个:

if ((output_file_ptr = fopen(output_filename, "wb")) == NULL) printf("fail"); 
exit(0); 
if ((source_file_ptr = fopen(source_filename, "rb")) == NULL) { 
    error("unable to open input file"); 
} 
if ((output_file_ptr = fopen(output_filename, "wb")) == NULL) { 
    error("unable to open output file"); 
} 

这将打印“失败”(显然退出而不更加深入了解)。

但是,如果我将exit(0)行注释掉,它将再次显示相同的崩溃行为,而不打印“失败”,也不会捕获错误。

我无法解释为什么这是...我怀疑是一个悬挂指针,但函数内的唯一前面的代码行已经包含在if-else if-else all with括号中。上面只有其他几个函数,但我已经检查并确保它们都被括在括号内。

我还在学习C,关于这里发生了什么的任何想法?
非常感谢!

注意:用括号括住printf(“fail”)不会改变我观察到的行为。

编辑:额外的代码如下上面:

if (fread(&file_struct, sizeof(file_struct), 1, source_file_ptr) < 1) { 
    error("unable to read %s", source_filename); 
} 
else { 
    error_check(file_struct); 

    if (fwrite(&file_struct, sizeof(file_struct), 1, output_file_ptr) < 1) { 
    error("unable to write file header"); 
    } 
} 
+0

你可以在这些if后面发布后续代码吗?如果遇到故障,这些'if's将打印消息,但文件指针仍可能被使用。 – hmjd 2012-03-29 11:11:49

+0

使用'perror()'来描述错误。 – pmg 2012-03-29 11:14:48

+0

您可能需要在printf()和exit()周围使用一些{}。 – wildplasser 2012-03-29 11:15:11

回答

2

我检查的第一件事是,source_filename是一个有效的C字符串。然后我会以同样的方式检查output_filename

如果发现您的更改以某种方式损坏了其中一个或两个,您将进入未定义的区域行为。

调试器的使用将是理想的,因为您可以在该代码的开始处设置一个断点,然后单步执行,检查即将使用的变量。

你应该检查的另一件事是你的电话error实际返回(而不是,例如,呼叫exit)。

如果他们不那么,即使fopen调用返回NULL,你还是会通过,以fread和/或fwrite,一个明确的禁忌。

由于你的第二个案例打印"fail",这是一个给定的问题,你打开输出文件时有一些问题。从那里使用NULL句柄不是一件好事。

如果功能确实回报,那么你就应该使用类似:

if ((source_file_ptr = fopen(source_filename, "rb")) == NULL) { 
    error("unable to open input file"); 
    return; 
} 
if ((output_file_ptr = fopen(output_filename, "wb")) == NULL) { 
    fclose (source_file_ptr); 
    error("unable to open output file"); 
    return; 
} 
+0

我可以验证我没有设置output_filename(指针)指向任何内容,并且output_filename == NULL返回false。如果我不提供source_filename,它将返回错误消息。它只是不会做输出。即使我切换if语句的顺序。 – xlm 2012-03-29 11:29:54

+0

道歉我应该补充说,错误打印消息,也退出(-1) – xlm 2012-03-29 11:32:55

+0

@xlm,那么你需要或者_print_值来确认他们的内容,包括之后刷新,以确保你不会因为丢失核心转储)或通过调试器运行它。严重的是,如果可能的话,做后者。它应该告诉你_完全问题是什么,而不是我们试图猜测(不管这些猜测是否合理)。 – paxdiablo 2012-03-29 11:40:54

2

“故障”不会,必须,显示如果你有缓冲输出 - 因为它崩溃,也不要退出并刷新输出缓冲区。

如果你

则setbuf(标准输出,NULL);

我猜你会得到fail印,也没有exit

正如@caf下文提到的,还要检查是否fflush()更适合:

stdio.h


编辑:
这可能是有点混乱,但是,既然你确认的Linux + gcc我会添加它,一个非常短的gdb介绍,并在??%最佳攻击方式:

  1. -ggdb添加到您的编译行3.9 Options for Debugging Your Program or GCC。如果您使用的参数

    • $ gdb -args my_prog arg1 arg2
    • 否则:$ gdb my_prog
    • 参数也GDB内部提供

  2. 启动GDB会话断点:

    • (gdb) break 112,< - 其中112是行号,或
    • (gdb) break main,< - 其中主要是功能
    • (gdb) break foo.c:bar,< - 在foo.c是文件,bar是功能
    • 也有不少其他方法

  3. 开始执行:(gdb) run

  4. 当程序遇到断点停止,从这里可以即:

    • (gdb) print output_filename,< - 文件名
    • (gdb) print *source_file_ptr,<的打印值 - 打印什么这点到
    • (gdb) list,单 - ,列表行号等< - 打印源
    • 和广阔其他物品清单

  5. 要继续,您可以这样做:例如:
    • (gdb) next,< - 通过子例程调用进行步骤。
    • (gdb) step,< - 步骤直至不同的源极线,(即,在给一个函数)
    • (gdb) continue,< - 继续execuition,直到下一个断点,程序或其它的结束。
    • 如此反复

  6. 要退出:(gdb) quit

最常使用gdb的指令有一个字母的缩写。即r对于run,b对于break,n对于next等。它也具有对已知变量的标签填充。

很多命令还需要数字或其他参数。即next 8

使用手册,手册页等,在运行时也可以发出帮助命令。即(gdb) help run

+0

而不是完全删除缓冲区,您可以在'printf'调用刷新缓冲区后简单地将一个调用添加到'fflush(stdout);'。 – caf 2012-03-29 11:38:27

+0

@caf确实。想到这一点,但认为在调试的情况下,关闭它比较简单,例如,如果有多个测试printf的话。但我应该提到它。懒惰的我。 – Morpfh 2012-03-29 11:59:04

+0

+1感谢gdb的简介!刚刚下载;正在寻找一个介绍 – xlm 2012-03-29 13:21:38