@kigar64551 just to reiterate, pointer is not an address. It is variable. They are different things. A variable has an address. A pointer is a variable that has an address, and points to another variable. |
In a lot of (beginners) tutorials they do
not make a distinction between a
pointer (memory address) and a
variable that has
pointer type and therefore "stores" a
pointer (memory address). That's a simplification which often is perfectly fine for what they are trying to explain. Still, you do
not necessarily
have to store a pointer (memory address) in a pointer variable for it to come into existence or for it to be a pointer. Just like an
integer value does
not necessarily
have to be stored in an integer variable to exist or to be an integer 🙂
Quotes from the
official documentation:
Address-of operator &
The unary address-of operator (&) returns the address of (that is, a pointer to) its operand. |
-
https://docs.microsoft.com/en-us/cpp/cpp/address-of-operator-amp?view=msvc-170
new operator (C++)
Attempts to allocate and initialize an object or array of objects of a specified or placeholder type, and returns a suitably typed, nonzero pointer to the object |
-
https://docs.microsoft.com/en-us/cpp/cpp/new-operator-cpp?view=msvc-170
void* malloc (size_t size);
Allocates a block of size bytes of memory, returning a pointer to the beginning of the block.
|
-
https://cplusplus.com/reference/cstdlib/malloc/
Neither of the above operators/functions forces you to store the returned pointer (address) in a variable 😉
Proof that we can print the pointer to an object
without that pointer being stored in a variable:
1 2 3 4 5 6 7
|
void test(void)
{
Foo foo;
printf("Pointer to local object 'foo': %p", &foo);
printf("Pointer to new heap-allocated object: %p", new Foo());
printf("Pointer to heap-allocated memory block: %p", malloc(666));
}
|
Proof that we can access an object
without its pointer (as return by
new operator) being stored in a variable:
1 2 3 4
|
void test(void)
{
(new Foo())->do_something(); // <-- note: this is a memory leak, but still possible
}
|
________
As an aside: In some CPU architectures, a "pointer" is
not as simple as a memory address. For example, some CPU architectures use
segmented memory, where a "far" pointer consists of a segment selector
plus an offset (within that segment), whereas a "near" pointer consists only of an offset (within the "default" segment). That is why using the term "memory address" is
not fully correct when actually a "pointer" is meant.
So, a "pointer" does
not only exists when it is stored in a variable. A "pointer"
is the thing that we
can (but don't have to) store in a pointer variable. And, if we
do store a pointer in a pointer variable, then what actually gets stored there (i.e. the pointer) may
not be as simple as a memory address (cf. segmented memory).
(I will use "memory address" and "pointer" synonymously, more or less, for reasons of simplicity)
________
Regarding the original question:
It's kind of weird that, apparently, in the latest versions of MSVC, the
delete operator actually tries to modify the given pointer variable and overwrites it with some "sentinel" value.
Clearly, that can
only be possible, if the pointer is passed to
delete as a modifiable "lvalue". There are many cases where
delete definitely can
not modify the pointer value, simply because the pointer is
not given as a variable that could be modified! And, even if we
do pass the pointer to
delete as a variable (modifiable "lvalue"), then other
copies of the pointer may exist elsewhere. So I doubt the usefulness of this "feature".
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
void test(void)
{
Foo *foo = new Foo();
Foo *bar = foo;
printf("foo: %p\n", foo);
printf("bar: %p\n\n", bar);
delete foo;
printf("foo: %p\n", foo);
printf("bar: %p\n\n", bar);
bar->do_something(); // <-- whooops !!!
}
|
foo: 0000027322CF7C70
bar: 0000027322CF7C70
foo: 0000000000008123
bar: 0000027322CF7C70 |
1 2 3 4 5 6 7 8
|
void test2(void)
{
Foo* foo = new Foo();
printf("foo: %p\n", foo);
delete (foo + 0);
printf("foo: %p\n", foo);
foo->do_something(); // <-- whooops !!!
}
|
foo: 000001FE134C7B60
foo: 000001FE134C7B60 |