Deleting a dynamically allocated 2D array [duplicate]
Asked Answered
S

1

23

So I'm used to memory management in C where free(pointer) will free up all space pointed to by pointer. Now I'm confusing myself when attempting to do something simple in C++.

If I have a 2D array of doubles allocated in a manner similar to this

double** atoms = new double*[1000];
for(int i = 0; i < 1000; i++)
  atoms[i] = new double[4];

what would be the correct method of freeing the memory on the heap allocated by new?

My thoughts were originally this (because my brain was thinking in C):

for(int i = 0; i < 1000; i++)
  delete atoms[i];
delete atoms;

But I had forgotten the existence of the delete[] operator so I believe the correct method is as follows:

for(int i = 0; i < 1000; i++)
  delete[] atoms[i];
delete[] atoms;

Is it important to understand the difference between the delete and delete[] operators? Or can I just assume that whenever I allocate an array with ptr = new x[] I must also deallocate it with delete[] ptr?

Spoliate answered 8/6, 2015 at 23:17 Comment(2)
The version with delete [] is valid. If you call delete [] for non array object, it is an ub. You can see in other SO questions: https://mcmap.net/q/16878/-delete-vs-delete-duplicateCleliaclellan
Thanks, I had trouble finding posts like that for some reason... but that pretty much clears it up. I marked this as a duplicate of that questionSpoliate
H
34

In reality, an array of pointers pointed to by a pointer is still an array of integral data types or numbers to hold the memory addresses. You should use delete[] for both.

Also, yes, a new[] implies a delete[].

When you create an array of arrays, you're actually creating an array of numbers that happen to hold the memory address for another array of numbers. Regardless, they're both arrays of numbers, so delete both with delete[].

http://coliru.stacked-crooked.com/a/8a625b672b66f6ce

#include <iostream>

int main() {

    //Hey, pointers have a finite size, no matter the indirection level!
    std::cout << "sizeof(int*): " << sizeof(int*) << std::endl;
    std::cout << "sizeof(int**): " << sizeof(int**) << std::endl;
    std::cout << "sizeof(int***): " << sizeof(int***) << std::endl;

    //Create an array of pointers that points to more arrays
    int** matrix = new int*[5];
    for (int i = 0; i < 5; ++i) {
        matrix[i] = new int[5];
        for (int j = 0; j < 5; ++j) {
            matrix[i][j] = i*5 + j;
        }
    }

    //Print out the matrix to verify we have created the matrix
    for (int j = 0; j < 5; ++j) {
        for (int i = 0; i < 5; ++i) {
            std::cout << matrix[j][i] << std::endl;
        }
    }

    //Free each sub-array
    for(int i = 0; i < 5; ++i) {
        delete[] matrix[i];   
    }
    //Free the array of pointers
    delete[] matrix;

    return 0;
}
Haystack answered 8/6, 2015 at 23:20 Comment(1)
Just to be clear, the type of a pointer is not necessarily an int or a long long or any other type. The standard defines specific types you can cast to that are arithmetic types: std::intptr_t and std::uintptr_t. Because of size differences (and possibly some REALLY weird stuff, if you can imagine a weird architecture where pointers are double), int and std::intptr_t could possibly be different sizes (e.g. 32-bit int and 64-bit std::uintptr_t values).Haystack

© 2022 - 2024 — McMap. All rights reserved.