简体   繁体   中英

Passing 2D arrays in C

I took a hiatus from C and am just getting back into it again.

If I want to create a 2D array of doubles, I can do it two ways:

double** m_array = (double**) malloc(2*sizeof(double*));
double*  m_array = (double*)  malloc(2*sizeof(double));

OR

double array[2][2];

But, when I wish to pass the malloc'd array versus passing the other, there seems to be two conventions:

//allowed for passing in malloc'd array, but not for other array
func_m(m_array) //allowed
func_m(array) //disallowed
func_m(double** m_array)

//allowed for passing in either array; required for passing in non-malloc'd array
func(m_array) //allowed
func(array) //allowed
func(double array[][2])

In the first, I don't need any information beyond that it is a pointer to an array of pointers. But it can only be a malloc'd array.

In the second, I need to pass the length of each array that the array of double* points to. This seems silly.

Am I missing something? Thanks in advance.

The first one doesn't create a 2-D array at all. It creates an array of pointers, which apparently point nowhere. If you did initialize each pointer to be an array, that would still be an array of arrays, not a 2-D array.

Why don't you just create a 2-D array?

double* dynamicArray = malloc(rows * columns * sizeof (double));

or

double autoArray[rows][columns];

and then you can use either one with this function:

void func(double* array, size_t rows, size_t columns);

For clarification, suppose you declare:

int my_static_array[row][col];

What is the type of my_static_array vs the malloc'd array (let's call it my_malloc array) in pmg's answer? my_static_array is a contiguous row*col*sizeof(int) number of bytes in memory, with an int* pointer to its beginning. When computing my_static_array[x][y], the compiler knows to do (because they are stored in row-major order ):

*(addr(my_static_array) + [sizeof(int)*col*x] + [sizeof(int)*y])

This is why I need to pass in the col value for C:

(1) func(int my_static_array[][col])
(2) func(int my_static_array[][])

In (1), the compiler knows how to compute the address properly for the static array. In (2), the compiler does not have enough information. This is why (1) compiles, and (2) will not.

If it is the malloc'd array, however, one can pass:

func(int** my_malloc_array)

Since it is a pointer to a contiguous array of 1D arrays, the compiler needs no help. my_malloc_array[x][y] is done simply:

*(*(addr(my_malloc_array)+x)+y)

The compiler needs no other information about its dimensionality in order to compute.

So, what is the lesson? 2D arrays are NOT the same as an array of pointers to 1D arrays. The former has type int (*my_static_array)[row*col] -- pointer to an array of row*col elements, the latter has type int** -- pointer to an array of row pointers.

Your allocation for the 2D array is wrong. The usual 2D way is

double **m_array = malloc(NROWS * sizeof *m_array);
for (i = 0; i < NROWS; i++) {
  m_array[i] = malloc(NCOLS * sizeof *m_array[i]);
}

When passing arrays or pointers (any dimensions) to functions you have to let the function know the dimensions: they are not encoded within the array or pointers themselves.

On one hand, you have pointers (to pointers (to pointers (...))) to some and on the other hand you have arrays (of arrays (of arrays (...))) of some type.

When you pass a pointer to a function, that's exactly what happens. When you "pass an array", what happens is that the array decays into a pointer to its first element. So

foo(array); // is the same as foo(&(array[0]));

and when array is a multidimensional array the type of the decayed pointer is pointer to array[size] of SOMETHING (but you can't use that size!).

The easy way out is to write your functions with a simple pointer and pass the multiplication of all dimensions

double array[3][4][5];
foo(&(array[0][0][0]), 3*4*5);

Suppose the computer memory is a warehouse full of sequentially numbered boxes. When you do

int matrix[10][3];

you reserve 30 boxes for holding matrix values (let's say boxes 131 to 160 inclusive).

Now, lets say you want to sum all the values in the matrix in a function:

int matrix_sum(int matrix[10][3]) { /* ... */ }

but all the function receives (mandated by the Standard) is a pointer to the first element of matrix: a pointer to an array of 3 int s. So the function knows boxes 131, 132, and 133 belong to the matrix, but it doesn't know how long the matrix really is.

Use a simple typedef:

typedef double Twodouble_t[2];

void func(Twodouble_t *ptr)
{
  size_t i, numelements = sizeof*ptr/sizeof**ptr;
  puts("first array");
  for( i=0; i<numelements; ++i )
    printf("\n%f",ptr[0][i]);
}
...
Twodouble_t array[]={ {1., 1.},{2., 2.},{3.,3.} };
func(array);
...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM