I am trying to pass a two-dimensional array to a function. I am not having trouble to pass it to the function. But I am having trouble in understanding the logic behind this. The functions and main definition goes like this:
// Function to print the two-dimensional array
void print(int x, int y, int a[x][y]){
printf("\n");
int i, j;
for(i = 0; i < x; i++){
for(j = 0; j < y; j++)
printf("%d ", a[i][j]);
printf("\n");
}
}
// Function to initialize the two-dimensional array
void init_2d(int *a, int x, int y){
int i, j;
for(i = 0; i < x; i++){
for(j = 0; j < y; j++){
a[i*y + j] = i + j;
}
printf("\n");
}
}
int main(){
int m = 2, n = 3;
int a[m][n]; // a two dimensional whose size has been defined using m and n
init_2d(a, m, n);
print(m, n, a);
}
Ironically everything is working fine. And that is my problem as I am not able to digest the logic of it.
The main problems are:
- What I read in books was that the size of the two-dimensional arrays should be defined using constants or symbolic constants. In my main I am defining 2-D array using variables
mandnand yet it works fine. Why? - I was also told to pass the two-dimensional array by decaying it into a single dimensional array (by defining it as a pointer to int in function) i.e. the way I have done it in function
init_2d. But in theprintfunction I am doing it using a two dimensional array whose size has been defined using variablesxandy. Is it fine to do so? - Can a two-dimensional array be traversed using a pointer to a pointer as well?
Can anybody suggest me a good read on this topic which can clear all my concepts?
I am using codeblocks to compile my code and the compiler is GNU GCC Compiler.
6 Answers 6
Yes and no. What you read in books is true for the older, original version of C language specification - C89/90. Since C99 version of C language compilers support so called Variable Length Arrays (VLAs), whose size can be specified by run-time values. You inadvertently used that C99-specific feature of the language, which is apparently supported by your compiler in its default mode. If you ask your compiler to switch to the strict C89/90 mode your code will fail to compile specifically because you used non-constants to specify array sizes.
Note that even in C99 VLAs are supported only in certain contexts, like local arrays and function parameter declarations. You will not be able to declare a static VLA or a VLA that is a member of a struct type. In those contexts you are still required to use constant array sizes even in C99.
Whoever told you to decay 2D arrays to 1D arrays was very wrong. The way you use
init_2din your program is invalid: you are not allowed to "reinterpret" a 2D array as a 1D array by yourself. Any C compiler will immediately complain about yourinit_2d(a, m, n);call, since this call attempts to pass 2D
intarray argument for anint *parameter. This is illegal. To silence the compiler you'd have to doinit_2d((int *) a, m, n);It is quite possible that your
init_2dwill "work as expected", but still there's no good reason to use hacks like that.What can really be done here is decaying 2D array to a pointer to a 1D array. Believe it or not, this is exactly what you already did in your
printdeclaration, even if it is not immediately noticeable. Yourvoid print(int x, int y, int a[x][y])declaration is actually equivalent to
void print(int x, int y, int (*a)[y])declaration. I.e.
ais actually a pointer to typeint [y]- a pointer to a 1Dintarray of sizey. The above two declarations are just two superficially different ways to say the same thing.Note also that are not required to pass 2D arrays to functions by "decaying them" to anything. You can pass a 2D array by pointer to the entire 2D array, as in
void print(int x, int y, int (*a)[x][y])In this case to pass the array you have to use
&operatorprint(m, n, &a);and to access the array inside the function you have to use
*operatorprintf("%d ", (*a)[i][j]);No, in your case it can't be. A built-in 2D array cannot be traversed by pointer-to-pointer. BTW, as you already figured out in your
init_2dimplementation (which "works", even though it is illegal), a 2D array is internally implemented as a 1D array with automatic 2D-to-1D index recalculation. Pointer-to-pointer won't help you to traverse such "flat" linear data.Pointer-to-pointer types (or, more generally, multi-level pointer types) are often used when working with a completely different kind of multi-dimensional arrays: when an N-dimensional array is implemented as an array of pointers to independently allocated (N-1)-dimensional arrays. This kind of multi-dimensional array is also used quite often in C programs, but it has to be implemented and managed manually (you can find quite a few examples here on SO). Meanwhile, as I said above, built-in language arrays are implemented in a completely different fashion and they cannot be traversed by multi-level pointers.
3 Comments
(**a) pointer. However, it is not possible to simply allocate a "flat" block of memory for such an array. An array implemented through (**a) pointer has to have two-tiered structure. This structure has to be built manually. And it will be completely different from and incompatible to a built-in 2D array. See here for example: stackoverflow.com/questions/2842135/… VLAs (variable length arrays) exist in some versions of C. VLAs allow for allocating a fixed-length array on the stack using runtime-assigned variables, versus on the heap via
malloc()or similar function.I have never seen the array declaration syntax used in
print()before, so I can't answer that. Typically one would leave the brackets empty, or use a decayed pointer.yes.
Comments
- ISO C90 forbids variable length array, but your compiler is newer, so it is okay. It will fail if you compile with -ansi -pedantic -Wall flags
- This will also fail if you compile with -ansi -pedantic -Wall flags (ISO C90 doesn't like it either). Your newer compiler is fine with it though.
- Yes, you can use a pointer to a pointer to traverse the 2d array (In fact, char** argv parameter for
mainis an example)
It looks like the books you are reading conform to the C90 standard. Newer versions of gcc are okay though.
Comments
- VLA.
init_2dtakes aint *, but you have passed it aint (*)[n]. However, sinceais an array of an array, the address ofa[0]is the same as the address ofa[0][0], which is whyinit_2d"works". Your compiler should have warned you about a type incompatibility when you passedatoinit_2d. If it did not, you should complain to your compiler vendor.printuses VLA for the third parameter.If you mean "Can I traverse
awith anint **, the answer is "not in a straightforward manner". The reason is that if you haveint **p, you can only realistically traverseaifpis initialized like this:int a[n][m]; int *ap = &a[0][0]; int **p = ≈But, then you are just traversing
aviaapby dereferencingp. A pointer to a pointer is usually used to represent an array of pointers.int *x[10]; int **p = x;But, your
ais not an array of pointers.
Comments
Memory allocation in C arrays is contiguous, that is why expression a[i][j] (init_2d) can be assimilated to a[i*y + j] (print).
a as int** (2-dim : N x M) can be seen as a int* one-dimensional array as well (size : N*M)...
See good others answers for 1. 2. and 3.
See : Row-major order (C-style), so 2. is fine.
Note : Apparently, what I'm saying here is only true with recent versions of the C programming language and/or some specific context and is absolutely not generalizable. So better read the several other answers first.
Comments
1) m and n in your main have already a value so your array declaration is just fine.They are used just as numeric constants. 2)An array is a pointer. So
void foo(int array[]);
void foo(int* array);
are exactly the same.
An array int a[2][2] is a 1-dimensional array with 2 pointers to int and those 2 pointers point to 2 integers. So with right playing around your first pointer(in your case a) you can access those integers.
3) look up to wikipedia. There is a good algorithm to understand better.
void print(int x, int y, int a[x][y])and is connected to VLA's. gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Variable-Length.html I got the keyword from somewhere else though.