c++memorynew-operatorallocationreassign

Is it valid to assign a new memory block to a variable with a deleted pointer?


int main(){
    char *Allocation;
    Allocation = new char[8]; //here is out first allocation;

    delete[] Allocation;      //after while we delete it;
    
    Allocation = new char[16];//then we allocate some chunk of memory again to our pointer;

    delete[] Allocation;      //then finally we delete our second allocation;
}

Is this valid in C++? Does the first allocation affect the second allocation? Can I allocate a new memory block to a deleted pointer?


Solution

  • tl;dr Yes, that's perfectly fine.

    You have to distinguish between the actual value in memory and the value of the pointer variable that points to this actual value in memory.

    new provides the actual value in memory (which is not bound to a variable) and returns its address. This returned address is then set by assignment as the value of the variable Allocation.

    With delete you remove the actual value from memory. But this usually does not affect the value of the variable Allocation. The address is therefore retained, but must no longer be dereferenced. It is undefined what happens if you do this anyway.

    Therefore it is recommended to assign a nullptr (null pointer) to Allocation right after calling delete. Some implementations do assign a nullptr implicitly after delete if the program is compiled in debug mode. Therefore the usually above.

    A nullptr is just the address with the value 0. An access on this address is always invalid, so its the common value to imply that a pointer doesn't point to a valid location.

    So you can assign a different address as value to the variable Allocation at any time. The memory managed with new and delete exists independent of variables. These are only used for access.


    By the way, you should initialize your variable Allocation with either a call to new or with a nullptr. Otherwise your variable Allocation may contain some random value

    #include <iostream>
    
    int main() {
        char* a = new char[8]; // contains address of valid value
        // or
        char* b = nullptr; // contains address to "nowhere"
        char* c{nullptr};  // also fine
        char* d(nullptr);  // also fine
        char* e{};         // also fine (implicit nullptr)
    
        // Bad:
        char* f; // may contain any address
    
        std::cout << std::hex
            << "a: " << reinterpret_cast<std::size_t>(a) << '\n'
            << "b: " << reinterpret_cast<std::size_t>(b) << '\n'
            << "c: " << reinterpret_cast<std::size_t>(c) << '\n'
            << "d: " << reinterpret_cast<std::size_t>(d) << '\n'
            << "e: " << reinterpret_cast<std::size_t>(e) << '\n'
            << "f: " << reinterpret_cast<std::size_t>(f) << '\n';
    
        // Note: C++ syntax is evil 👿
        char* g(); // function declaration, e{} was a variable definition
        char* h(std::nullptr_t); // also a function declaration, compare to d
                                 // nullptr is a value
                                 // std::nullptr_t is a type
    
        // Linker error: undefined reference to `g()'
        // std::cout << reinterpret_cast<std::size_t>(g) << '\n';
    
        delete[] a;
    }
    

    Live code at Compiler Explorer

    Output:

    a: 558bb9976eb0 (possible output, always a valid address value)
    b: 0
    c: 0
    d: 0
    e: 0
    f: 3a66 (possible output, may have any address value)
    

    Note that C++ distinguishes between initialization and assignment:

    char* Allocation = new char[8]; // initialization 
    Allocation = new char[8];       // assignment