-2

Am I breaking C++ coding conventions writing a helper function which allocates a 2D array outside main()? Because my application calls for many N-dimensional arrays I want to ensure the same process is followed. A prototype which demonstrates what I am doing :

#include <iostream>

// my helper function which allocates the memory for a 2D int array, then returns its pointer.
// the final version will be templated so I can return arrays of any primitive type.
int** make2DArray(int dim1, int dim2)
{
    int** out = new int* [dim1];
    for (int i = 0; i < dim2; i++) { out[i] = new int[dim2];}
    return out;
}

//helper function to deallocate the 2D array.
void destroy2DArray(int** name, int dim1, int dim2)
{
    for (int i = 0; i < dim2; i++) { delete[] name[i]; }
    delete[] name;
    return;
}

int main()
{
    int** test = make2DArray(2,2); //makes a 2x2 array and stores its pointer in test.

    //set the values to show setting works
    test[0][0] = 5;
    test[0][1] = 2;
    test[1][0] = 1;
    test[1][1] = -5;

    // print the array values to show accessing works
    printf("array test is test[0][0] = %d, test[0][1] = %d, test[1][0] = %d, test[1][1] = %d",
        test[0][0],test[0][1],test[1][0],test[1][1]);

    //deallocate the memory held by test
    destroy2DArray(test,2,2);

    return 0;
}

My concern is this may not be memory-safe, since it appears I am allocating memory outside of the function in which it is used (potential out-of-scope error). I can read and write to the array when I am making a single small array, but am worried when I scale this up and there are many operations going on the code might access and alter these values.

I may be able to sidestep these issues by making an array class which includes these functions as members, but I am curious about this as an edge case of C++ style and scoping.

user4157124
  • 2,809
  • 13
  • 27
  • 42
Jordan
  • 3
  • 1
  • 2
    Why not using a `std::vector` or smart pointers at least? – πάντα ῥεῖ Sep 14 '22 at 16:04
  • `std::vector> make2DArray(unsigned dim1, unsigned dim2) { return std::vector>(dim1, std::vector(dim2)); }` – Some programmer dude Sep 14 '22 at 16:04
  • Your current code wont work very well if `dim1 != dim2`. – Some programmer dude Sep 14 '22 at 16:05
  • 2
    As for your worry about life-time: Memory allocated with `new` (or `new[]`) have a life-time until the pointer is passed to `delete` (or `delete[]`). – Some programmer dude Sep 14 '22 at 16:06
  • 2
    Don't use a "jagged" array for a 2d array. Use a single MxN buffer and index it with [row*N + col]. Wrap this inside a class with a real constructor and destructor. If this is practical rather than educational, use the Eigen library instead of writing this yourself. – Peter Sep 14 '22 at 16:10
  • The reason I do not use a `std::vector` (or any other object) in my example is because I want to understand when and how memory allocated by `new` is protected within the program. @Someprogrammerdude has answered this sufficiently, thanks. Since I am writing this for a practical application, I will take the suggestion @Peter and import the Eigen library. – Jordan Sep 14 '22 at 16:21
  • @Jordan [See this answer](https://stackoverflow.com/questions/21943621/how-to-create-a-contiguous-2d-array-in-c/21944048#21944048). This creates a 2D array, gives you contiguous storage (unlike your attempt), and you can still use `[][]` to access the elements. – PaulMcKenzie Sep 14 '22 at 16:56
  • 1
    @Jordan *but I am worried that when I scale this code up and there are many operations going on that the code might access and alter these values.* -- Why would scaling it up cause an issue, if the code already works with a smaller amount of values? The only issue is if you have the memory to hold the larger amounts of data, and not if you will have an access violation or similar. If you were to get an access violation with the larger array, then you can bet that you had the same issue with the smaller array, but didn't detect it. – PaulMcKenzie Sep 14 '22 at 17:04

2 Answers2

0

There is a difference between allocating 2D arrays like this and what you get when you declare a local variable like int ary[10][10] that based on your statement

My concern is that this operation may not be memory-safe, since it appears that I am allocating memory for an array outside of the function in which it is used (potential out-of-scope error)

I am guessing you do not fully understand.

You are allocating arrays on the heap. Declaring a local variable like int ary[10][10] places it on the stack. It is the latter case where you need to worry about not referencing that memory outside of its scope-based lifetime; that is, it is the following that is totally wrong:

//DON'T DO THIS.

template<size_t M, size_t N>
int* make2DArray( ) {
    int ary[M][N];
    return reinterpret_cast<int*>(ary);
}

int main()
{
    auto foo = make2DArray<10, 10>();
}

because ary is local to the function and when the stack frame created by the call to make2DArray<10,10> goes away the pointer the function returns will be dangling.

Heap allocation is a different story. It outlives the scope in which it was created. It lasts until it is deleted.

But anyway, as others have said in comments, your code looks like C not C++. Prefer an std::vector<std::vector<int>> rather than rolling your own.

jwezorek
  • 8,592
  • 1
  • 29
  • 46
0

If you must use an array and are allergic to std::vector, create the 2d array (matrix) as one contiguous area in memory:

int * matrix = new int [dim1 * dim2];

If you want to set the values to zero:

std::fill(matrix, (matrix + (dim1 * dim2)), 0);  

If you want to access a value at <row, column>:

int value = matrix[(row * column) + column];

Since the matrix was one allocation, you only need one delete:

delete [] matrix;  
Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154