1

What is the correct syntax in C++ to initialise and alter a 2D integer array declared using int** ?

I am using the code below, but I get unexpected behaviour due to memory being de-allocated upon function exit, which is of course what it's meant to do.

But, how can this be done neatly in C++? I know others have faced similar issues but questions such as Is passing pointer argument, pass by value in C++? is too general.

void Insert_into_2D_Array(int** foo, int x_pos, int y_pos, int x_size, int y_size)
{
  int insert_value = 10; 

  if (x_pos < x_size && y_pos < y_size) {
    foo[x_pos][y_pos] = insert_value;    // insert_value lost post func exit?
  }
}

void Init_2D_Array(int** foo, int x_size, int y_size)
{

  foo = new int*[x_size];    // new alloc mem lost post func exit ?
  for (int i=0;i<x_size;i++)
  {
      foo[i] = new int[y_size];     // new alloc mem lost post func exit
  }
}

int main(int agc, char** argv)
{

  int** foo; 
  int x_size=10, y_size=10;   
  Init_2D_Array(foo, x_size, y_size); 
  Insert_into_2D_Array(foo, 3,3, x_size, y_size); 

}
dr_rk
  • 4,107
  • 12
  • 45
  • 67
  • when you call `new` the memory isnt deallocated when the function returns. – 463035818_is_not_a_number Jul 14 '17 at 14:32
  • It is not being deallocated. You are simply only setting the value of the pointer `foo`, which is passed *by-value*. You need to either declare `foo` locally, or pass an `int*** foo` and de-reference at the end. – willywonkadailyblah Jul 14 '17 at 14:34
  • Doing `Init_2D_Array(foo)` passes it by value as you are saying. To pass it by reference, what should the syntax be? – dr_rk Jul 14 '17 at 14:36
  • @dr_rk `int**& foo` – willywonkadailyblah Jul 14 '17 at 14:36
  • 2
    "the correct syntax in C++" is to use `std::vector`. – underscore_d Jul 14 '17 at 14:37
  • Thanks! So is the only change necessary here is then how it's passed? – dr_rk Jul 14 '17 at 14:37
  • I know how to do this with a `std::vector`, but wanted to use arrays – dr_rk Jul 14 '17 at 14:38
  • @dr_rk no. If you `new` without `delete` you have memory leaks. – 463035818_is_not_a_number Jul 14 '17 at 14:38
  • I don't know why you'd want to eschew a standard container to use brittle, leaky, horribly manual C-style code instead, but if you say so. – underscore_d Jul 14 '17 at 14:39
  • @underscore_d It's better to learn how to use the old style properly than to ignore it and hope you don't encounter it because there are better options available. OP understanding the answer to this question is an important step for OP in learning C++. – Millie Smith Jul 14 '17 at 14:49
  • @MillieSmith yes but it's too generic and doesn't clarify how to pass 2D arrays by value or reference. – dr_rk Jul 14 '17 at 15:00
  • @dr_rk The duplicate is not too general. You don't understand pass by reference vs pass by value. You need to do `Init_2D_Array(&foo, ...)` and declare `Init_2D_Array` as `Init_2D_Array(int***, ...)`, or you need to pass by reference with `Init_2D_Array(int** &, ...)`. That duplicate I posted is exactly what you need. And everyone saying that you should switch to a vector is not helping because you will run into similar and arguably worse problems when you don't pass your vector by reference. – Millie Smith Jul 14 '17 at 15:00
  • @dr_rk A 2d array (a pointer to a pointer) is just another type. The exact same rules apply for pass by reference / pass by address / pass by value. – Millie Smith Jul 14 '17 at 15:01

3 Answers3

1

Like spug said, memory is not being deallocated, you just lose the pointer. You need to pass it by reference:

void Init_2D_Array(int** & foo, int x_size, int y_size)

I'd recommend never using multi-dimensional arrays in C++ unless you really need pointers to pointers. A simplier and safer way is to create a wrapper class around a single-dimensional array of size x*y, then define functions or operators enabling you to access the underlying elements by specifying x and y coordinates.

class Array2D
{
private:
    int* m_array;
    int m_sizeX;
    int m_sizeY;

public:
    Array2D(int sizeX, int sizeY) : m_sizeX(sizeX), m_sizeY(sizeY)
    {
        m_array = new int[sizeX*sizeY];
    }

    ~Array2D()
    {
        delete[] m_array;
    }

    int & at(int x, int y)
    {
        return m_array[y*sizeX + x];
    }
};

This solution has an additional benefit of being more cache-friendly by storing the contents linearly and tightly packed.

Pontifex
  • 136
  • 6
1

as underscore_d suggested, correct way is using a std::vector and pass references.

But if you still want to use pointers as in your code,

here

if (x_pos < x_size && y_pos < y_size) { foo[pos] = insert_value; // insert_value lost post func exit }

The code that worked for me is :

void Insert_into_2D_Array(int** foo, int x_pos, int y_pos, int x_size, int y_size)
{
  int insert_value = 10000;

  if (x_pos < x_size && y_pos < y_size) {
    (foo)[x_pos][y_pos] = insert_value;    // insert_value lost post func exit
  }
}

void Init_2D_Array(int*** foo, int x_size, int y_size)
{

  *foo = new int*[x_size];    // new alloc mem lost post func exit
  for (int i=0;i<x_size;i++)
  {
      (*foo)[i] = new int[y_size];     // new alloc mem lost post func exit
  }
}

void main(){

      int** foo = NULL;
      int x_size=10, y_size=10;
      Init_2D_Array(&foo, x_size, y_size);
      Insert_into_2D_Array(foo, 3,3, x_size, y_size);

      cout<<"#############  "<<foo[3][3]<<endl;
}
AdityaG
  • 408
  • 1
  • 3
  • 14
1

First of all, for 2D arrays or any other multidimensional array it is better to use a one dimensional buffer as storage and you address an item as y*width+x;

But for your particular case, if you really want to use it like that you a few problems. Here you have the fixed code. First of all, in the init function we will not get back the new allocated pointer. You pass to the function and address, and you will get back the same address that you passed to the function. The new allocated address is lost.

void Insert_into_2D_Array(int** foo, int x_pos, int y_pos, int x_size, int y_size)
{
    int insert_value = 10;

    if (x_pos < x_size && y_pos < y_size) {
        foo[x_pos][y_pos] = insert_value;    // insert_value lost post func exit
    }
}

int** Init_2D_Array(int x_size, int y_size)
{

    int** foo = new int*[x_size];    // new alloc mem lost post func exit  
    for (int i = 0; i<x_size; i++)
    {
        foo[i] = new int[y_size];     // new alloc mem lost post func exit
    }

    return foo;
}

int main()
{

    int** foo;
    int x_size = 10, y_size = 10;
    foo = Init_2D_Array(x_size, y_size);
    Insert_into_2D_Array(foo, 3, 3, x_size, y_size);

    return 0;
}
  • Are you absolutely certain that `foo[x_pos][y_pos] = insert_value;` will set the value in the array and it won't be lost after function exits. – dr_rk Jul 14 '17 at 15:10
  • Yes. You can try it in debbuger, or print the result after the execution. At insert the buffer is already allocated, you pass an address for foo to the function Insert_into_2D_Array and at the end you have the same address. Also you assign the value of insert_position, so is not problem that after the function exit's this variable will be destroyed. – Constantin Iliescu Jul 14 '17 at 15:17