1

I am trying to understand some code that passes a multi dimension array to a function. But the prototype of this function intrigues me.

The program creates this "tab" variable:

#define N 8

float tab[N][N];
tab[0][0] = 2; tab[0][1] = 3; tab[0][2] = -1;
tab[1][0] = 3; tab[1][1] = 1; tab[1][2] = -4;
tab[2][0] = 1; tab[2][1] = 2; tab[2][2] =  3;
hello(tab);

And we have this function:

function hello(float mat[][N]) {

I dont understand why the hello function gets the tab variable with an empty [] and then with [N]. What does it change ? I don't understand... Why not tab[][] ?

The code seems to have been made by a good developer so I don't think that the N variable is here for no reason.

If you can explain me this, thanks for your time !

The original code

Doctor
  • 7,115
  • 4
  • 37
  • 55
  • 2
    About half of the answer is here: http://stackoverflow.com/questions/1461432/what-is-array-decaying . From it you can infer the second half: The first dimension's size is lost when the array decays. – user4581301 Feb 07 '17 at 23:40
  • 1
    I should reword my comment. The size is not necessarily lost. It may be discarded. `float mat[][N]` elects to discard and becomes a pointer to an unknown number of arrays of length `N` – user4581301 Feb 07 '17 at 23:48
  • @user4581301 Thanks for your answer. The stackoverflow answer that you provided me is a little to high level for me to understand. I Though that the first dimension of an array contained the addresses of the starting points of each colones of the second dimension. If I loose the size of my first dimension then how could I safely get to my second dimension without getting out of the array and segfaulting !? – Doctor Feb 07 '17 at 23:51
  • @user4581301 Ok but it doesn't change that If I can't access my first dimension safely I cannot access the second either ? – Doctor Feb 07 '17 at 23:53
  • The first dimension is not pointers to the second dimension in this sort of array. Since all of the dimension are known and constant, you will get a block of memory that is `N` x `N` floats long and the compiler hides the indexing math to traverse it. Once you run off the end of the first row, you will enter the second row. You can get huge caching advantages over the dynamic array of arrays. – user4581301 Feb 08 '17 at 00:22
  • 1
    Imagine you're the compiler and see `void hello(float mat[][]) { use(mat[1][1]); }` How do you compute the memory location of `mat[1][1]` if you do not know how many columns the array has? – M.M Feb 08 '17 at 02:32

2 Answers2

1
float tab[N][N];

defines an array of N by N. I'm not going to use row or column because how the array is oriented to the program logic may have no bearing on how the array is represented in memory. Just know that it will be a block of memory N*N long that can be access with mat[0..N-1][0..N-1]. The sizes are known and constant. When you define an array, it must know its size and this size cannot change. If you do not know the size, use a std::vector or a std::vector<std::vector<YOUR TYPE HERE>>

float tab[][];

is illegal because the size of the array is unknown. The compiler has no clue how much storage to allocate to the array and cannot produce a functional (even if flawed) program.

When you pass an array to a function such as

function hello(float mat[][N])

the array decays into a pointer. More info: What is array decaying? Once the array has decayed, the size of the first dimension is lost. To safely use the array you must either already know the size of the array or provide it as another parameter. Example:

function hello(float mat[][N], size_t matLen)

In question, the size is given as N. You know it's N and you can safely call

hello(mat);

without providing any sizing and simply use N inside the function as the bounds. N is not a devious magic number, but it could be given a more descriptive name.

You can also be totally explicit and

function hello(float mat[N][N])

and remove any ambiguity along with the ability to use the function with arrays of size M by N. Sometimes it's a trade-off worth making.

Community
  • 1
  • 1
user4581301
  • 33,082
  • 7
  • 33
  • 54
0

Let me explain a little bit "untechnically", but probably comprehensive:

Think float tab[ROW][COL] as a two dimensional array of floats, where "ROW" stands for the rows, and "COL" stands for the columns, and think that the array is mapped to memory one complete row following the other, i.e.

r0c0,r0c1,r0c2
r1c0,r1c1,r1c2
r2c0,r2c1,r2c2

for ROW=3 and COL=3. Then, if the compiler would have to find out where to write tab[2][1], it would have to take 2 times the size of a row + 1 (where row size is actually the number of columns COL). So for addressing a cell, it is relevant to know the size of the row, whereas within a row one has just to add the column index. Hence, a declaration like tab[][N] is sufficient, as N defines the number of columns - i.e. the size of a row - and lets the compiler address each cell correctly.

Hope it helps somehow.

Stephan Lechner
  • 34,891
  • 4
  • 35
  • 58
  • Thanks for your answer. If I got it right the compiler doesn't need the information of how many rows are passed in the first [] ! Ok... so it doesn't change anything to the content of the array ? Its just a simplification ? – Doctor Feb 08 '17 at 00:14
  • @Doctor: Yes, it does not change the content of the array. If it is a simplification or an abstraction, I cannot say: if the type is `tab[][N]`, you can pass in any array with `N` columns, regardless of how many rows this array has. If type is `tab[R][C]`, then you can just pass in arrays with exactly `C` columns and `R` lines. – Stephan Lechner Feb 08 '17 at 07:19