13

I'm trying to understand what "best practice" (or really any practice) is for passing a multidimensional array to a function in c is. Certainly this depends on the application, so lets consider writing a function to print a 2D array of variable size. In particular, I'm interested in how one would write the function printArry(__, int a, int b) in the following code. I have omitted the first parameter as I'm not exactly sure what that should be.

void printArry(_____, int a, int b){
/* what goes here? */
}


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

int a1=5;
int b1=6;
int a2=7;
int a2=8;

int arry1[a1][b1];
int arry2[a2][b2];

/* set values in arrays */

printArry(arry1, a1, b1);
printArry(arry2, a2, b2);

}
haccks
  • 104,019
  • 25
  • 176
  • 264
ABD
  • 209
  • 1
  • 7
  • I've posted a follow-up at: http://stackoverflow.com/questions/34560001/passing-a-multidimensional-array-of-variable-size-without-vla – ABD Jan 01 '16 at 21:00

2 Answers2

17

The easiest way is (for C99 and later)

void printArry(int a, int b, int arr[a][b]){
    /* what goes here? */
}

But, there are other ways around

void printArry(int a, int b, int arr[][b]){
    /* what goes here? */
}

or

void printArry(int a, int b, int (*arr)[b]){
    /* what goes here? */
}

Compiler will adjust the first two to the third syntax. So, semantically all three are identical.

And a little bit confusing which will work only as function prototype:

void printArry(int a, int b, int arr[*][*]);
haccks
  • 104,019
  • 25
  • 176
  • 264
  • 2
    I never knew `int arr[a][b]` was an option as a parameter; when did that appear in the standard? – abligh Jan 01 '16 at 19:21
  • @haccks. Another one is also to pass a reference to the 2D as a 1D pointer (not recommended). – stian Jan 01 '16 at 19:24
  • @stian; There are many ways. But, I recommend first one in this answer. – haccks Jan 01 '16 at 19:25
  • I've never seen the last thing arry[\*][\*] before. What does that do (or where can I read about it)? – ABD Jan 01 '16 at 19:26
  • Will the code within the function the same for all 4 ways? Can you explain the last in more detail, please? – Martin Zabel Jan 01 '16 at 19:28
  • 1
    I like the second/third examples, since only the width of a 2-D array needs to be known to use it. – Weather Vane Jan 01 '16 at 19:30
  • @Weather Vane, well you can pass the array without knowing the number of rows with this, but then how will you know when to stop printing rows? – ABD Jan 01 '16 at 19:32
  • @MartinZabel; Yes. For the explanation of last one I added a link. Accepted answer explained it in detailed. – haccks Jan 01 '16 at 19:34
  • 1
    @ABD; I attached a link to the answer. Read the accepted answer for detailed explanation. – haccks Jan 01 '16 at 19:34
  • Thanks for the link. But, the last way is not possible because `[*]` is only allowed in function prototypes and the OP actually wanted to implement the function. – Martin Zabel Jan 01 '16 at 20:26
  • @haaccks, this is useful, however I now see that none of these (since they use VLA) will compile for me since I'm using C98. Moreover, they don't even compile on C++11 since (from my brief research) VLA is optional in that standard. How would you do this in C98? – ABD Jan 01 '16 at 20:28
  • @ABD; You have to declare array as `int array1[5][6]; int array2[7][8]` and then define two functions with third parameter as `int array[5][6]` and `int array[7][8]`. – haccks Jan 01 '16 at 20:36
  • I'm not following the last part about two functions with a third parameter. Obviously, I'd like the function to work on other size arrays too. – ABD Jan 01 '16 at 20:42
  • @ABD; Well, its limitations of C89. – haccks Jan 01 '16 at 20:48
  • @haccks. I know, just thought it would be cool to collect as many possibilities as possible in your answer. – stian Jan 02 '16 at 00:24
  • 1
    The first 3 options all have identical semantic meaning , I think it would help the general reader to cover this in the question. (The first two are adjusted to `int (*arr)[b]`). I would use the first one for its self-documentary value however. Also it has been proposed for C2X to deprecate the `*` version – M.M Jan 23 '19 at 00:42
2

This is not really an answer, but extended comment to the OP's comment question, "well you can pass the array without knowing the number of rows with this, but then how will you know when to stop printing rows?"

Answer: generally, you can't, without passing the array size too. Look at this 1-D example, which breaks the array size.

#include <stdio.h>

int procarr(int array[16], int index)
{
   return array[index];
}

int main (void)
{
    int arr[16] = {0};
    printf("%d\n", procarr(arr, 100));
    return 0;
}

Program output (although all elements initialised to 0):

768

That was undefined behaviour and there was no compiler warning. C does not provide any array overrun protection, except for array definition initialisers (although such initialisers can define the array length). You have to pass the array size too, as in

#include <stdio.h>

int procarr(int array[16], size_t index, size_t size)
{
    if (index < size)
        return array[index];
    return -1;                  // or other action / flag
}

int main (void)
{
    int arr[16] = {0};
    printf("%d\n", procarr(arr, 100, sizeof arr / sizeof arr[0]));
    return 0;
}

Program output:

-1
Weather Vane
  • 33,872
  • 7
  • 36
  • 56