这是一个简单的测试用例,无需任何警告即可编译.看起来像一个常见的错误,但clang,gcc和visual studio在这种情况下不会发出警告.为什么?
class Image { private: int width, height; int* array; public: Image(int _width, int _height); void crashTest(); }; Image::Image(int _width, int _height) { array = new int[width * height]; // ^^^^^ ^^^^^^ this is wrong // I expect a warning here e.g.: 'width is uninitialized here' width = _width; height = _height; } void Image::crashTest() { for (int x = 0; x < width; ++x) { for (int y = 0; y < height; ++y) array[x + y * width] = 0; } } int main() { const int ARRAY_SIZE = 1000; Image image(ARRAY_SIZE, ARRAY_SIZE); image.crashTest(); return 0; }
例如:
g++ -Wall -Wextra -O2 -Wuninitialized test.cpp clang++ -Wall -Wextra -O2 -Wuninitialized test.cpp
没有给我任何警告
这个老问题的后续解决方案:通过使用启用来自“ Effective C ++”的警告,您可以使用g ++获得警告-Weffc++
。这将抱怨没有明确初始化的成员变量。
这可能太激进了,因为它还会抱怨带有未明确初始化的默认构造函数的类成员。
我还没有看到Clang的等效选项-我同意关于未初始化原始数据成员的类的警告将非常有用。
正如评论中指出的那样,从未初始化的变量中读取是未定义的行为.编制者没有义务为此提供警告.
(事实上,只要您的程序表达未定义的行为,编译器就会有效地从任何和所有义务中释放......)
从 标准的[defns.undefined]部分(强调添加):
未定义的行为
本国际标准没有要求的行为
[注意:当本国际标准忽略任何明确的行为定义或程序使用错误的构造或错误数据时,可能会出现未定义的行为.允许的未定义行为包括完全忽略不可预测的结果,在翻译或程序执行期间以环境特征(有或没有发出诊断消息)的特定行为,终止翻译或执行(发布时)一条诊断信息).许多错误的程序结构不会产生未定义的行为; 他们需要被诊断出来. - 尾注]
对于编译器来说,这可能是一个困难的情况(如果它确实检测到它,则很难以某种有用的方式通知用户).
因为它试图从初始化读取你的代码只呈现出不确定的行为的成员变量 width
和height
.它们是成员变量的事实是使这种情况难以诊断的事情之一.
对于局部变量,检测到这一点的静态分析可能相对简单(不是所有时间,请注意).
例如,在这里很容易看到问题:
int foo() { int a; int b = 0; return a + b; // Danger! `a` hasn't been initialized! }
在这种情况下怎么样:
int foo(int& a) { int b = 1; return a + b; // Hmm... I sure hope whoever gave me `a` remembered to initialize it first } void bar() { int value; int result = foo(value); // Hmm... not sure if it matters that value hasn't been initialized yet }
一旦我们开始处理范围超出单个块的变量,就很难检测变量是否已经初始化.
现在,有关这回手头的问题(你的问题):变量width
和height
是不是本地的构造- 他们可以在构造函数外已被初始化.
例如:
Image::Image(int _width, int _height) { Initialize(); array = new int[width * height]; // Maybe these were initialized in `Initialize`... width = _width; height = _height; } Image::Initialize() { width = 0; height = 0; }
编译器是否应在此方案中发出警告?
经过一些粗略的分析,我们可以最终说"不,它不应该警告",因为我们可以看到该Initialize
方法确实初始化了有问题的成员变量.
但是,如果Initialize
将此委托给另一种方法MoreInitialize()
呢?那个方法将它委托给另一个方法YetEvenMoreInitialize
?这开始看起来像我们无法合理期望编译器解决的问题.