【问题标题】:Weird: printf() affects variable value奇怪: printf() 影响变量值
【发布时间】:2013-04-30 14:05:42
【问题描述】:

这是一段代码:

int somefunc() {
    /* ... */
    while ((pos = KMP_index(array, size, pattern, plen)) > -1) {
        count++;
    }
    return count;
}

somefunc()在多个子进程中被调用,每个fork调用一次。

我的代码在 Linux x86_64 和 i386 上按预期编译和工作。但是当我在 Atom 上网本(Arch Linux i686)上运行它时,count 变量永远不会超过 2!

while (...) {
    count++;  //succesfully increments 
}
return count; //it's maximum 2!

但是,如果我添加 printf() :

while (...) {
    count++;  //succesfully increments
    printf("%d", anything);
}
return count; //value as expected

打印空字符或fflushing 标准输出在这里不起作用。我必须打印至少一个字符,然后变量才可以。它让我发疯。

有人可以告诉我,为什么我还要使用这种“解决方法”? 这可能是我的linux环境的问题吗? (没什么特别的,GCC 4.8,股票内核) 谢谢。

P.S 整个来源在这里 http://pastebin.com/4eEHMbKn 。 是的,这是一个家庭作业 :) 我需要创建一个类似 grep 的实用程序,在单独的进程中处理每个文件。

【问题讨论】:

  • 闻起来像未定义的行为。无法从可见代码中分辨出哪里。
  • 可能您的代码的某些部分正在影响函数的操作,而 printf 会更改执行时间,从而影响结果。它可以无线程工作吗?您在函数中访问了哪些非本地变量?一个线程更改某个此类 var 对任何其他线程有什么影响?
  • 您可能需要将其标记为volatile 以避免编译器优化快捷方式。一般来说,在多个线程或进程中弄乱一个变量并不是一个好主意。如果您没有明确控制来自多个线程的同时访问,我不确定该行为是否已定义。
  • @ArtSwri,它确实可以无线程工作。唯一的非本地变量是参数:int badgrep(const char *file, int size, const char *pattern, int plen)。有问题的 var 是在函数内创建和返回的,所以它完全是本地的(我认为?)。我还应该把它标记为易失性吗,@JimStewart?
  • 如果您使用线程,或单独的进程但共享内存,您可以使用原子内置函数:__sync_fetch_and_add(&counter, 1) 递增计数器,__sync_fetch_and_add(&counter, 0) 读取它。但是,Mike Roll,我不认为是这样。非空 printf() 需要实际运行时间,因此很可能在 KMP_index() 函数中存在竞争条件,导致循环仅在原子上执行两次。您可以通过在正文中添加 sched_yield() 或 usleep(1) 调用来验证这一点(而不是 printf() 来改变时间):您应该得到相同的效果。

标签: c linux


【解决方案1】:

您的KMP_index() 调用KMP_failure() 来初始化failure[] 数组。不幸的是,KMP_failure() 未能初始化数组的第一个元素。

KMP_index() 确实检查了failure[] 的第一个元素。由于该值未设置,KMP_index() 的结果是半随机的,具体取决于先前的函数调用如何使用堆栈(局部变量)。这就解释了为什么该函数的结果取决于您在 KMP_index() 调用之前调用的内容。

(我确实注意到您的代码在 GCC 4.6.3 上编译时没有任何警告,因此编译器也没有注意到。)

您的成功测试很可能是在 64 位平台上进行的,在该平台上,与 32 位 Atom 架构相比,该平台的第一个元素会碰巧放置一些不同的东西。特别是,printf() 改变了这种情况,因为它使用了相当多的堆栈:KMP_index() 的下一次调用将获得一个 failure[] 数组,其中初始元素来自调用 printf() 留下的垃圾。

事实上,使用-m32 和-m64 以及不同的优化设置(-Os、-O3)编译会在failure[] 的第一个元素中产生不同的半随机值。

希望这会有所帮助。

【讨论】:

  • 正如经常发生的那样,问题是一小块未初始化的内存破坏了所有的乐趣。在调用KMP_failure() 之前设置它failure[0] = 0 就可以了。非常感谢,根本不容易发现。
猜你喜欢
  • 1970-01-01
  • 2012-02-12
  • 1970-01-01
  • 2011-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多