how can you set the pointer to a known bad pointer value? How can you set a pointer to an address which you can assure won't be allocated?
First, let me be clear that the example code you refer to is a dirty hack and is intended to provide some guidance to assist debugging. It is not intended as a production quality memory management tool; it isn't even intended to be "drop in" code - it's an example of a debugging technique.
Setting a pointer to a hardcoded value isn't guaranteed to be a "bad pointer" unless you know something about the target environment. 0xDEADBEEF
is a value that is likely to be an invalid pointer on many environments just out of luck. The value was chosen in because I had seen it used as a marker for "invalid data" in other code and it is easily spotted when viewing memory dumps. I believe it is (or was, maybe not anymore - that answer was from 14 years ago!) commonly used to indicate memory areas that are invalid/unused. Similar to some of the values Microsoft used in their debug library versions of some memory management routines (see https://stackoverflow.com/a/370362)
what does: if (DEBUG) bar = (bar_type*)(long_ptr)(0xDEADBEEF);
even do? What's DEBUG
? I couldn't find a macro named so.
I might not have said explicitly in the answer you refer to, but the example code might more properly be call a pseudo-code example. if (DEBUG)
is used to indicate a bit of code that is conditionally executed "if this is a debug build".
For example, DEBUG
could be a macro (or variable) that is defined as non-zero during a debug build of the problem. Possibly on the compiler's command line (maybe something like -DDEBUG=1
). A DEBUG
macro is something that I found commonly used for code that is enabled in debug builds only.
Also, what's long_ptr
? What does it do?
To aid in the transition from 32-bit to 64-bit systems, MS added types that are size of pointers. LONG_PTR
is an integer type that has the size of a pointer (32 or 64 bits as appropriate). I probably should have used LONG_PTR
instead of long_ptr
. I believe the cast is technically unnecessary, but I think it's still useful as a notation that makes clear that an integer is being 'converted' to a pointer - a coding idiom that uses dirty looking casts to call out a dirty hack.
DEBUG
is not a standard macro, it's one that you would define to work with this code. – Impede0xDEADBEEF
will not be valid. There's no standard way to get an invalid pointer other than NULL. – Impede0xDEADBEEF
overNULL
is that it will be more obvious in debugging output. NULL pointers are often used for initial values, so you can't distinguish a pointer that hasn't been updated from one that has been cleared. – ImpedeDEADBEEF
then you "know" that the pointer was set to that value to indicate it has been cleared. If it was spurious, oh well, 1 in a couple billion shot. – Tally0xDEADBEEF
relatively safe value for bad pointers decided upon by programmer.(long_ptr)(0xDEADBEEF)
cast the value to along_ptr
, which is presumably an alias for a pointer on a 32 bit address system. It's probablyvoid*
these days. The(bar_type*)
tells the C++ compiler, "Yes, I really mean to violate strict aliasing and assign a pointer of the wrong type to abar
. I know what I'm doing, so don't complain." – Cow0xDEADBEEF
is that when you see it in a debugger it is extremely likely that it is deallocated storage. The goal is not to implement a robust system, you can't (or I guess shouldn't) write code that compares pointers to0xDEADBEEF
to manage errors, it's just a flag a programmer can spot while debugging. – Laparotomybar
. When the destructor exits reading from it will be undefined behavior. But if you made a mistake somewhere and accidentally try to read it after it was destructed, then you want your program to fail as quickly and hard as possible while testing. A random address gives a better chance than zero or a previous value that has been valid earlier. – Polyesteroperator delete
to do it for you in everydelete
call. – Polyesterif(DEBUG)
, when it would much more likely be something like#ifdef DEBUG
and 2. why it uses the weird cast combination when the simpler and cleanerbar = reinterpret_cast<bar_type*>(0xDEADBEEF)
would have exactly the same effect. – Polyestermalloc
'd by the heap manager.) – Dagon(bar_type*)0xDEADBEEF
has also always been allowed in C. – Polyesterbar = (bar_type*) 0xDEADBEEF
work the same? – Macneil(long_ptr)
(whatever that is) but it could be some legacy thing, taking 16 bit short jump pointers into account perhaps. – Untruthvoid*
and anything fromvoid*
, so the code probably originally looked likeif (DEBUG) bar = (long_ptr)(0xDEADBEEF);
. C++ is significantly more paranoid about casting because casting is one hell of a fast way to blow your program up if you're not very, very careful. – Cowlong_ptr
likely would have been a bit more interesting that justvoid*
back in those days. Mind you, you wouldn't be stuffing DEADBEAF in as a address on one of those systems. – CowNow if anything has a dangling reference to the Foo object that's been deleted, any use of bar will not avoid referencing it due to a NULL check - it'll happily try to use the pointer and you'll get a crash that you can fix
but what would result in a crash? dereferencing the pointer? Because from my tests, it doesn't crash when dereferencing the pointer wafter assigning 0xDEADBEEF to it – Macneilnullptr
. It's not as easy to visually recognize as being wrong, many reasons to park a pointer atnullptr
, but it almost certainly will crash. Unfortunately we can't guarantee it will, Undefined Behaviour's undefined after all, but I haven't worked on a processor that doesn't reserve some amount of space around 0 as a no-man's land and force a crash if someone steps into it in quite a while. – Cownullptr
. If you can find a magic, non NULL you can guarantee will crash, use that. If you cannot, you takes yer chances. These days we've got some really good tools like valgrind and ASAN you can use to trap a lot of problems like this without vandalizing your code. Consider using them. – Cow