我想知道在启用任何类型的gcc / g ++优化时,下面的代码是否会引发错误或警告。
int a;
a = func();
if (a == 2) {
assert(false);
}
我认为以下代码可以在发布配置中发出警告“设置但未使用的变量”。
int a;
a = func();
assert(a != 2);
但是上面的代码怎么样? (gcc 可以删除if语句本身,因为在if语句或if-block(在发布版本中)都不会做任何事情,然后抛出警告“未使用但设置变量“)
编辑:这绝对不是减少代码或exe的大小的问题。我想知道在任何构建配置中成功的一段代码。
编辑:我们在发布模式下禁用断言
答案 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
如果if
,else
或do 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)
是的,此代码可能会在将来的某个时间点发出一些奇怪的编译器设置警告。
你的问题永远不会得到肯定的回答。它不可能。未来是不可预测的。即使在目前,编译器标志的数量代表组合爆炸,这可能很难完全分析。任何给你一个'是'答案的人都可能忽略了一些东西。
现在,我会说编译器一般(根据我的经验)只发出代码实际看起来像什么的警告,而不是优化器完成它后的样子。是的,如果您运行优化器,它可能能够进行更深入的分析并找到更微妙的问题。但它不会开始标记你现在多余的构造,因为优化器能够完全删除它。
所以,我认为你在这里大部分都是安全的,就像你要从我那里得到的那样接近'是'。