3

正如许多年轻的程序员所做的那样,我学会了在代码的不同点插入大量“here1”、“here2”等打印到控制台的语句来找出我的程序何时出错的有用性。在我的 CS 学习过程中,这种蛮力调试技术为我节省了很多很多次。然而,当我开始用 C 编程时,我偶然发现了一个有趣的问题。如果我试着跑

void* test;

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

当然,我得到一个段错误,因为我没有为 testChar 分配内存。但是,从逻辑上讲,您会认为“hello world”会在段错误发生之前打印,因为这是代码的流程,但根据我的经验,总是首先发生段错误,并且“hello world” " 根本不会打印到控制台。(我无法测试这个确切的例子,但我在linux机器上使用gcc多次遇到这种情况。)我猜这与编译器重新排列一些东西和/或printf有关使用某种异步刷新的缓冲区,因此不是立即的。这完全是我的猜测,因为我真的不知道为什么会发生。在我使用过的任何其他语言中,无论出现什么问题,“testChar =...

我的问题是为什么在我编写 C 语言时会发生这种情况?为什么不先打印hello world?其次,是否有比这更好的 C 编程调试技术来完成相同的基本任务?例如,一种简单/直观的方法来查找有问题的代码行?

编辑:我偶然给出了一个工作示例哈哈。我现在拥有的应该会导致段错误。有趣的是,当我想要一个段错误时,我通常会得到一个,而现在当我真正想要一个时,我会编写合法代码!

4

7 回答 7

10

您发布的代码是完全合法的,不应导致段错误 - 无需 malloc 任何内容。您的问题必须在其他地方 - 请发布导致问题的最小代码示例。

编辑:您现在已经编辑了代码以具有完全不同的含义。尽管如此,没有显示“hello world”的原因是输出缓冲区没有被刷新。尝试添加

fflush( stdout );

在 printf 之后。

关于定位问题的根源,您有两种选择:

  • __FILE__使用和__LINE__C 宏在您的代码中自由地使用 printfs
  • 学习使用你的调试器——如果你的平台支持核心转储,你可以使用核心映像来查找错误所在。
于 2009-06-10T13:09:54.657 回答
5

printf写入缓冲的标准输出。有时该缓冲区在程序崩溃之前不会被刷新,因此您永远看不到输出。避免这种情况的两种方法:

  1. 使用fprintf( stderr, "error string" );因为 stderr 没有缓冲。
  2. fflush( stdout );在 printf 调用之后添加调用。

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

于 2009-06-10T13:12:47.803 回答
5

“例如,一种简单/直观的方法来查找有问题的代码行?”

使用 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) 
于 2009-06-10T13:14:04.493 回答
3

默认情况下输出缓冲,段错误发生在输出实际写入标准输出之前。尝试:

fprintf(stderr, "hello, world\n");

(默认情况下,stderr 是无缓冲的。)

于 2009-06-10T13:14:01.903 回答
1

此代码不应出现段错误。您只是将指向文字字符串的指针分配给指针变量。strcpy如果您使用无效指针复制内容,情况会有所不同。

该消息未出现可能是由于缓冲 I/O。打印换行符\n或调用fflush以刷新输出缓冲区。

于 2009-06-10T13:11:50.427 回答
0

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

然后通常是缓冲区的问题,一个在 C 运行时库中,一个在操作系统本身中。你需要冲洗它们。

最简单的方法是(在 UNIX 中,不完全确定fsync在 Linux 中,但您应该保证这最终会发生,除非系统本身出现故障):

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

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

于 2009-06-10T13:12:47.333 回答
0
void* test;

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

系统可能会在某处缓冲“hello world”,并且不会立即打印到屏幕上。它存储的等待机会让任何进程/线程/负责屏幕编写的任何东西都有机会处理它。在它等待(并可能缓冲其他数据以输出)的同时,您的功能正在完成。它涉及非法访问和段错误。

于 2009-06-10T13:17:00.307 回答