简体   繁体   English

如果你取消引用`new int`会发生什么?

[英]What happens if you dereference `new int`?

Is the following safe? 以下是安全的吗?

*(new int);

I get output as 0 . 我输出为0

It's undefined because you're reading an object with an indeterminate value. 它是未定义的,因为您正在读取具有不确定值的对象。 The expression new int() uses zero-initialisation, guaranteeing a zero value, while new int (without parentheses) uses default-initialisation, giving you an indeterminate value. 表达式new int()使用零初始化,保证零值,而new int (没有括号)使用默认初始化,为您提供不确定的值。 This is effectively the same as saying: 这实际上与说:

int x;              // not initialised
cout << x << '\n';  // undefined value

But in addition, since you are immediately dereferencing the pointer to the object you just allocated, and do not store the pointer anywhere, this constitutes a memory leak. 但另外,由于您立即取消引用指向您刚刚分配的对象的指针,并且不将指针存储在任何位置,这就构成了内存泄漏。

Note that the presence of such an expression does not necessarily make a program ill-formed; 请注意,这种表达式的存在并不一定会使程序形成错误; this is a perfectly valid program, because it sets the value of the object before reading it: 这是一个非常有效的程序,因为它在读取之前设置了对象的值:

int& x = *(new int);  // x is an alias for a nameless new int of undefined value
x = 42;
cout << x << '\n';
delete &x;

This is undefined behavior ( UB ) since you are accessing an indeterminate value, C++14 clearly makes this undefined behavior. 这是未定义的行为UB ),因为您正在访问一个不确定的值,C ++ 14显然会产生这种未定义的行为。 We can see that new without initializer is default initialized , from the draft C++14 standard section 5.3.4 New paragraph 17 which says ( emphasis mine going forward ): 我们可以看到没有初始化程序的new 默认初始化 ,来自草案C ++ 14标准5.3.4段落17强调我的未来 ):

If the new-initializer is omitted, the object is default-initialized (8.5). 如果省略new-initializer,则默认初始化对象(8.5)。 [ Note: If no initialization is performed, the object has an indeterminate value. [注意:如果未执行初始化,则对象具有不确定的值。 —end note ] - 尾注]

for int this means an indeterminate value, from section 8.5 paragraph 7 which says: 对于int,这意味着一个不确定的值,来自第8.5节第7段,其中说:

To default-initialize an object of type T means: 默认初始化T类型的对象意味着:

