Pass 2d array to function?

Aug 17, 2013 at 1:55am
Hello,
As far as I understand, this is correct/fine code, aside from the "8" being hard-coded (it normally should not be)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void ShowArray(int* a)
{
  for(int col = 0; col < 8; ++col)
  {
    drawFunc(a[col]);
  }
}

int main(int argc, char* args[])
{
  //..
  map[8];
  ShowArray(map);
  //..
}

As far as I know, the above code sends the ptr to the func, not the values individually (which is what I want)

So how do I do this for a contiguous 2d array (i.e. not an array of pointers using 'new'!)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void ShowArray(int*??? a)
{
  for(int row = 0; row < 8; ++row
  {
    for(int col = 0; col < 8; ++col)
    {
      drawFunc(a[row][col]);
    }
  }
}

int main(int argc, char* args[])
{
  //..
  map[8][8];
  ShowArray(map???);
  //..
}

Thank you very much!
Aug 17, 2013 at 2:36am
Add another * for each dimension of the array.
Aug 17, 2013 at 3:06am
Forgot to say that that was the first thing I tried (since it seems intuitive to try that), but it doesn't work.. unless I'm misunderstanding something.

so, ShowArray(int** a) ?


ShowArray(map)
gets "argument int* incompatible with parameter int**"

ShowArray(*map)
gets "argument int incompatible with parameter int**"

ShowArray(&map)
gets "argument int(*)[8] incompatible with parameter int**"

Obviously I don't have enough understanding of what needs to be done, so any explanation would be appreciated.
Aug 17, 2013 at 4:08am
one way is pass by reference
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void ShowArray(int (&a)[8][8])
{
  for(int row = 0; row < 8; ++row)
  {
    for(int col = 0; col < 8; ++col)
    {
      drawFunc(a[row][col]);
    }
  }
}

int main()
{
  int map[8][8];
  ShowArray(map);
}


another is pass by pointer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void ShowArray(int (*a)[8])
{
  for(int row = 0; row < 8; ++row)
  {
    for(int col = 0; col < 8; ++col)
    {
      drawFunc(a[row][col]);
    }
  }
}

int main()
{
  int map[8][8];
  ShowArray(map);
}

Last edited on Aug 17, 2013 at 4:09am
Aug 17, 2013 at 4:24am
Thank you very much, I tried passing by reference and it works, however, I have a question:

if I'm passing "map" into ShowArray, and map is an entire array, does this mean I'm passing the entire array (one int at a time) through the stack, into the reference?
Aug 17, 2013 at 4:36am
does this mean I'm passing the entire array (one int at a time) through the stack, into the reference?

no, "map" within ShowArray and "map" within main() are two names of the same object - that's the point of pass-by-reference.
Aug 17, 2013 at 12:34pm
Note that this form

void ShowArray(int (*a)[8])

is identical to

void ShowArray(int a[][8])

as far as the compiler is concerned. They are not seen as distinct overloads.

(for what it's worth,

void ShowArray(int a[8][8])

void ShowArray(int a[1][8])

void ShowArray(int a[1024][8])

...

are all also seen as the same overload, as the compiler totally ignores the first dimension, just to confuse people.)

As the (*)[8] and [][8] forms put no constraints on the first dimension (as does the reference form), you should really pass the first dimension as well as the array.

Also, to complete the set, even though I've never seen this form used outside of exercises, you can use an array pointer. See code below.

By the way, your orig. question is a bit ambiguous when it comes to what you want?

As far as I know, the above code sends the ptr to the func, not the values individually (which is what I want)

You want to send the values individually??

Andy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void ShowArray(int (*a)[8][8])
{
  for(int row = 0; row < 8; ++row)
  {
    for(int col = 0; col < 8; ++col)
    {
      drawFunc((*a)[row][col]); // derference a
    }
  }
}

int main()
{
  int map[8][8];
  ShowArray(&map); // pass address of map

  return 0;
}
Last edited on Aug 17, 2013 at 12:39pm
Aug 17, 2013 at 12:53pm
The better way is to declare the function the following way

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iterator>

void drawFunc( int x ) { /* some processing with x */ }

void ShowArray( const int ( *first )[8], const int ( *last )[8] )
{
   for ( ; first != last; ++first )
   {
      for ( int x : *first ) drawFunc( x );
   }
}

int main()
{
   int map[8][8];

   ShowArray( std::begin( map ), std::end( map ) );
}
Aug 18, 2013 at 1:59am
Cubbi, Andy, Vlad:
Thank you so much for the information and advice. I've learned a lot, and I've always been a bit baffled at proper syntax for sending a ptr to a 2D array, so thank you.

To answer some questions:
You want to send the values individually??

I worded that part ambiguously -- oops. I meant, "By doing it this way, am I pushing the entire array onto the stack? Because I'd like to avoid that if possible (i.e. I'd highly prefer if the only thing sent through the stack was one pointer/address when that function is called once.)

By the way, your orig. question is a bit ambiguous when it comes to what you want?

Also, I may be able to clear something up by saying that the size of the two dimensions would also be passed into the function, so that it would support multiple sized 2D arrays. So something like: ShowArray(map, 8, 5) or ShowArray(table, 6, 31). I hard-coded the 8's because I was more concerned with the passing the ptr properly and figured I'd cross the bridge of having variable sizes when I come to it. As to exactly what I want, the function, in reality, is a SaveFunc which (hopefully) takes a ptr to the 2D array (which may be different x,y, and writes it to a binary file (which is already implemented and working).

Vlad, thank you for the suggestion, but, if possible, I would like to use the most basic-as-possible datatypes, mostly for myself, to have a project that would be a good reference for bare foundations work. (I'm even using char arrays instead of strings so that I never forget how to implement them without issue)

-

So, with the addition of wanting variable sized dimensions, is it still possible? How could I use the vars passed in, in the proper places? I almost feel that I would have to ... cast after the fact? Am I wrong?

1
2
3
4
5
6
7
8
9
10
11
12
ShowArray(int* ptr, int x, int y)
{
   int (*arr)[][x] = &ptr;

   for(int row = 0; row < y; ++row)
   {
      for(int col = 0; col < x; ++col)
      {
         func(arr[row][col]);
      }
   }
}
Aug 18, 2013 at 2:33am
(I'm even using char arrays instead of strings so that I never forget how to implement them without issue)

It's like using addition instead of multiplication so that you never forget how to multiply by hand.

saying that the size of the two dimensions would also be passed into the function, so that it would support multiple sized 2D arrays

the low-level C-style 2D arrays have their sizes as parts of their types, which means to pass them into a function, you need a function template:

1
2
3
4
5
6
7
8
9
10
11
template<std::size_t X, std::size_t Y>
void ShowArray(int (&arr)[X][Y])
{
   for(int row = 0; row < Y; ++row)
   {
      for(int col = 0; col < X; ++col)
      {
         func(arr[row][col]);
      }
   }
}


or you could use (or build, if you like) a matrix type that uses 1D storage internally and exposes a row,col indexing in the interface, so that you could write the usual
1
2
3
void ShowArray(const Matrix& m) {
[...]
func(m[row][col]) // or func(m(row, col)), depending on the library you're using) 
Aug 18, 2013 at 3:26am
If I set up the function call as such:

ShowArray(&array, numCols, numRows);
or
ShowArray(array, numCols, numRows);

could I still get "ShowArray" to work as discussed?
Aug 18, 2013 at 10:52am
could I still get "ShowArray" to work as discussed?

I assume you mean as sort of shown in your last mail? There have been several different approaches suggested in this discussion...

Note that Cubbi's template approach is the best way to go if you have to use a C-style array in C++ code, as you can't pass dimensions that are out of step with the array. And the "Matrix" class far better (esp. as they can be coded to size dynamically, if required.)

But you can take advantage of the the fact that the elements of a 2D array are stored contiguously in row major order and do this (which is what a Matrix class would usually do internally, and what the compiler is doing behaind the scenes for you when you use operator[] on an array.)

1
2
3
4
5
6
7
8
9
10
void ShowArray2D(int* ptr, int x, int y)
{
   for(int row = 0; row < y; ++row)
   {
      for(int col = 0; col < x; ++col)
      {
         func(arr[row * x + col]); // explicilty do the maths the
      }                            // compiler normally does for you
   }
}


calling it like this

1
2
3
   int map[8][8];
   ShowArray2D(&map[0][0], 8, 8); // pass the address of the first array
                                  // element plus dimensions 


The one disadvantage of the template approach is that a different version of the function will be instantiated for each set of array dimensions you use. Not a big deal with a small functions, or if you only use a limited number of array sizes, but it could add up. So you could make the template function a wrapper of the "linear" function.

1
2
3
4
5
template<std::size_t X, std::size_t Y>
void ShowArray(int (&arr)[X][Y])
{
    ShowArray2D(&arr[0][0], X, Y);
}


Andy
Last edited on Aug 18, 2013 at 10:55am
Topic archived. No new replies allowed.