[英]What happens if you dereference `new int`?
以下是安全的吗?
*(new int);
我输出为0
。
它是未定义的,因为您正在读取具有不确定值的对象。 表达式new int()
使用零初始化,保证零值,而new int
(没有括号)使用默认初始化,为您提供不确定的值。 这实际上与说:
int x; // not initialised
cout << x << '\n'; // undefined value
但另外,由于您立即取消引用指向您刚刚分配的对象的指针,并且不将指针存储在任何位置,这就构成了内存泄漏。
请注意,这种表达式的存在并不一定会使程序形成错误; 这是一个非常有效的程序,因为它在读取之前设置了对象的值:
int& x = *(new int); // x is an alias for a nameless new int of undefined value
x = 42;
cout << x << '\n';
delete &x;
这是未定义的行为 ( UB ),因为您正在访问一个不确定的值,C ++ 14显然会产生这种未定义的行为。 我们可以看到没有初始化程序的new
默认初始化 ,来自草案C ++ 14标准第5.3.4
节新段落17 ( 强调我的未来 ):
如果省略new-initializer,则默认初始化对象(8.5)。 [注意:如果未执行初始化,则对象具有不确定的值。 - 尾注]
对于int,这意味着一个不确定的值,来自第8.5
节第7段,其中说:
默认初始化T类型的对象意味着:
- 如果T是(可能是cv限定的)类类型(第9节),则调用T的默认构造函数(12.1)(如果T没有默认构造函数或重载解析(13.3),则初始化是错误的歧义或在初始化上下文中删除或无法访问的函数中;
- 如果T是数组类型,则每个元素都是默认初始化的;
- 否则,不执行初始化。
我们可以从8.5
节看到生成不确定值是未定义的:
如果没有为对象指定初始化程序,则默认初始化该对象。 当获得具有自动或动态存储持续时间的对象的存储时, 该对象具有不确定的值 ,并且如果没有对该对象执行初始化,则该对象保留不确定的值,直到该值被替换(5.17)。 [注意:具有静态或线程存储持续时间的对象是零初始化的,请参见3.6.2。 - 结束注释如果评估产生不确定的值,则行为未定义,但以下情况除外
并且所有异常都与unsigned narrow char有关 ,而int不是。
乔恩提出了一个有趣的例子:
int& x = *(new int);
为什么这不是未定义的行为可能不会立即显而易见。 要注意的关键点是产生值的未定义行为,但在这种情况下不会产生任何值。 我们可以通过参见8.5.3
参考文献来看到这一点, 参考资料包括参考文献的初始化,它说:
对类型“cv1 T1”的引用由类型“cv2 T2”的表达式初始化,如下所示:
- 如果引用是左值引用和初始化表达式
- 是左值(但不是位字段),“cv1 T1”与“cv2 T2”引用兼容,或者
接着说:
然后在第一种情况下将引用绑定到初始化表达式lvalue [...] [注意: 通常的左值到右值(4.1),数组到指针(4.2)和函数到指针(4.3) )当完成对左值的这种直接绑定时, 不需要标准转换,因此被抑制 。 - 尾注]
计算机可能具有“陷阱” int
值:无效值,例如校验和位,当它与预期状态不匹配时会引发硬件异常。
通常,未初始化的值会导致未定义的行为。 首先初始化它。
否则,不,取消引用new-expression没有任何错误或非常不寻常。 以下是使用您的构造的一些奇怪但完全有效的代码:
int & ir = * ( new int ) = 0;
…
delete & ir;
首先, Shafik Yaghmour在答案中提到了标准。 这是最好,最完整和最权威的答案。 尽管如此,让我试着给你一些具体的例子来说明前面提到的观点。
此代码安全,格式良好且有意义:
int *p = new int; // ie this is a local variable (ptr) that points
// to a heap-allocated block
但是,您不能取消引用指针,因为这会导致未定义的行为 。 IE可能会得到0x00或0xFFFFFFFF,或指令指针(也就是英特尔上的RIP寄存器)可能会跳转到随机位置。 电脑可能会崩溃。
int *p = new int;
std::cout << *p; // Very, bad. Undefined behavior.
诸如Valgrind和ASan之类的运行时检查程序将捕获该问题,标记它并使用一个很好的错误消息崩溃。
但是,初始化已分配的内存块是完全正常的:
int *p = new int;
*p = 0;
背景信息:编写规范的这种特殊方式对于性能非常有用,因为实现替代方案非常昂贵。
请注意,根据标准参考,有时初始化很便宜,因此您可以执行以下操作:
// at the file scope
int global1; // zero-initialized
int global2 = 1; // explicitly initialized
void f()
{
std::cout << global1;
}
这些东西进入可执行文件的部分(.bss和.data),并由OS加载程序初始化。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.