— if T is a (possibly cv-qualified) class type (Clause 9), the default constructor (12.1) for T is called (and the initialization is ill-formed if T has no default constructor or overload resolution (13.3) results in an ambiguity or in a function that is deleted or inaccessible from the context of the initialization); - 如果T是(可能是cv限定的)类类型(第9节),则调用T的默认构造函数(12.1)(如果T没有默认构造函数或重载解析(13.3),则初始化是错误的歧义或在初始化上下文中删除或无法访问的函数中;

— if T is an array type, each element is default-initialized; - 如果T是数组类型,则每个元素都是默认初始化的;

otherwise, no initialization is performed. - 否则,不执行初始化。

we can see from section 8.5 that producing an indeterminate value is undefined: 我们可以从8.5节看到生成不确定值是未定义的:

If no initializer is specified for an object, the object is default-initialized . 如果没有为对象指定初始化程序,则默认初始化该对象。 When storage for an object with automatic or dynamic storage duration is obtained, the object has an indeterminate value , and if no initialization is performed for the object, that object retains an indeterminate value until that value is replaced (5.17). 当获得具有自动或动态存储持续时间的对象的存储时, 该对象具有不确定的值 ,并且如果没有对该对象执行初始化,则该对象保留不确定的值,直到该值被替换(5.17)。 [ Note: Objects with static or thread storage duration are zero-initialized, see 3.6.2. [注意:具有静态或线程存储持续时间的对象是零初始化的,请参见3.6.2。 — end note If an indeterminate value is produced by an evaluation, the behavior is undefined except in the following cases - 结束注释如果评估产生不确定的值,则行为未定义,但以下情况除外

and all the exceptions have to do with unsigned narrow char which int is not. 并且所有异常都与unsigned narrow char有关 ,而int不是。

Jon brings up an interesting example: 乔恩提出了一个有趣的例子:

int& x = *(new int); 

it may not be immediately obvious why this is not undefined behavior. 为什么这不是未定义的行为可能不会立即显而易见。 The key point to notice is that is is undefined behavior to produce a value but in this case no value is produced. 要注意的关键点是产生值的未定义行为,但在这种情况下不会产生任何值。 We can see this by going to section 8.5.3 References , which covers initialization of references and it says: 我们可以通过参见8.5.3 参考文献来看到这一点, 参考资料包括参考文献的初始化,它说:

A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows: 对类型“cv1 T1”的引用由类型“cv2 T2”的表达式初始化,如下所示:

— If the reference is an lvalue reference and the initializer expression - 如果引用是左值引用和初始化表达式

— is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2,” or - 是左值(但不是位字段),“cv1 T1”与“cv2 T2”引用兼容,或者

and goes on to say: 接着说:

then the reference is bound to the initializer expression lvalue in the first case [...][ Note: The usual lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are not needed, and therefore are suppressed, when such direct bindings to lvalues are done. 然后在第一种情况下将引用绑定到初始化表达式lvalue [...] [注意: 通常的左值到右值(4.1),数组到指针(4.2)和函数到指针(4.3) )当完成对左值的这种直接绑定时, 不需要标准转换,因此被抑制 —end note ] - 尾注]

It is possible that a computer has "trapping" values of int : invalid values, such as a checksum bit which raises a hardware exception when it doesn't match its expected state. 计算机可能具有“陷阱” int值:无效值,例如校验和位,当它与预期状态不匹配时会引发硬件异常。

In general, uninitialized values lead to undefined behavior. 通常,未初始​​化的值会导致未定义的行为。 Initialize it first. 首先初始化它。

Otherwise, no, there's nothing wrong or really unusual about dereferencing a new-expression. 否则,不,取消引用new-expression没有任何错误或非常不寻常。 Here is some odd, but entirely valid code using your construction: 以下是使用您的构造的一些奇怪但完全有效的代码:

int & ir = * ( new int ) = 0;
…
delete & ir;

First of all, Shafik Yaghmour gave references to the Standard in his answer . 首先, Shafik Yaghmour答案中提到了标准。 That is the best, complete and authoritative answer. 这是最好,最完整和最权威的答案。 None the less, let me try to give you specific examples that should illustrate the aforementioned points. 尽管如此,让我试着给你一些具体的例子来说明前面提到的观点。

This code is safe, well-formed and meaningful: 此代码安全,格式良好且有意义:

int *p = new int;       // ie this is a local variable (ptr) that points 
                        // to a heap-allocated block

You must not, however, dereference the pointer as that results in undefined behavior . 但是,您不能取消引用指针,因为这会导致未定义的行为 IE you may get 0x00, or 0xFFFFFFFF, or the instruction pointer (aka RIP register on Intel) may jump to a random location. IE可能会得到0x00或0xFFFFFFFF,或指令指针(也就是英特尔上的RIP寄存器)可能会跳转到随机位置。 The computer may crash. 电脑可能会崩溃。

int *p = new int;
std::cout << *p;    // Very, bad. Undefined behavior.

Run-time checkers such as Valgrind and ASan will catch the issue, flag it and crash with a nice error message. 诸如ValgrindASan之类的运行时检查程序将捕获该问题,标记它并使用一个很好的错误消息崩溃。

It is, however, perfectly fine to initialize the memory block you had allocated: 但是,初始化已分配的内存块是完全正常的:

int *p = new int;
*p = 0;

Background info: this particular way of writing the specification is very useful for performance, as it is prohibitively expensive to implement the alternative. 背景信息:编写规范的这种特殊方式对于性能非常有用,因为实现替代方案非常昂贵。

Note, as per the Standard references, sometimes the initialization is cheap, so you can do the following: 请注意,根据标准参考,有时初始化很便宜,因此您可以执行以下操作:

// at the file scope
int global1;          // zero-initialized
int global2 = 1;      // explicitly initialized

void f()
{
    std::cout << global1;
}

These things go into the executable's sections (.bss and .data) and are initialized by the OS loader. 这些东西进入可执行文件的部分(.bss和.data),并由OS加载程序初始化。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM