3

I'm having problems passing a multidimensional array to a function in C. Both in the typedef and what the argument should be.

Currently, my function def looks like this:

int foo(char *a, char b[][7], int first_dimension_of_array);

I declare an array like this:

char multi_D_array[20][7];

When I try to call the function like this:

foo(d, multi_D_array, 20);

I get the warning that the second argument to the function is from incompatible pointer type. I've tried many of the variations that I've seen online but still can't get it correct. Also, when using GDB, I find that only the first array of the 2D array is passed in. Feedback on what I'm doing wrong would be greatly appreciated.

grassclip
  • 43
  • 3
  • 8

6 Answers6

5

Big warning: I wouldn't normally handle multi dimensional arrays this way.

I tried this just to check, but this:

#include <stdio.h>

int foo(char b[][7])
{
    printf("%d\n", b[3][4]);
    return 0;
}

int main(int argc, char** argv)
{
    char multi_d_arr[20][7];
    multi_d_arr[3][4] = 42;
    foo(multi_d_arr);
    return 0;
}

Compiles and runs without any issue whatsoever, using gcc -Wall. I'm not sure, honestly, how the second argument can be considered an incompatible pointer type. Works fine.

However, since you ask, how would I handle 2-D arrays? A better way is to use pointers like so:

char** array2d = malloc((MAX_i+1)*sizeof(char*));
for ( i = 0; i < (MAX_i+1); i++ )
{
    array2d[i] = malloc((MAX_j+1)*sizeof(char));
}

Now, just to be clear, the greatest element you can access is array2d[MAX_i][MAX_j].

Don't forget to invert the process when done:

for ( i = 0; i < (MAX_i+1); i++ )
{
    free(array2d[i]);
}
free(array2d);

Now, using this method, subscript notation remains valid, so array2d[x][y] still accesses the right value. Literally speaking, array2d is an array of pointers, and so is each array2d[i].

Why is this a better method? Well, you can have variable size sub-arrays if you so like. All you have to do is change the for-loop. This techique is often used for arrays of strings, particularly in the int main(int argc, char** argv) signature of an app. argv is an array of an array of variable-length strings. Use strlen() to determine their length.

Can you pass this around? Of course. You just received it in main, for starters, but you could also write your function signature like so:

int foo(char** ar2d, const unsigned int size);

and call it:

char** somearray;
/* initialisation of the above */

foo(ar2d, thesizeofar2d);

/* when done, free somearray appropriately */

Just to be clear, your method of including size parameters (like const unsigned int size) is very good practise and you should stick to it.

  • you can do much better using C99 Variable length arrays and declare the type of the array depending of it's run-time size. What you suggest is really **bad idea**. If sub-arrays change size, you *must* keep size for each row (or risk accessing outside not allocated subarray if it change size). That means allocating a structure with length and pointer to data and more complexe syntax. If every sub array is the same size, then allocating only one chunk of memory with one malloc is much more efficient, easier to read and less error prone. – kriss Jan 20 '11 at 07:47
  • to clarify, I mean when sub-arrays are strings the problem is completely different because the zero terminator which implicitely give the sub-array size (string size) and avoid keeping it along structure. Also you often want to store pointers to statically allocated strings. This use case is the only good one and you shouldn't generalize from it. – kriss Jan 20 '11 at 07:51
1

The declarations and function call you have given in your question is perfectly correct C, and does not give a warning with gcc.

Are you sure you've copied the problematic code exactly?

caf
  • 233,326
  • 40
  • 323
  • 462
0

Your code works fine for me? Here's my test code: http://codepad.org/wSppLiwl

Hoàng Long
  • 10,746
  • 20
  • 75
  • 124
0

Well, it is incompatible by definition.

char[20][7] is not the same as char[][7]

just get rid of the warning.

And it is not passing "only the first array of the 2D array", it is passing only one pointer to the array (pointer to the first element, or to the beginning of the array). You can see only first 7 elements in GDB because based on the type of the array inside the function, GDB is aware of only 7 elements.

sny
  • 415
  • 8
  • 21
  • 1
    They are compatible. The array `multi_D_array`, when used in the function call context, is converted into a pointer to its first element, which has type `char (*)[7]`. The parameter `char [][7]` is of exactly this type. – caf Jan 20 '11 at 03:22
0

Beginning with C99 you can have variable length arrays and do things like below. Basically the provided parameter for dimension array is used to declare the array size. This is most probably the best approach for modern compilers.

#include <stdio.h>

int foo(char *a, int fdim, char b[fdim][7]){

    printf("b[19][6] = %d\n", b[19][6]);
    return 0;
}

int main(){
    char d[10];
    char multi_D_array[20][7];
    int x, y;

    for (x = 0 ; x < 7 ; ++x){
        for (y=0 ; y < 20 ; ++y){
            multi_D_array[y][x] = y + x;
        }
    }
    foo(d, 20, multi_D_array);
}

Even if it also applies to C++, you could also be interested by reading my answers there and there (and certainly answers of others) for better understanding of C arrays.

Community
  • 1
  • 1
kriss
  • 23,497
  • 17
  • 97
  • 116
  • correct, only that just for the first dimension, this doesn't change a bit. Your declaration is completely equivalent to the one given by the OP (but for the switch of positions). Only starting from the second dimension VLA as function arguments bring something new. – Jens Gustedt Jan 20 '11 at 07:53
-2

The bottom line is that C/C++ doesn't deal well with multi-dimensional arrays. Most of the time it is easier to have it be a single-dimensional array and do the row-column addressing yourself.