gcc优化

时间:2012-06-22 07:38:47

标签: c++ c gcc g++

我想知道在启用任何类型的gcc / g ++优化时,下面的代码是否会引发错误或警告。

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

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

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

但是上面的代码怎么样? (gcc 可以删除if语句本身,因为在if语句或if-block(在发布版本中)都不会做任何事情,然后抛出警告“未使用但设置变量“)

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

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

6 个答案:

答案 0 :(得分: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的使用,而初始化计为使用。

我不会尝试对照未来的可能的编译器警告,因为编程器会改变并且他们的诊断会有所改进,因为对冲有时会无意中抑制有效警告。

如何定义断言?

标准委员会和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 :(得分:1)

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

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

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

手册显示

  

-Wempty-body
如果ifelsedo while声明中出现空身,则发出警告。此警告也由 -Wextra 启用。

适用于:

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

然后使用-Wextra进行编译,但未定义SOME_BUILD_OPTION

但是使用大括号它没有警告,正如Dietrich Epp评论的那样assert它没有发出警告,因为即使定义了NDEBUG它也不会扩展为空。

在你的代码中a被初始化并使用了它的值,所以我会惊讶于它发出警告。

答案 2 :(得分:1)

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

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

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

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

在这种情况下,编译器仍会看到a中使用CONDITION,但会在ENABLE_ASSERT为零时对其进行优化。让编译器优化器移除代码比使用预处理器通常更好:它避免了这样的警告,并且通常会导致更易读的代码,并且如果代码有一天变成运行时测试则不需要重写代码

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

答案 3 :(得分:0)

两个代码块都可以,但我更喜欢:

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

答案 4 :(得分:0)

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

答案 5 :(得分:0)

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

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

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

所以,我认为你在这里大部分都是安全的,就像你要从我那里得到的那样接近'是'。