3

The Question given to me specifically says that a functions takes an integer 2d array and size as parameters. That means, 1) im not allowed to define the size of the array at the declaration. 2) I have to pass the number of rows and cols into the function.

I did it for 1D array previously and it worked, however in the 2D array case it gives a syntax error. I made test codes to check the logic of the syntax. This is the 1D array logic that works. Also this module requires me only to use stido.h library in C. The reason for passing the size of array size is to fill the array in a different function where the loop conditions will take the array size to go through indexes

#include<stdio.h>
void print(int table[],int r);
int main()
{   int r=7,table[r];
    print(table,r);
}

 void print(int table[],int r)
{  printf("table rows is%d\n",r);
} 

However this same logic doesnt work for when I try with a 2D array.(gives a syntax error)

#include<stdio.h>
void print(int table[][],int c,int r);
int main()
{int c=7,r=7,table[c][r];
    print(table,c,r);
}

void print(int table[][],int c,int r)
{printf("table rows is\ntable cols is\n",r,c)
}

Can someone kindly explain why this is happening, and suggest a solution for this problem. ? Thanks in advance

4 Answers4

5

You can't have a 2D array with unspecified dimensions. However, it seems that you have a C99+ compiler and therefore can use a variable-length array. However, you need to reorder the arguments too.

Notice that I use size_t here instead of int for the indices/dimensions - as one might have a table (though unlikely) that has more elements than is possible to represent with an int.

#include <stdio.h>
#include <stdlib.h>

void print(size_t, size_t, int [*][*]);

int main(void) {
    size_t c = 7, r = 9;
    int table[c][r];
    print(c, r, table);
}

void print(size_t c, size_t r, int table[c][r]) {
    printf("table rows is %zu\ntable cols is %zu\n", r, c);
}

It compiles in C99, (and those C11 compilers that support the optional VLA extension); and when run, it prints

table rows is 9
table cols is 7
4

First, you want to know why this isn't possible. It's not allowed by the language standard and the technical reason for this is the compiler cannot calculate offsets into the array when a second dimension isn't known.

A 2D array is just like a 1D array a set of values in memory one after the other. It is not, like in some other languages, an array of arrays. With a 2D array like int a[5][3], you have 5 times 3 ints in a row, all in all 15 ints. For accessing an element, the compiler will compute the offset for you, so if you write e.g. a[2][2], this is the 2*3 + 2th element (there are two rows of 3 columns to skip for the beginning of the third row).

If the compiler doesn't know the second dimension, this calculation of offets is impossible.

Antti Haapala's answer already shows how you can get around this using the C99 feature variable length arrays. That's a good approach, but unfortunately, with C11, this feature is optional, so there may be compilers not supporting it. If you're sure to only use compilers supporting VLAs (which is the great majority of all modern C compilers), go with the code from this answer.


When passing an array to a function, instead of the array, a pointer to its first element is passed (the array decays as a pointer). So the following declarations are the same:

void print(size_t n, int arr[]);
void print(size_t n, int *arr);

With that knowledge, you could be tempted to write it differently and do the offset calculations yourself:

void print(size_t r, size_t c, void *data)
{
    int *arr = data;
    // access arr[2][2]:
    int v = arr[2*c + 2];
}

And indeed, this is very likely to work, but be aware this is undefined: int[] and int[][] are not compatible types. For details on this idea, see this question with two quite interesting answers. Bottom line: This kind of code will work with a very high probability. Still, to be sure, don't do it....


So, if you don't have variable length arrays, and if you don't want to rely on something not perfectly well-defined, the only other way would be to actually build an array of arrays by having an array of pointers:

int row1[3] = {0, 1, 2};
int row2[3] = {3, 4, 5};
int *rows[2] = { row1, row2 };

Then you can pass it like this:

void print(size_t r, size_t c, int **arr)
{
    int a = arr[0][2]; // 2
}

Of course, this isn't a 2D array any more, it's a replacement construct that can be used in some similar fashion.

1

You can't pass a 2D array without a defined constant for the final dimension. (absent the C99 VLA additions). You can however change the order of your parameters in print so that r is defined before you declare table as a pointer to array of int [r], and pass table as follows, e.g.

#include<stdio.h>

void print (int c, int r, int (*table)[r]);

int main (void) {

    int c = 7, r = 7, table[c][r];

    for (int i = 0; i < r; i++)
        for (int j = 0; j < c; j++)
            table[i][j] = i * j + i + j;

    print (c, r, table);

    return 0;
}

void print (int c, int r, int (*table)[r])
{
    for (int i = 0; i < r; i++) {
        for (int j = 0; j < c; j++)
            printf (" %3d", table[i][j]);
        putchar ('\n');
    }
}

