简体   繁体   中英

What happens if you dereference `new int`?

Is the following safe?

*(new int);

I get output as 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. 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. 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 ):

If the new-initializer is omitted, the object is default-initialized (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:

To default-initialize an object of type T means:

— 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);

— if T is an array type, each element is default-initialized;

otherwise, no initialization is performed.

we can see from section 8.5 that producing an indeterminate value is undefined:

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). [ Note: Objects with static or thread storage duration are zero-initialized, see 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.

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:

A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

— 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

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. —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.

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. 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 . 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. 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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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