首页 文章

gcc优化

提问于
浏览
3

我想知道下面的代码是否会在启用任何类型的gcc / g优化的编译时抛出错误或警告 .

int a;
a = func();
if (a == 2) {
    assert(false);
}

我认为以下代码可以在发布配置中发出警告“设置但未使用的变量” .

int a;
a = func();
assert(a != 2);

但是上面的代码怎么样? (gcc can 删除if语句本身,因为在if语句或if-block(在发布版本中)都不会执行任何操作,然后抛出警告"unused but set variable")

编辑:这绝对不是减少代码或exe的大小的问题 . 我想知道在任何构建配置中成功的一段代码 .

编辑:我们在发布模式下禁用断言

6 回答

  • 1

    两个代码块都可以,但我会优先:

    int a = func();
    assert(a != 2);
    
  • 0

    是的,此代码可能会在将来的某个时间点发出一些奇怪的编译器设置警告 .

    你的问题永远不会得到肯定的回答 . 它不可能 . 未来是不可预测的 . 即使在目前,编译器标志的数量代表组合爆炸,这可能很难完全分析 . 任何给你一个'是'答案的人都可能忽略了一些东西 .

    现在,我将说一般(根据我的经验)编译器只发出代码实际看起来像什么的警告,而不是优化器完成它后的样子 . 是的,如果您运行优化器,它可能能够进行更深入的分析并找到更微妙的问题 . 但它不会开始标记你现在多余的构造,因为优化器能够完全删除它 .

    所以,我认为你在这里大部分时间都是安全的,这与你从我这里得到的“是”很接近 .

  • 0

    如果预处理器删除了 assert ,这可能会导致问题,如下所示:

    #ifdef ENABLE_ASSERT
    #define assert (CONDITION) {if (!(CONDITION)) abort ();}
    #else
    #define assert (CONDITION) /* Nothing */
    #endif
    

    但是,如果你做得恰当,那么就没有问题:

    #define assert (CONDITION) {if ((ENABLE_ASSERT) && !(CONDITION)) abort ();}
    

    在这种情况下,编译器仍然会在 CONDITION 中看到 a ,但是当 ENABLE_ASSERT 为零时会将其优化掉 . 如果它有一天变成运行时测试,则需要重写它 .

    显然, ENABLE_ASSERT 必须始终定义为零或非零 .

  • 0

    您可能知道 assert 宏是使用宏 NDEBUG 进行管理的 . 我认为使用 #ifdef NDEBUG 的东西会更容易阅读并具有相同的效果 .

  • 3

    根据我的测试,以下代码使用 -Wall -Wextra -O2 -DNDEBUG 生成警告:

    int a = func(); // warning: unused variable ‘a’
    assert(a != 2);
    

    但是以下代码没有:

    // no warnings
    int a;
    a = func();
    assert(a != 2);
    

    但是,您始终可以通过强制转换为 void 来抑制未使用的变量警告 .

    int a = func();
    (void) a; // suppresses "unused variable" warning
    assert(a != 2);
    

    据我所知,行 a = func() 语句总是计为变量 a 的使用,而初始化不算作使用 .

    我不打算在编译器改变及其诊断改进时对冲未来可能的编译器警告,因为对冲有时会无意中抑制有效警告 .

    How is assert defined?

    标准委员会和C实施者已经仔细设计了 assert ,因此它不会产生虚假警告 . 注意 void 的常见演员阵容是......

    • 如果没有 NDEBUG ,glibc大致按以下方式定义 assert (除了 abort 之外的其他内容):
    #define assert(expr) ((expr) ? (void) 0 : abort())
    
    • 使用 NDEBUG ,glibc以这种方式定义它(按照C标准的要求):
    #define assert(expr) ((void) 0)
    
    • assert 的以下定义不符合,因为它不会扩展为表达式:
    #define assert(expr) { if (expr) { ... } } // wrong
    

    C的定义也略有不同 . 所以你看, assert 是以正确的方式定义的,所以它不会创建任何伪造的编译器警告,并且它确实在语法上表现得像函数调用 .

  • 1

    通常不可能说一段代码永远不会从编译器得到任何警告,因为将来可能会添加新警告,或者编译器错误可能会导致虚假警告 .

    我很确定GCC不会警告一个用大括号定义的空 if 主体,正是因为这很容易发生在有效的代码中,比如这个(这与你的情况非常相似):

    int a = func();
    if (a == 2)
    {
    #ifdef SOME_BUILD_SETTING
        launch_missiles();
    #endif
    }
    

    手册显示

    -Wempty-body如果在if,else或do语句中出现空体,则发出警告 . -Wextra也启用此警告 .

    哪个适用于:

    #ifdef SOME_BUILD_OPTION
    # define LAUNCH_MISSILES launch_missiles()
    #else
    # define LAUNCH_MISSILES
    #endif
    if (a==2)
      LAUNCH_MISSILES;
    

    然后使用 -Wextra 编译而不定义 SOME_BUILD_OPTION .

    但是有了大括号,它并没有发出警告,正如迪特里希·埃普所说的那样,即使用 NDEBUG 定义,它也不会扩展到零 .

    在你的代码 a 初始化并使用它的值,所以我会惊讶于它警告 .

相关问题