#include #include // A dangling pointer is a pointer that still // stores an address, but the object that was // stored at that address no longer exists. // // For example: // // int *p_int{ new int{ 42 } }; // // delete p_int; // // The integer has been destroyed and its // memory has been deallocated, but p_int // has not been assigned a new value. // // It is now a dangling pointer. // // Dereferencing p_int after the deletion // causes undefined behavior. The program // might appear to work, print an unexpected // value, crash, or behave differently on // another execution. // // This exercise investigates what happens // when memory is released and new memory // is subsequently allocated. // // Before running the program, predict: // // 1. Does deleting p_int automatically // assign nullptr to it? // // 2. Must the next allocation reuse the // memory that was just released? // // 3. If the same address is reused, does // that make p_int valid again? // // 4. What happens when p_int is explicitly // assigned nullptr? int main() { int *p_int{ new int{ 42 } }; std::cout << "Initially:" << std::endl; std::cout << " p_int = " << p_int << std::endl; std::cout << "*p_int = " << *p_int << std::endl; // Save the address as an integer so that // we can compare it with a later allocation // without using the dangling pointer. // // Converting pointers to integers is // implementation-defined. std::uintptr_t // is available on implementations that // provide an integer type capable of // holding a pointer. // // For this introductory example, we will // simply print the address before deletion // and compare it visually with the next // allocation. std::cout << std::endl; std::cout << "Calling delete p_int..." << std::endl; delete p_int; // The allocated integer no longer exists. // // p_int is now a dangling pointer. // // Do not attempt to dereference it: // // std::cout << *p_int << std::endl; // // Do not attempt to write through it: // // *p_int = 0; // // Both operations cause undefined behavior. // Set the pointer to nullptr to indicate // that it no longer points to an object. p_int = nullptr; std::cout << "After assigning nullptr:" << std::endl; std::cout << " p_int = " << p_int << std::endl; // Print the double to 17 significant digits. std::cout << std::setprecision( 17 ); double *p_pi{ new double{ 3.14159265358979323 } }; std::cout << std::endl; std::cout << "After allocating p_pi:" << std::endl; std::cout << " p_pi = " << p_pi << std::endl; std::cout << "*p_pi = " << *p_pi << std::endl; // Compare the address printed for p_pi // with the original address of p_int. // // The allocator may or may not reuse // the memory previously occupied by // the integer. // // Even if the same address is reused, // the old integer no longer exists. // // Assigning through the old dangling // pointer would still be invalid. delete p_pi; p_pi = nullptr; return 0; } // Questions: // // 1. What value did p_int initially store? // // 2. Did assigning nullptr to p_int change // the integer that was previously stored // at that address? // // 3. Did the allocator reuse the address of // the deleted integer for the double? // // 4. If the addresses were the same, would // the following statement be valid? // // *p_int = 0; // // 5. Suppose two pointers both point to the // same dynamically allocated integer: // // int *p_a{ new int{ 42 } }; // int *p_b{ p_a }; // // What happens to p_b after // // delete p_a; // p_a = nullptr; // // 6. Does assigning nullptr to a pointer // release the dynamically allocated // memory to which it points? // Answers: // // 1. Initially, p_int stores the address // of the dynamically allocated integer. // // 2. No. Assigning nullptr changes only // the pointer. The integer had already // been destroyed by delete. // // 3. The answer depends on the allocator // and the particular execution. Memory // reuse is not guaranteed. // // 4. No. A dangling pointer does not become // valid merely because another allocation // happens to reuse the same address. // // In this program, p_int has also been // assigned nullptr, so dereferencing it // would be invalid for that reason. // // 5. p_b becomes a dangling pointer. // Assigning nullptr to p_a does not // change p_b. // // 6. No. Assigning nullptr does not call // delete. If no other pointer retains // the address, the allocation may become // unreachable, resulting in a memory leak.