为什么GCC被欺骗以允许未定义的行为,只是通过把它放在一个循环? [英] Why is GCC tricked into allowing undefined behavior simply by putting it in a loop?
问题描述
以下是无意义的,但用 g ++ -Wall -Wextra -Werror -Winit-self
(我测试了GCC 4.7.2和4.9.0) p>
The following is nonsensical yet compiles cleanly with g++ -Wall -Wextra -Werror -Winit-self
(I tested GCC 4.7.2 and 4.9.0):
#include <iostream>
#include <string>
int main()
{
for (int ii = 0; ii < 1; ++ii)
{
const std::string& str = str; // !!
std::cout << str << std::endl;
}
}
标记为!
导致未定义的行为,但不是由GCC诊断。但是,注释为
行使得GCC抱怨:
The line marked !!
results in undefined behavior, yet is not diagnosed by GCC. However, commenting out the for
line makes GCC complain:
error: ‘str’ is used uninitialized in this function [-Werror=uninitialized]
我想知道:为什么GCC这么容易被愚弄?当代码不在一个循环中时,GCC知道它是错误的。但是把同样的代码放在一个简单的循环中,GCC不再理解了。这让我感到困扰,因为我们依靠相当多的编译器通知我们当我们在C ++中犯了愚蠢的错误,但它失败了一个看似琐碎的情况。
I would like to know: why is GCC so easily fooled here? When the code is not in a loop, GCC knows that it is wrong. But put the same code in a simple loop and GCC doesn't understand anymore. This bothers me because we rely quite a lot on the compiler to notify us when we make silly mistakes in C++, yet it fails for a seemingly trivial case.
奖金琐事:
- 如果将
std :: string
更改为int c> 和启用优化,GCC将诊断错误,即使是循环。
- 如果您构建破碎的代码与
-O3
,GCC从字面上调用带有空指针的ostream插入函数。
- If you change
std::string
toint
and turn on optimization, GCC will diagnose the error even with the loop. - If you build the broken code with
-O3
, GCC literally calls the ostream insert function with a null pointer for the string argument. If you thought you were safe from null references if you didn't do any unsafe casting, think again.
我提交了一个GCC申请表,如果你没有做任何不安全的投放,错误: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63203 - 我仍然想要更好地了解哪里出错了,以及它可能如何影响类似诊断的可靠性。
I have filed a GCC bug for this: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63203 - I'd still like to get a better understanding here of what went wrong and how it may impact the reliability of similar diagnostics.
推荐答案
我仍然想更好地了解出错了什么以及它可能如何影响类似诊断的可靠性。
I'd still like to get a better understanding here of what went wrong and how it may impact the reliability of similar diagnostics.
与Clang不同,GCC没有检测自初始化引用的逻辑,因此,在这里获取警告依赖于检测未初始化变量的使用的代码,这是相当温和和不可靠的请参阅更好的未初始化警告进行讨论)。
Unlike Clang, GCC doesn't have logic to detect self-initialized references, so getting a warning here relies on the code for detecting use of uninitialized variables, which is quite temperamental and unreliable (see Better Uninitialized Warnings for discussion).
使用 int
编译器可以知道,您将一个未初始化的 int
写入流, code> std :: string 在 std :: string
类型的表达式之间显然有太多的抽象层, code> const char * 它包含,并且GCC未能检测到该问题。
With an int
the compiler can figure out that you write an uninitialized int
to the stream, but with a std::string
there are apparently too many layers of abstraction between an expression of type std::string
and getting the const char*
it contains, and GCC fails to detect the problem.
GCC确实给出了一个更简单的例子的警告,在声明和使用变量之间的代码较少,只要你启用一些优化:
e.g. GCC does give a warning for a simpler example with less code between the declaration and use of the variable, as long as you enable some optimization:
extern "C" int printf(const char*, ...);
struct string {
string() : data(99) { }
int data;
void print() const { printf("%d\n", data); }
};
int main()
{
for (int ii = 0; ii < 1; ++ii)
{
const string& str = str; // !!
str.print();
}
}
d.cc: In function ‘int main()’:
d.cc:6:43: warning: ‘str’ is used uninitialized in this function [-Wuninitialized]
void print() const { printf("%d\n", data); }
^
d.cc:13:19: note: ‘str’ was declared here
const string& str = str; // !!
^
我怀疑这种缺失的诊断只会影响一小部分诊断这依赖于启发式来检测问题。这些是会给出警告形式可能未初始化或可能违反严格别名规则的警告,可能是数组下标在数组上边界警告。这些警告不是100%准确,复杂的逻辑,如循环(!)可能导致编译器放弃尝试分析代码,并无法给出诊断。
I suspect this kind of missing diagnostic is only likely to affect a handful of diagnostics which rely on heuristics to detect problems. These would be the ones that give a warning of the form "may be used uninitialized" or "may violate strict aliasing rules", and probably the "array subscript is above array bounds" warning. Those warnings are not 100% accurate and "complicated" logic like loops(!) can cause the compiler to give up trying to analyse the code and fail to give a diagnostic.
IMHO的解决方案是在初始化时添加对自初始化引用的检查,而不是依赖于检测它在以后被使用时未初始化。
IMHO the solution would be to add checking for self-initialized references at the point of initialization, and not rely on detecting it is uninitialized later when it gets used.
这篇关于为什么GCC被欺骗以允许未定义的行为,只是通过把它放在一个循环?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!