C中的奇怪错误

时间:2009-08-22 08:31:05

标签: c heisenbug

所以我有一个C程序。而且我认为由于复杂性问题我不能发布任何代码片段。但我会概述我的错误,因为它很奇怪,看看是否有人可以给出任何见解。

我设置了指向NULL的指针。如果在我将指针设置为NULL的同一函数中,我printf()指针(带"%p"),我得到0x0,当我打印相同的指针时在我的计划结束的一百万英里之外,我得到了0x0。如果我删除printf()并使完全没有其他更改,那么当稍后打印指针时,我得到0x1,并且我的结构中的其他随机变量的值不正确好。我在-O2上使用GCC编译它,但如果我取消优化它会有相同的行为,所以这不是问题。

这听起来像Heisenbug,我不知道它为什么会发生,也不知道如何修复它。过去曾经处理过类似问题的人有没有就如何解决这类问题提出建议?我知道这听起来有点模糊。

编辑:不知何故,它现在有效。感谢大家的建议。

调试器告诉我有趣的事情 - 我的变量正在被优化掉。所以我重写了函数,因此它不需要中间变量,现在它可以使用和不使用printf()。我对可能发生的事情有一个模糊的概念,但我需要的睡眠比我需要知道的更多。

5 个答案:

答案 0 :(得分:8)

您使用的是多线程吗?我经常发现打印出来的行为足以有效地抑制竞争条件(即不会删除错误,只是让它更难发现)。

至于如何诊断/修复它...你可以提前和早些时候移动第二个印刷品,直到你能看到它在哪里变化?

当你没有printf时,你总是看到0x1吗?

避免printf延迟/同步的一种方法是将指针值复制到第一个printf位置的另一个变量中,然后打印出该值 - 这样你就可以了看看那时 的值是什么,但是在一个时间要求不高的地方。当然,正如你有一些奇怪的价值“腐败”,这可能不如听起来那么可靠......

编辑:你总是看到0x1这一事实令人鼓舞。它应该更容易追踪。诚然,不是多线程确实会让它稍微难以解释。

我想知道是否与额外的printf调用有关,这会对堆栈的大小产生影响。如果您在与第一个printf调用相同的位置打印不同变量的值,会发生什么?

编辑:好的,让我们进一步了解堆栈的想法。你能创建一个与printf具有相同签名的另一个函数,并且有足够的代码来避免它被内联,但实际上并没有打印任何内容吗?称之为而不是printf,看看会发生什么。我怀疑你还没事。

基本上我怀疑你在某个地方搞乱你的堆栈内存,例如通过在堆栈上写过数组的末尾;通过调用函数来改变堆栈的使用方式可能会掩盖它。

答案 1 :(得分:4)

如果您在支持硬件数据断点的处理器(如x86)上运行,只需在写入指针时设置断点。

答案 2 :(得分:1)

您有可用的调试器吗?如果是这样,那些价值观是什么样的呢?你能在值上设置任何类型的内存/硬件断点吗?也许在其他地方有些东西践踏记忆,而printf会移动足够的东西来移动或隐藏错误?

可能值得看看asm,看看那里有什么明显的错误。此外,如果您还没有,请执行完全干净的重建。如果结构的定义最近发生了变化,那么如果依赖性检查无法正确地重建所需的一切,那么编译器可能会错误地改变它。

答案 3 :(得分:0)

您是否尝试在调试器中设置条件,以便在修改该值时通知您?或者通过Valgrind运行?这些是我要尝试的两件大事,特别是Valgrind,如果你使用的是Linux。找出内存错误没有更好的方法。

答案 4 :(得分:0)

没有代码,它有点难以帮助,但我明白为什么你不想在我们身上浪费大量资金。

这是我的第一个建议:使用调试器并在指针位置设置观察点。

如果那不可能,或者虫子再次消失,这是我的第二个建议。

1 /从错误的代码开始,你打印指针值的那个,你看到的是0x1。

2 /从那里稍微插入另一个printf(就代码执行路径而言)。

3 /如果它仍然是0x1,请返回步骤2,每次向后移动一点执行路径。

4 /如果它是0x0,你知道问题所在。

如果0x0 printf和0x1 printf之间没有任何明显的结果,那么很可能是某种类型的损坏。如果没有观察点,那将很难追踪 - 您需要检查每个堆栈变量,以确保不会出现溢出。

我假设指针是全局的,因为你设置它并在“一百万英里以外”打印它。如果是,请使用您在其两侧定义的变量(在源中)。他们是最有可能导致超支的人。

另一种可能性是关闭优化以查看问题是否仍然存在。我们偶尔不得不在我们无法在截止日期之前修复错误的情况下发送这样的代码(当然,我们将在以后修复它)。