以下是否安全?
*(new int);
我的输出为0
。
答案 0 :(得分:11)
它未定义,因为您正在读取具有不确定值的对象。表达式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;
答案 1 :(得分:7)
这是undefined behavior( UB ),因为您正在访问一个不确定的值,C ++ 14显然会产生这种未定义的行为。我们可以在draft C++14 standard部分new
新段落 17 >中看到5.3.4
没有初始化程序默认初始化 em>其中说(强调我的前进):
如果省略new-initializer,则默认初始化该对象 (8.5)。 [注意:如果没有执行初始化,则对象具有 不确定的价值。 - 后注]
for int 这意味着一个不确定的值,来自8.5
段 7 ,其中包含:
默认初始化T类型的对象意味着:
- 如果T是(可能是cv限定的)类类型(第9节),则调用T的默认构造函数(12.1)(和 如果T没有默认构造函数或重载分辨率(13.3)导致a初始化是错误的 歧义或在初始化上下文中删除或无法访问的函数中;;
- 如果T是数组类型,则每个元素都是默认初始化的;
- 否则,不会执行初始化。
我们可以从8.5
部分看到,生成不确定值是未定义的:
如果没有为对象指定初始化程序,则该对象为 默认初始化即可。使用自动或自动存储对象时 获得动态存储持续时间,该对象具有不确定性 值,如果没有对该对象执行初始化,那么 对象保留不确定的值,直到替换该值 (5.17)。 [注意:具有静态或线程存储持续时间的对象是 零初始化,见3.6.2。 - 结束说明 如果评估产生了不确定的值,则行为未定义,但以下情况除外
并且所有异常都与 unsigned narrow char 有关, int 不是。
Jon提出了一个有趣的例子:int& x = *(new int);
为什么这不是未定义的行为可能不会立即显而易见。需要注意的关键点是生成值是未定义的行为,但在这种情况下不会产生任何值。我们可以通过转到8.5.3
参考部分来看到这一点,该部分涵盖了参考文献的初始化,它说:
对类型“cv1 T1”的引用由类型为“cv2 T2”的表达式初始化,如下所示:
- 如果引用是左值引用和初始化表达式
- 是左值(但不是位域),“cv1 T1”与“cv2 T2”或
引用兼容
继续说:
然后将引用绑定到初始化表达式lvalue中 第一种情况[...] [注:通常的左值到右值(4.1), 数组到指针(4.2)和函数到指针(4.3)标准 转换不是必需的,因此被抑制,等 直接绑定到左值。 - 后注]
答案 2 :(得分:2)
计算机可能会有&#34;陷阱&#34;值int
:无效值,例如校验和位,当它与预期状态不匹配时会引发硬件异常。
通常,未初始化的值会导致未定义的行为。首先初始化它。
否则,不,取消引用new-expression没有任何错误或非常不寻常。以下是使用您的构造的一些奇怪但完全有效的代码:
int & ir = * ( new int ) = 0;
…
delete & ir;
答案 3 :(得分:1)
首先, Shafik Yaghmour 在answer中提到了标准。这是最好,最完整和最权威的答案。尽管如此,让我试着给你一些具体的例子,说明上述几点。
此代码安全,格式良好且有意义:
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加载程序初始化。