2009-06-10 70 views
3

许多年轻程序员都这样做,我学会了在代码中的不同点插入大量“here1”,“here2”等打印到控制台语句的用处弄清楚我的程序何时会出错。这种强力调试技术为我的CS研究节省了很多次。但是,当我开始用C编程时,我偶然发现了一个有趣的问题。如果我试着运行C编程:seg故障,printf和相关的怪癖

void* test; 

printf("hello world"); 
test[5] = 234; 

当然,我得到了一个段错误,用于没有为malloc测试testChar的内存。然而,从逻辑上来说,在seg故障发生之前会打印出“hello world”,因为这是代码的流向,但根据我的经验,seg故障总是首先发生,而“hello world “永远不会被打印到控制台上。 (我无法测试这个确切的例子,但是我已经在Linux机器上多次使用gcc遇到过这种情况)。我猜这与编译器重新安排一些事情和/或printf使用某种不同步刷新的缓冲区,因此不会立即生效。这完全是我的猜测,因为我真的不知道为什么会发生。在我使用的任何其他语言中,无论“testChar = ...”行产生什么问题,“hello world”都会被打印出来,因此我可以确定问题所在。

我的问题是为什么当我编程C时会发生这种情况?为什么不是先打印出你好的世界?其次,是否有比此更好的C编程调试技术来实现相同的基本功能?在中,一种简单/直观的方式来查找是问题的代码行?

编辑:我偶然发现了一个实例哈哈。我现在拥有的应该会导致段错误。这很有趣,通常当我不要想要一个segfault我得到一个,现在当我真的想要一个我写法律代码!

回答

9

您发布的代码是完全合法的,不应该导致段错误 - 不需要malloc任何东西。您的问题必须在别的地方 - 请发布导致问题的最小代码示例。

编辑:您现在编辑的代码具有完全不同的含义。但是,“hello world”没有显示的原因是输出缓冲区没有被刷新。尝试addinig

fflush(stdout); 

printf后。

关于定位问题的根源,你有两个选择:

  • 从优通过您的代码使用__FILE____LINE__ C宏
  • 学习使用调试器洒printfs输出 - 如果你的平台支持核心转储,您可以使用核心映像找到错误的位置。
+0

我曾经这么想过。*尽管我正在精神上,但会划伤头部。 – 2009-06-10 13:10:59

+0

对不起,大声笑,我解决了我的代码,这是一个真正的问题。我需要任何会导致段错误的东西。 – JoeCool 2009-06-10 13:11:34

+0

“哦,这是坏的废话”将被存储在符号区(?)中,并且指针被存储在testChar中...您可以使用调试器查看它。 – stefanB 2009-06-10 13:12:06

1

此代码不应该段错误。你只是将一个指向字符串的指针赋给一个指针变量。如果你是例如,情况会有所不同使用strcpy复制带有无效指针的东西。

未显示的消息可能是由于缓冲的I/O。打印换行符\n或致电fflush刷新输出缓冲区。

0

你有两个问题。首先是你的(原始)代码不会段错误。将该字符串常量分配给char指针是完全有效的。但现在让我们暂时搁置一下,假装你在那里放了段错误。

然后它通常是一个缓冲区,一个在C运行时库和一个在操作系统本身。你需要刷新它们。

要做到这一点是(在UNIX中,没有完全确定有关Linux中的fsync,但你要保证,除非系统本身关闭此西港岛线最终会发生),最简单的方法:

printf ("DEBUG point 72\n"); fflush (stdout); fsync (fileno (stdout)); 

我在UNIX中经常这样做,它确保将C运行时库刷新到UNIX(fflush),并将UNIX缓冲区同步到磁盘(fsync),如果stdout不是终端设备,或者您正在为一个不同的文件句柄。

5

printf写入标准输出,它被缓冲。有时候,在你的程序崩溃之前,这个缓冲区不会被刷新,所以你永远不会看到输出。有两种方法可以避免这种情况:

  1. 使用fprintf(stderr, "error string");因为stderr未被缓冲。
  2. 在printf调用后添加对fflush(stdout);的调用。

正如Neil和其他人所说的那样,编写的代码很好。也就是说,直到您开始修改testChar指向的缓冲区。

2

输出在默认情况下被缓冲,segfault发生在输出实际写入stdout之前。尝试:

(错误默认情况下未缓冲。)

3

“作为,一个简单/直观的方式找到的代码行是一个问题吗?”

使用gdb(或任何其他调试器)。

要找到你的程序赛格故障您-g选项编译(包括调试符号)从GDB运行应用程序,它会停在赛格故障。

然后,您可以使用bt命令查看回溯,以查看您在哪一点得到了seg故障。

例如:

> gdb ./x 
(gdb) r 
Starting program: /proj/cpp/arr/x 
Program received signal EXC_BAD_ACCESS, Could not access memory. 
Reason: KERN_PROTECTION_FAILURE at address: 0x00000000 
0x000019a9 in willfail() at main.cpp:22 
22   *a = 3; 
(gdb) bt 
#0 0x000019a9 in willfail() at main.cpp:22 
#1 0x00001e32 in main() at main.cpp:49 
(gdb) 
0
void* test; 

printf("hello world"); 
test[5] = 234; 

其可能的“Hello World”是由系统缓存的地方,并没有立即显示在屏幕上。它的存储等待一个进程/线程/任何负责屏幕写入的机会,都有机会处理它。而它的等待(并可能缓冲其他数据输出),你的功能正在完成。它来自非法访问和段错误。