【问题标题】:a switch jumping in unexpected place一个开关跳到意想不到的地方
【发布时间】:2013-09-13 13:04:02
【问题描述】:

我已经分叉了一个 C++ 项目并在 switch 中添加了一些案例。然后它跳到下一个案例的中间。条件为int,案例值为#defined。由于代码比较大,我这里写一个等价的简单的东西:

#define AAA 1
#define BBB 2
int X::func(int i) {
  bool b = (i == 5);
  int v = (b ? 10 : 5);
  switch (i) {
    case AAA:
      if (b && v > 4) {
        v = v + 1;
        return 0;
      }
      break;
    case BBB:
      if (!b || v > 40) {
        v = v + 2;
        b = false; // direct jump here from the switch for i = AAA
        return 0;
      }
      break;
  }
return -1;
}

如果我将 AAA 传递给 X::func,代码执行会直接从 BBB 案例中的 switch 跳转到 b = false;。在真实的代码中,我有几十个案例,而且它发生在中间。如果我完全评论case BBB,它会跳到 cmets 的同一个地方!每个案例都以break; 结尾,并且内部没有变量声明。就像这里的变量 b 和 v 一样,它们是在 switch 语句之前声明的。

部分案例位于#ifdef #endif 块中。出现问题的其他情况在此块中。条件为真,因此编译该块。反正我把这两条预编译语句都打了之后测试了一下,还是一样的。

如果我添加括号{} 以完全包含这里的情况,我仍然有相同的结果:

case BBB: {
  if (!b || v > 40) {
    v = v + 2;
    b = false; // direct jump here from the switch for i = AAA
    return 0;
  }
  break;
}

无论是在调试会话中还是在正常执行中,一切都可以正常编译并执行相同的操作。我在 Eclipse CDT Indigo 下使用 gcc 4.5.4。

它看起来像一个编译器错误,但可能是什么原因?

【问题讨论】:

  • 这个“等效”代码是否也有同样的问题?
  • 您是否有机会在启用优化的情况下进行调试?
  • 它跳转到 cmets 中的同一个位置的事实告诉我,您实际上并没有调试您的“最新版本”。请注意,优化的发布代码可以在没有任何方法的情况下疯狂地跳来跳去(是的,我知道这不是真的,但你们编译器编写者有一些非常有趣的想法,比如模块间优化 )。很少,很少,不再存在编译器错误 - 这并不是说它们没有,而是 1000 个中有 999 个,“编译器错误”代码实际上正在按照您的要求执行(我很惊讶它是如何做到的) !) :-D
  • 让您的案例和条件更简单。您的“等效”代码看起来很乱。如果“部分案例在#ifdef #endif 块中。”,这只会使事情变得更糟。
  • 等待对 doctorlove 评论的回复...如果“等效”代码没有同样的问题,那也无济于事。您可能没有看到派生代码中不存在的某些因素(实际上这是最可能的情况)。没有看到这个因素,我们就无法知道出了什么问题。提供的任何代码绝对应该重现该问题。

标签: c++ gcc compiler-construction switch-statement case


【解决方案1】:

如果您通过调试器单步执行该函数,则调试器中的“当前突出显示的行”不一定对应于当前正在执行的行(涉及循环时、打开优化时、有大量宏时等)。

切换后将变量打印到stdout/stderr,看看它们的值是否正确。还将一些调试行打印到 cases 中的 stdout/stderr。如果你得到的输出是正确的,不要担心奇怪的代码执行顺序。

例子:

#define AAA 1
#define BBB 2
int X::func(int i) {
  bool b = (i == 5);
  int v = (b ? 10 : 5);
  std::cout << "before switch/case" << std::endl;
  switch (i) {
    case AAA:
      std::cout << "AAA, before if block" << std::endl;
      if (b && v > 4) {
        v = v + 1;
        std::cout << "within AAA if block" << std::endl;
        return 0;
      }
      std::cout << "AAA, after if block" << std::endl;
      break;
    case BBB:
      std::cout << "BBB, before if block" << std::endl;
      if (!b || v > 40) {
        v = v + 2;
        b = false; // direct jump here from the switch for i = AAA
        std::cout << "BBB, within if block" << std::endl;
        return 0;
      }
      std::cout << "BBB, after if block" << std::endl;
      break;
  }
  std::cout << "after switch/case" << std::endl;
  return -1;
}

--编辑--

如果为 case 语句添加 {} 括号可能会有所帮助:

case AAA:{
    break;
}

如果您将案例中的内容转移到单独的函数中,这肯定会有所帮助。这不会改进代码,但调试器肯定能够遍历它们。

【讨论】:

  • 这是一个半合理的解决方法,但不是解决方案。调试器搞砸了这一事实表明,要么 OP 没有使用他认为正在编译的任何标志进行编译,要么他没有调试最新版本。这两个潜在的问题都可能给他带来很多你的调试语句无法解决的悲伤。此外,使用调试语句是一种非常原始且受限制的调试形式,在一般情况下使用完整的调试器会更好。
  • @SchighSchagh:IMO,调试器在流控制上搞砸是很常见的事情(即使是在调试版本中),你可能无法修复它。我很久以前就放弃了从 Q_FOREACH 获得正确的行为。
  • 当我稍微改变一下 printf 时,输出就会改变。所以我得出结论,我正在调试我期望调试的内容。 printf 很好。但是调试器不能这样使用。可能是由于自动工具吗?我将尝试使用带有源代码预拷贝的经典版本建立一个影子项目。
  • @lalebarde:这可能是因为您没有生成足够的调试信息,或者当您认为所有优化都被禁用时,您实际上正在优化代码,等等。我会推荐 CMake 或 qmake(qmake 是 qt 的一部分),而不是 autotools。这些工具更容易处理。 IMO,除非您绝对必须使用自动工具,否则您应该避免使用它。
  • CFLAGS = -g3 -O0 CPP = gcc -std=gnu99 -E CPPFLAGS = -pedantic -Werror -Wall -D_GNU_SOURCE -D_REENTRANT -D_THREAD_SAFE -g3 -O0 -DZMQ_FORCE_EPOLL CXX = g++ CXXCPP = g++ - E CXXDEPMODE = depmode=gcc3 CXXFLAGS = -g3 -O0 我的 Makefile 标志看起来正确。所以我放弃了自动工具。我现在正在尝试的是构建一种镜像项目,仅使用来自原始项目的 rsync 源文件,并让 Eclipse CDT 默认构建工具工作。
【解决方案2】:

我已经从原始项目(项目)构建了一个“克隆”项目(project_test)。在预构建步骤中,我有一个复制所有源文件的 rsync(重点是不要复制 autotools 的东西):

rsync -a --exclude=tests/* --exclude=perf/* --exclude=tools/*  --include=*/ --include=*.[ch] --include=*.[ch]pp --exclude=* ${workspace_loc:/project}/ ${workspace_loc:/project_test}/ && cp ${workspace_loc:/project}/tests/one_with_main.cpp ${workspace_loc:/project_test}/tests/

构建良好,调试符合预期。

替代方案可以是rsync -a --delete,而不是rsync -a

【讨论】:

    猜你喜欢
    • 2016-06-24
    • 2010-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-02
    • 2010-10-20
    • 2013-12-27
    • 1970-01-01
    相关资源
    最近更新 更多