我想问为什么使用未初始化的变量被认为是非类型安全的?
我正在阅读Bjarne的初学者书籍(编程原则和使用C++的实践),在这个网站上的C++书籍指南。
书中有一部分是关于类型安全的,它说:
当对象仅根据其类型的规则使用时,程序(或程序的一部分)是类型安全的。例如,在初始化变量之前使用它不被认为是类型安全的.
然后,本书提供了以下代码作为示例:
int main() {
double x; // we "forgot" to initialize
// the value of x is undefined
double y = x; // the value of y is undefined
double z = 2.0+x; // the meaning of + and the value of z are undefined
}
我理解没有初始化的局部变量将有一个不确定的值,读取这个变量会导致未定义的行为。我不明白的是它是如何与类型安全联系在一起的。我们仍然从变量的定义中知道类型。
为什么上述代码中的注释指出,当2.0和x都是双倍时,+的含义是未定义的,而+的定义则是double + double?
发布于 2018-06-23 06:37:16
未定义行为意味着输出可能是您所期望的,或者可能超出了类型的有效范围的某个不确定值。
未定义行为的一个明显示例是https://en.cppreference.com/w/cpp/language/ub#Signed_overflow。
unsigned int i; // uninitialized
int x = i + 2; // indeterminate value
if (x + 1 > x) {} // undefined behavior due to signed overflow
如果x
的最大值为unsigned int
,则i
的值可以超出i
的有效范围。
因此,对于值不确定的表达式,类型安全性得不到保证。
发布于 2018-06-23 08:03:44
@codekaizer和@Shankar是正确的:根据定义,未定义的行为不是类型安全行为。然而,如何将其应用于原始类型则更难理解。任何适当长的位序列都可能是有效的int
,这似乎是合理的。正如@BoPersson在下面指出的,这是不完全正确,实现可以自由地包含导致算术下中断的值。对于整数,这实际上只适用于用于除法时的0,但这并不意味着标准不允许在适当不寻常的体系结构上使用浮点NaN
之类的整数版本。
读者可能会发现一个虚拟函数的例子,更直观地说明了为什么未初始化的变量不是类型安全的。考虑:
struct Base {
virtual int foo() const =0;
};
struct DerivedA : public Base {
int foo() const override { return 10; }
};
struct DerivedB : public Base {
int foo() const override { return -10; }
};
int main() {
Base* abstractStructPtr;
std::cout << abstractStructPtr->foo() << std::endl;
return 0;
}
abstractStructPtr
的类型意味着您可以在它上调用foo()
。表达式是有效的:abstractStructPtr
有一个类型,这就是为什么可以调用foo()
。但是,foo()
的实现存在于派生类中。
由于abstractStructPtr
没有初始化,所以它所指向的数据的结构不能保证它能够完成对foo()
的调用。换句话说,虽然absractStructPtr
的类型是Base*
,但不能保证所指向的数据实际上是任何类型的Base
对象。因此,调用foo()
是未定义的行为,而不是类型安全。任何事情都可能发生;实际上,它可能只是通过内存访问冲突而崩溃,但它可能不会!卡布卢西。
发布于 2018-06-23 06:25:32
即使“x”被声明为“双”,但由于没有初始化,它在内存中有一个随机位模式,并且该模式可能不代表任何有效的双精度数字。因此,“z的意义”是没有定义的。
https://stackoverflow.com/questions/51002033
复制