Thi G.
Thi G.

Reputation: 1658

Passing dynamically allocated array as a parameter in C

So... I have a dynamically allocated array on my main:

int main()
{
    int *array;
    int len;

    array = (int *) malloc(len * sizeof(int));
    ...
    return EXIT_SUCCESS;
}

I also wanna build a function that does something with this dynamically allocated array. So far my function is:

void myFunction(int array[], ...)
{
   array[position] = value;
}

If I declare it as:

void myFunction(int *array, ...);

Will I still be able to do:

array[position] = value;

Or I will have to do:

*array[position] = value;

...?

Also, if I am working with a dynamically allocated matrix, which one is the correct way to declare the function prototype:

void myFunction(int matrix[][], ...);

Or

void myFunction(int **matrix, ...);

...?

Upvotes: 5

Views: 25459

Answers (4)

aducore
aducore

Reputation: 31

Yes, please use array[position], even if the parameter type is int *array. The alternative you gave (*array[position]) is actually invalid in this case since the [] operator takes precedence over the * operator, making it equivalent to *(array[position]) which is trying to dereference the value of a[position], not it's address.

It gets a little more complicated for multi-dimensional arrays but you can do it:

int m = 10, n = 5;

int matrixOnStack[m][n];
matrixOnStack[0][0] = 0;      // OK
matrixOnStack[m-1][n-1] = 0;  // OK
// matrixOnStack[10][5] = 0;  // Not OK. Compiler may not complain
                              // but nearby data structures might.

int (*matrixInHeap)[n] = malloc(sizeof(int[m][n]));
matrixInHeap[0][0] = 0;       // OK
matrixInHeap[m-1][n-1] = 0;   // OK
// matrixInHeap[10][5] = 0;   // Not OK. coloring outside the lines again.

The way the matrixInHeap declaration should be interpreted is that the 'thing' pointed to by matrixInHeap is an array of n int values, so sizeof(*matrixInHeap) == n * sizeof(int), or the size of an entire row in the matrix. matrixInHeap[2][4] works because matrixInHeap[2] is advancing the address matrixInHeap by 2 * sizeof(*matrixInHeap), which skips two full rows of n integers, resulting in the address of the 3rd row, and then the final [4] selects the fifth element from the third row. (remember that array indices start at 0 and not 1)

You can use the same type when pointing to normal multidimensional c-arrays, (assuming you already know the size):

int (*matrixPointer)[n] = matrixOnStack || matrixInHeap;

Now lets say you want to have a function that takes one of these variably sized matrices as a parameter. When the variables were declared earlier the type had some information about the size (both dimensions in the stack example, and the last dimension n in the heap example). So the parameter type in the function definition is going to need that n value, which we can actually do, as long as we include it as a separate parameter, defining the function like this:

void fillWithZeros(int m, int n, int (*matrix)[n]) {
    for (int i = 0; i < m; ++i)
        for (int j = 0; j < n; ++j)
            matrix[i][j] = 0;
}

If we don't need the m value inside the function, we could leave it out entirely, just as long as we keep n:

bool isZeroAtLocation(int n, int (*matrix)[n], int i, int j) {
    return matrix[i][j] == 0;
}

And then we just include the size when calling the functions:

fillWithZeros(m, n, matrixPointer);
assert(isZeroAtLocation(n, matrixPointer, 0, 0));

It may feel a little like we're doing the compilers work for it, especially in cases where we don't use n inside the function body at all (or only as a parameter to similar functions), but at least it works.

One last point regarding readability: using malloc(sizeof(int[len])) is equivalent to malloc(len * sizeof(int)) (and anybody who tells you otherwise doesn't understand structure padding in c) but the first way of writing it makes it obvious to the reader that we are talking about an array. The same goes for malloc(sizeof(int[m][n])) and malloc(m * n * sizeof(int)).

Upvotes: 3

Tom
Tom

Reputation: 2389

If I declare it as:

void myFunction(int *array, ...);

Will I still be able to do:

array[position] = value;

Yes - this is legal syntax.

Also, if I am working with a dynamically allocated matrix, which one is correct to declare the function prototype:

void myFunction(int matrix[][], ...);

Or

void myFunction(int **matrix, ...);

...?

If you're working with more than one dimension, you'll have to declare the size of all but the first dimension in the function declaration, like so:

void myFunction(int matrix[][100], ...);

This syntax won't do what you think it does:

void myFunction(int **matrix, ...);
matrix[i][j] = ...

This declares a parameter named matrix that is a pointer to a pointer to int; attempting to dereference using matrix[i][j] will likely cause a segmentation fault.

This is one of the many difficulties of working with a multi-dimensional array in C.

Here is a helpful SO question addressing this topic: Define a matrix and pass it to a function in C

Upvotes: 8

Mario
Mario

Reputation: 36487

No, you'd just keep using array[position] = value.

In the end, there's no real difference whether you're declaring a parameter as int *something or int something[]. Both will work, because an array definition is just some hidden pointer math.

However, there's is one difference regarding how code can be understood:

  • int array[] always denotes an array (it might be just one element long though).
  • int *pointer however could be a pointer to a single integer or a whole array of integers.

As far as addressing/representation goes: pointer == array == &array[0]

If you're working with multiple dimensions, things are a little bit different, because C forces you declare the last dimension, if you're defining multidimensional arrays explicitly:

int **myStuff1;    // valid
int *myStuff2[];   // valid
int myStuff3[][];  // invalid
int myStuff4[][5]; // valid

Upvotes: 1

datenwolf
datenwolf

Reputation: 162164

Will I still be able to do:

array[position] = value;

Yes, because the index operator p[i] is 100% identical to *(ptr + i). You can in fact write 5[array] instead of array[5] and it will still work. In C arrays are actually just pointers. The only thing that makes an array definition different from a pointer is, that if you take a sizeof of a "true" array identifier, it gives you the actual storage size allocates, while taking the sizeof of a pointer will just give you the size of the pointer, which is usually the system's integer size (can be different though).

Also, if I am working with a dynamically allocated matrix, which one is the correct way to declare the function prototype: (…)

Neither of them because those are arrays of pointers to arrays, which can be non-contigous. For performance reasons you want matrices to be contiguous. So you just write

void foo(int matrix[])

and internally calculate the right offset, like

matrix[width*j + i]

Note that writing this using the bracket syntax looks weird. Also take note that if you take the sizeof of an pointer or an "array of unspecified length" function parameter you'll get the size of a pointer.

Upvotes: 1

Related Questions