This method is proper for utilizing the VLA with the C99 VLA extensions.

Example Use/Output

$ ./bin/fpass2d
   0   1   2   3   4   5   6
   1   3   5   7   9  11  13
   2   5   8  11  14  17  20
   3   7  11  15  19  23  27
   4   9  14  19  24  29  34
   5  11  17  23  29  35  41
   6  13  20  27  34  41  48

If VLA extensions are not available, you can work around that by allocating and then manually indexing 2D array as a 1D array. This works because a 2D array is an array-of-arrays sequentially stored in memory (e.g. a[2][3] is a00,a01,a02,a10,a11,a12 in memory.) To prevent any undefined behavior, you can allocate storage for table (e.g. int *p = malloc (sizeof table); copy table and then index each element calling print (p, c, r) as follows, e.g.

void print (int *table, int c, int r)
{
    for (int i = 0; i < r; i++) {
        for (int j = 0; j < c; j++)
            printf (" %3d", table[i * r + j]);
        putchar ('\n');
    }
}

Where table is indexed with table[i * r + j] to access each of the original elements of the 2D array. While this method is a workaround, without the VLA extensions, you would presumably have a defined constant to pass for the final array dimension making the workaround or a change in the order of parameters unnecessary.

Look things over and let me know if you have questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Undefined behaviour though, because of incompatible types and/or reading past the last index of the *first* `int[7]` array. I would be cautious with it :D – Antti Haapala -- Слава Україні Jun 20 '17 at 07:24
  • Hmm curious, standard wise, strict alias wise, what do you see? I ask because `table[0]` in `main` is `int *` so the assignment of `*p = *table` is correct. If you pass the dimensions `c` and `r`, I don't see the UB? (not saying your are wrong, saying I don't see it) – David C. Rankin Jun 20 '17 at 07:25
  • @AnttiHaapala I see what you are saying. Were it not an array, sequential in memory, then it would run off into UB. Understood. With an array itself, no UB results. – David C. Rankin Jun 20 '17 at 07:36
  • 1
    I think that the point made by @AnttiHaapala is that `p` points to an array object of 7 elements, and attempts to access beyond these 7 are UB. Further, you may be interested in [this example of UB from Annex J](http://port70.net/~nsz/c/c11/n1570.html#J.2): "An array subscript is out of range, even if an object is apparently accessible with the given subscript (as in the lvalue expression a[1][7] given the declaration int a[4][5]) (6.5.6)." Or in [this question and related discussion](https://stackoverflow.com/questions/3766074/is-unsigned-char-a45-a17-undefined-behavior). – ad absurdum Jun 20 '17 at 12:47
  • @DavidBowling Yes, I did see that, once the light-bulb winked on... To avoid the UB, you would need to create and pass an allocated object with a copy of `table` (e.g. `int *p = malloc (sizeof table); memcpy (p, table, sizeof table);`. Then passing `p` to print as shown above would not be subject to UB. (I'll update the answer shortly) – David C. Rankin Jun 20 '17 at 23:06
0

First of all, the type of your function argument decays to a pointer to the first element of the array. That is, the following declaration pairs are perfectly equivalent:

void f(int a[]);
void f(int *a);

void g(int a[][7]);
void g(int (*a)[7]);

void h(int a[][7][6]);
void h(int (*a)[7][6]);

Now, when you index into an array, that operation is in fact defined in terms of pointer arithmetic. So, the expression a[3] is in fact equivalent to *(a + 3).

In this expression, a yields a pointer to the first element of the array, and the addition adds the offset to skip three elements of the array. That is, the machine will perform this calculation: *(elementType*)((char*)a + 3*sizeof(elementType))

The crucial part in this is the sizeof(elementType): If the type of a is int[][], then the type of its elements is int[], and you cannot take the size of an array of unknown length for obvious reasons. Consequently, the types of all array dimensions except the outermost one must be known in order to be able to index into the multidimensional array.


So much for the explanation of what's going on. The consequence for you is, that you have to supply the size of the inner dimension. As the other answers have shown, this can be done by simply ordering the size arguments first, so they are available to the declaration of the array argument:

void print(int c, int r, int table[c][r]);

Or, equivalently, as the value of c is actually ignored in the declaration above:

void print(int rowCount, int columnCount, int (*table)[columnCount]) {
    printf("table has %d columns and %d rows\n", columnCount, rowCount);

    for(int curRow = 0; curRow < rowCount; curRow++) {
        for(int curColumn = 0; curColumn < columnCount; curColumn++) {
            printf("%d ", table[curRow][curColumn]);
        }
        printf("\n");
    }
}

(I have taken the liberty to use proper formatting and variable names in this last example. Please, never use just a single character for variables names as vital as array dimension sizes.)

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106