Related
I am not very good at C and I am really confused about double array. Below is an outline of a code I have a question about. Main function calls CreateRandConn function and passes it a 2D array filled with 0 as an argument. CreateRandConn function takes a 2D array as a parameter, changes some of the value in 2DArray from 0 to 1 and returns the changed 2DArray back to main. I want to indicate in the function prototype the return type of CreateRandConn function is a 2D array. How do I indicate that? I don't really understand the syntax. Is what I wrote wrong? Is the way I am passing the 2DArray as a parameter in the function header incorrect? If so, how I do write it? I still get confused about the relationship between pointers and double arrays. Can someone explain it with the below code outline? Hopefully someone knows what my question is...
//Function prototype
int ** CreateRandConn(char * RandRoom[7], int my2DArray[7][7], char * room_dir);
//Function
int ** CreateRandConn(char * RandRoom[7], int my2DArray[7][7], char * room_dir)
{
...
return my2DArray;
}
int main()
{
int 2DArray[7][7] = {0};
2DArray = CreateRandConn(RandRoomArray, my2DArray[7][7], room_dir);
return 0;
}
I don't really understand the syntax.
Ok, so let's recap the basics:
One cannot assign to an array variable.
If an array gets passed to a function it "decays" to a pointer to its 1st element.
A multidimensional array is just an array of arrays. So a 2D-array is a 1D-array of 1D-arrays, a 3D-array is a 1D-array of 2D-arrays, a 4D-array is a 1D-array of 3D-arrays, and so on ...
A pointer p to an array of N elements of type T is to be defined as: T (*p)[N]
Now for you example:
You have
int 2DArray[7][7] = ...;
for the sake of clarity of the following explanations I change this to be
int a[5][7] = ...;
So this then is passed to a function. Where the following happens/applies:
Following 1. above, it is not possible to pass an array, as if it were possible one would assign it to the variable inside the function, as arrays cannot be assigned, one cannot pass an array.
Following 2. above, the function would need to define the related variable as "a pointer to the arrays 1st element".
Following 3. above, the a's 1st element is an int [7]
Following 4. above, a pointer to an int[7] will be defined as: int(*)[7].
So the function's relevant variable would look like:
... func(int (*pa)[7])
pa points to the 1st element of a. As a side note: From this pointer a the function cannot derive how many elements a actually "provides", will say: how many valid element after the one a points to will follow, so this needs to be passed to the function as well:
... func(int (*pa)[7], size_t rows)
From the steps so far we learned, that an array is not passed, but just a pointer to it's 1st element *1 is passed, is copied into the function's local variable (pa here).
From this directly follows that an array cannot be passed back as the function's return value, but just a pointer to an array's element (typically the 1st)
Looking at how a pointer to an array is defined: T (*p)[N] we know need to derive how a function returning a pointer to an array would look. The function's defalcation somewhat needs to become the p above. So taking T as int and N as 7 we then get:
int (*func(int (*pa)[7], size_t rows))[7];
The trivial implementation and usage then would be:
#include <stdlib.h> /* for size_t */
#define ROWS (5)
#define COLS (7)
int (*func(int (*pa)[COLS], size_t rows))[COLS];
int (*func(int (*pa)[COLS], size_t rows))[COLS]
{
for (size_t i = 0; i < rows; ++i)
{
for (size_t j = 0; j < COLS; ++j)
{
pa[i][j] = 0;
}
}
return pa;
}
int main(void)
{
int a[ROWS][COLS];
int (*pa)[COLS] = func(a, ROWS);
return EXIT_SUCCESS;
}
*1
(which sloppy, but wrongly spoken often is referred to as "a pointer to an array is passed", which in general it is not, but just here, as it's a 2D-array, will say the array's elements are arrays themselves).
If you understood the above, then just for completeness following a less strange looking (but also probably less educational ;-)) version of the above function declaration. It may be declared by using a typedef construct hiding away the somehow complicated declaration of the array-pointers as parameter and return type.
This
typedef int (*PA)[COLS];
defines a type pointing a an array of COLS of ints.
So using PA we can instead of
int (*func(int (*pa)[COLS], size_t rows))[COLS];
write
PA func(PA pa, size_t rows))[COLS];
This version is identical to the above.
And yes it looks simpler, but brings along the fact, that pointers pa and the function's return value) are not identifiable as being pointers by just looking at their definition. Such constructs are considered "bad practice" by many fellow programmers.
There are tons of similar questions, but still I could not find any answer relevant for the feature of variable length arrays in C99/C11.
How to pass multidimensional variable length array to a function in C99/C11?
For example:
void foo(int n, int arr[][]) // <-- error here, how to fix?
{
}
void bar(int n)
{
int arr[n][n];
foo(n, arr);
}
Compiler (g++-4.7 -std=gnu++11) says:
error: declaration of ‘arr’ as multidimensional array must have bounds for all dimensions except the first
If I change it to int *arr[], compiler still complains:
error: cannot convert ‘int (*)[(((sizetype)(((ssizetype)n) + -1)) + 1)]’ to ‘int**’ for argument ‘2’ to ‘void foo(int, int**)’
Next question, how to pass it by value and how to pass it by reference? Apparently, usually you don't want the entire array to be copied when you pass it to a function.
With constant length arrays it's simple, since, as the "constant" implies, you should know the length when you declare the function:
void foo2(int n, int arr[][10]) // <-- ok
{
}
void bar2()
{
int arr[10][10];
foo2(10, arr);
}
I know, passing arrays to functions like this is not a best practice, and I don't like it at all. It is probably better to do with flat pointers, or objects (like std:vector) or somehow else. But still, I'm a bit curios what is the answer here from a theoretical standpoint.
Passing arrays to functions is a bit funny in C and C++. There are no rvalues of array types, so you're actually passing a pointer.
To address a 2D array (a real one, not array of arrays), you'll need to pass 2 chunks of data:
the pointer to where it starts
how wide one row is
And these are two separate values, be it C or C++ or with VLA or without or whatnot.
Some ways to write that:
Simplest, works everywhere but needs more manual work
void foo(int width, int* arr) {
arr[x + y*width] = 5;
}
VLA, standard C99
void foo(int width, int arr[][width]) {
arr[x][y] = 5;
}
VLA w/ reversed arguments, forward parameter declaration (GNU C extension)
void foo(int width; int arr[][width], int width) {
arr[x][y]=5;
}
C++ w/ VLA (GNU C++ extension, terribly ugly)
void foo(int width, int* ptr) {
typedef int arrtype[][width];
arrtype& arr = *reinterpret_cast<arrtype*>(ptr);
arr[x][y]=5;
}
Big remark:
The [x][y] notation with a 2D array works because the array's type contains the width. No VLA = array types must be fixed at compile-time.
Hence: If you can't use VLA, then...
there's no way to handle it in C,
there's no way to handle it without a proxy class w/ overloaded operator overloading in C++.
If you can use VLA (C99 or GNU C++ extensions), then...
you're in the green in C,
you still need a mess in C++, use classes instead.
For C++, boost::multi_array is a solid choice.
A workaround
For 2D arrays, you can make two separate allocations:
a 1D array of pointers to T (A)
a 2D array of T (B)
Then set the pointers in (A) to point into respective rows of (B).
With this setup, you can just pass (A) around as a simple T** and it will behave well with [x][y] indexing.
This solution is nice for 2D, but needs more and more boilerplate for higher dimensions. It's also slower than the VLA solution because of the extra layer of indirection.
You may also run into a similar solution with a separate allocation for every B's row. In C this looks like a malloc-in-a-loop, and is analogous of C++'s vector-of-vectors. However this takes away the benefit of having the whole array in one block.
There is no clear cut way for doing this but you can use a workaround to treat a 2 dimensional array as a one dimensional array and then reconvert it to a two dimensional array inside the function.
void foo2(int n, int *arr)
{
int *ptr; // use this as a marker to go to next block
int i;
int j;
for(i = 0; i < n; i++)
{
ptr = arr + i*n; // this is the starting for arr[i] ...
for (j = 0; j < n ;j++)
{
printf(" %d ", ptr[j]); // This is same as arr[i][j]
}
}
}
void bar2()
{
int arr[10][10];
foo2(10, (int *)arr);
}
There are tons of similar questions, but still I could not find any answer relevant for the feature of variable length arrays in C99/C11.
How to pass multidimensional variable length array to a function in C99/C11?
For example:
void foo(int n, int arr[][]) // <-- error here, how to fix?
{
}
void bar(int n)
{
int arr[n][n];
foo(n, arr);
}
Compiler (g++-4.7 -std=gnu++11) says:
error: declaration of ‘arr’ as multidimensional array must have bounds for all dimensions except the first
If I change it to int *arr[], compiler still complains:
error: cannot convert ‘int (*)[(((sizetype)(((ssizetype)n) + -1)) + 1)]’ to ‘int**’ for argument ‘2’ to ‘void foo(int, int**)’
Next question, how to pass it by value and how to pass it by reference? Apparently, usually you don't want the entire array to be copied when you pass it to a function.
With constant length arrays it's simple, since, as the "constant" implies, you should know the length when you declare the function:
void foo2(int n, int arr[][10]) // <-- ok
{
}
void bar2()
{
int arr[10][10];
foo2(10, arr);
}
I know, passing arrays to functions like this is not a best practice, and I don't like it at all. It is probably better to do with flat pointers, or objects (like std:vector) or somehow else. But still, I'm a bit curios what is the answer here from a theoretical standpoint.
Passing arrays to functions is a bit funny in C and C++. There are no rvalues of array types, so you're actually passing a pointer.
To address a 2D array (a real one, not array of arrays), you'll need to pass 2 chunks of data:
the pointer to where it starts
how wide one row is
And these are two separate values, be it C or C++ or with VLA or without or whatnot.
Some ways to write that:
Simplest, works everywhere but needs more manual work
void foo(int width, int* arr) {
arr[x + y*width] = 5;
}
VLA, standard C99
void foo(int width, int arr[][width]) {
arr[x][y] = 5;
}
VLA w/ reversed arguments, forward parameter declaration (GNU C extension)
void foo(int width; int arr[][width], int width) {
arr[x][y]=5;
}
C++ w/ VLA (GNU C++ extension, terribly ugly)
void foo(int width, int* ptr) {
typedef int arrtype[][width];
arrtype& arr = *reinterpret_cast<arrtype*>(ptr);
arr[x][y]=5;
}
Big remark:
The [x][y] notation with a 2D array works because the array's type contains the width. No VLA = array types must be fixed at compile-time.
Hence: If you can't use VLA, then...
there's no way to handle it in C,
there's no way to handle it without a proxy class w/ overloaded operator overloading in C++.
If you can use VLA (C99 or GNU C++ extensions), then...
you're in the green in C,
you still need a mess in C++, use classes instead.
For C++, boost::multi_array is a solid choice.
A workaround
For 2D arrays, you can make two separate allocations:
a 1D array of pointers to T (A)
a 2D array of T (B)
Then set the pointers in (A) to point into respective rows of (B).
With this setup, you can just pass (A) around as a simple T** and it will behave well with [x][y] indexing.
This solution is nice for 2D, but needs more and more boilerplate for higher dimensions. It's also slower than the VLA solution because of the extra layer of indirection.
You may also run into a similar solution with a separate allocation for every B's row. In C this looks like a malloc-in-a-loop, and is analogous of C++'s vector-of-vectors. However this takes away the benefit of having the whole array in one block.
There is no clear cut way for doing this but you can use a workaround to treat a 2 dimensional array as a one dimensional array and then reconvert it to a two dimensional array inside the function.
void foo2(int n, int *arr)
{
int *ptr; // use this as a marker to go to next block
int i;
int j;
for(i = 0; i < n; i++)
{
ptr = arr + i*n; // this is the starting for arr[i] ...
for (j = 0; j < n ;j++)
{
printf(" %d ", ptr[j]); // This is same as arr[i][j]
}
}
}
void bar2()
{
int arr[10][10];
foo2(10, (int *)arr);
}
I'm a bit confused at the difference here, in C99:
int myfunc (int array[n], int n) { ... }
will not compile. As far as I know you must always put the reference to the array size first, so it has to be written:
int myfunc (int n, int array[n]) { ... }
But if you supply the static keyword, this works absolutely fine:
int myfunc (int array[static 1], int n) { ... }
This order if far preferable to me, as I'm used to having arrays come first in a function call, but why is this possible?
Edit: Realising that the third example isn't actually a VLA helps...
For reference, this was the piece of code I was looking at that led to the question:
int sum_array(int n, int m, int a[n][m])
{
int i, j, sum = 0;
for (i = 0; i < n; i++)
for (j = 0; j < m; j++)
sum += a[i][j];
return sum;
}
The reason why
int myfunc (int n, int array[n]) { ... }
is valid and
int myfunc (int array[n], int n) { ... }
is not is due to the lexical scoping rules of C. An identifier cannot be used before it has been introduced in the scope. There are a few exceptions to this rule but this one is not one of them.
EDIT: here is the relevant paragraph of the C Standard:
(C99, 6.2.1p7) "Any other identifier has scope that begins just after the completion of its declarator."
This rule also applies to parameters declaration at function prototype scope.
The reason for error has already been explained to you: you have to declare n before you can use it in other declarations.
However, it is worth noting that none of these declarations actually declare variable length arrays, as you seem to believe.
It is true that syntax with [n] was first allowed in C99 and that it is formally a VLA declaration, but nevertheless in the given context all of these declarations declare array as a parameter of int * type, just like it has always been in C89/90. The [n] part is not a hint of any kind. The fact that you can use [n] in this declaration is indeed a side-effect of VLA support, but this is where any relationship with VLA ends. That [n] is simply ignored.
A "hint" declaration requires keyword static inside the []. So, your declaration with [static 1] is equivalent to classic int array[1] declaration (meaning that 1 is ignored and the parameter has type int *) except that it gives the compiler a hint that at least 1 element must exist at the memory location pointed by array.
It's because arrays must be declared with a constant value so you cannot create an array using a variable size and therefore cannot pass an array with a variable size. Also if it is just a single-dimension array you don't need to pass a value in at all, that is the point of passing in the second parameter to tell you the length of your array.
To get this to work properly just write the function header like this:
int myfunc (int myArray[], int n) {...}
The order shouldn't matter, but you cannot have the size of an array you are passing be variable it must be a constant value.
If you are using GCC and are willing to use some of their extensions, you can accomplish what you wish right here:
int myFunc (int len; /* notice the semicolon!! */ int data[len], int len)
{
}
The documentation for this extension (Variable Length Arrays) is here.
Please note that this extension is NOT available in clang for some reason, I'm not quite sure why, though.
EDIT: Derp, scope, of course.
My question is; why do you need to do it at all? You're really getting a pointer anyway (you can't pass arrays to a function in C, they degrade to a pointer, regardless of the function's signature). It helps to let the caller know the expected size of the input, but beyond that it is useless. Since they are already passing the size, just use...
int myfunc(int arr[], size_t size) {
// ...
}
Or
int myfunc(int *arr, size_t size) {
// ...
}
So I have some code that looks like this:
int a[10];
a = arrayGen(a,9);
and the arrayGen function looks like this:
int* arrayGen(int arrAddr[], int maxNum)
{
int counter=0;
while(arrAddr[counter] != '\0') {
arrAddr[counter] = gen(maxNum);
counter++;
}
return arrAddr;
}
Right now the compilier tells me "warning: passing argument 1 of ‘arrayGen’ makes integer from pointer without a cast"
My thinking is that I pass 'a', a pointer to a[0], then since the array is already created I can just fill in values for a[n] until I a[n] == '\0'. I think my error is that arrayGen is written to take in an array, not a pointer to one. If that's true I'm not sure how to proceed, do I write values to addresses until the contents of one address is '\0'?
The basic magic here is this identity in C:
*(a+i) == a[i]
Okay, now I'll make this be readable English.
Here's the issue: An array name isn't an lvalue; it can't be assigned to. So the line you have with
a = arrayGen(...)
is the problem. See this example:
int main() {
int a[10];
a = arrayGen(a,9);
return 0;
}
which gives the compilation error:
gcc -o foo foo.c
foo.c: In function 'main':
foo.c:21: error: incompatible types in assignment
Compilation exited abnormally with code 1 at Sun Feb 1 20:05:37
You need to have a pointer, which is an lvalue, to which to assign the results.
This code, for example:
int main() {
int a[10];
int * ip;
/* a = arrayGen(a,9); */
ip = a ; /* or &a[0] */
ip = arrayGen(ip,9);
return 0;
}
compiles fine:
gcc -o foo foo.c
Compilation finished at Sun Feb 1 20:09:28
Note that because of the identity at top, you can treat ip as an array if you like, as in this code:
int main() {
int a[10];
int * ip;
int ix ;
/* a = arrayGen(a,9); */
ip = a ; /* or &a[0] */
ip = arrayGen(ip,9);
for(ix=0; ix < 9; ix++)
ip[ix] = 42 ;
return 0;
}
Full example code
Just for completeness here's my full example:
int gen(int max){
return 42;
}
int* arrayGen(int arrAddr[], int maxNum)
{
int counter=0;
while(arrAddr[counter] != '\0') {
arrAddr[counter] = gen(maxNum);
counter++;
}
return arrAddr;
}
int main() {
int a[10];
int * ip;
int ix ;
/* a = arrayGen(a,9); */
ip = a ; /* or &a[0] */
ip = arrayGen(ip,9);
for(ix=0; ix < 9; ix++)
ip[ix] = 42 ;
return 0;
}
Why even return arrAddr? Your passing a[10] by reference so the contents of the array will be modified. Unless you need another reference to the array then charlies suggestion is correct.
Hmm, I know your question's been answered, but something else about the code is bugging me. Why are you using the test against '\0' to determine the end of the array? I'm pretty sure that only works with C strings. The code does indeed compile after the fix suggested, but if you loop through your array, I'm curious to see if you're getting the correct values.
I'm not sure what you are trying to do but the assignment of a pointer value to an array is what's bothering the compiler as mentioned by Charlie. I'm curious about checking against the NUL character constant '\0'. Your sample array is uninitialized memory so the comparison in arrayGen isn't going to do what you want it to do.
The parameter list that you are using ends up being identical to:
int* arrayGen(int *arrAddr, int maxNum)
for most purposes. The actual statement in the standard is:
A declaration of a parameter as "array of type" shall be adjusted to "qualified pointer to type", where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation. If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.
If you really want to force the caller to use an array, then use the following syntax:
void accepts_pointer_to_array (int (*ary)[10]) {
int i;
for (i=0; i<10; ++i) {
(*ary)[i] = 0; /* note the funky syntax is necessary */
}
}
void some_caller (void) {
int ary1[10];
int ary2[20];
int *ptr = &ary1[0];
accepts_pointer_to_array(&ary1); /* passing address is necessary */
accepts_pointer_to_array(&ary2); /* fails */
accepts_pointer_to_array(ptr); /* also fails */
}
Your compiler should complain if you call it with anything that isn't a pointer to an array of 10 integers. I can honestly say though that I have never seen this one anywhere outside of various books (The C Book, Expert C Programming)... at least not in C programming. In C++, however, I have had reason to use this syntax in exactly one case:
template <typename T, std::size_t N>
std::size_t array_size (T (&ary)[N]) {
return N;
}
Your mileage may vary though. If you really want to dig into stuff like this, I can't recommend Expert C Programming highly enough. You can also find The C Book online at gbdirect.
Try calling your parameter int* arrAddr, not int arrAddr[]. Although when I think about it, the parameters for the main method are similar yet that works. So not sure about the explanation part.
Edit: Hm all the resources I can find on the internet say it should work. I'm not sure, I've always passed arrays as pointers myself so never had this snag before, so I'm very interested in the solution.
The way your using it arrayGen() doesn't need to return a value. You also need to place '\0' in the last element, it isn't done automatically, or pass the index of the last element to fill.
#jeffD
Passing the index would be the preferred way, as there's no guarantee you won't hit other '\0's before your final one (I certainly was when I tested it).