I'm struggling to pass a C99 static 3D array of variable length to a function. I know this has been asked here on SO before, but I tried every solution I found so far, none of them worked. I have a following code:
int N;
int foo( ? ){
// do stuff
}
int main(){
cin >> N;
int arr[N][N][N];
foo(arr);
return 0;
}
The question is what to put instead of '?'. Among other things I tried creating a pointer to this 3D array and passing the pointer according to the answer here but that also would compile.
C++ does not have variable-style arrays. What you can do is cast the address of the array to a pointer, and pass the dimensions, or even cast to a pointer to an array of the same size but different shape, then dereference that pointer.
One C++ technique you can use to sugar this, if you are selecting between a finite number of possible shapes, is to declare a template< size_t L, size_t M, size_t N> int foo( int arr[L][M][N]). Another would be a multidimensional array class that wraps your array and its dimensions. You can then construct an object, which aliases rather than copies the array, that you can index as arr(i, j, k).
Related
I have encountered this while writing a program with matrices, I used int** m to declare my matrix - because I needed dynamic allocation and in the function i used int a[][]. I don't remember having any problem with that. But when I used a simple m[6][6] matrix and f(int**m, int** m2, rest params) I had trouble.
It compiled and when I was running the program (code blocks with GCC) it just crashed. I tried debugging by adding printf()s and it was crashing at an if() block where it made no sense to crash. Modified the first function parameter from int a[][] to int* a[6] and it moved on, modified the second param later and my program worked on first try. By a more careful debug what I was saving in int m[i][j] and checking in the if was junk value, not what I was putting in, I was just putting 1 or 0 to mark something.
After all this years, unless I get compiler error by GCC when I do stuff like this, I just write the first way that comes to mind.
What is the logic behind using int** and int [][] to declare variable/get function parameters, in all 4 combinations? Most predefined functions I worked with use int** in the function header.
I know int [][] is not equivalent to int**, it is int* [] correctly, but what are the things that i'm missing? int[][] is an multi dimensional array means array of arrays, all 3 ways of writing it seem the same. And for int[][] it almost always asks to only let the first parameter void, like for int array[][][][] I need to put int array a[][n1][n2][n3] in the function parameter, right? It needs to know the dimension for multi-dimensional arrays except for the first one, because int* and int[] can be used without problems when declaring function arguments?
What are the exact differences betwenn int **a and int a[][] as function parameters in C and C++?
int *a. This is a pointer to an int.
int **a. This is a pointer to pointer to an int.
int a[] This would be an array of unspecified number of ints in all other contexts, but as a function parameter declarator, it is adjusted to be a pointer to int i.e. in that case it is same as if you had written int *a.
int a[][] This would be an array of unspecified number of arrays of unspecified number of ints, but such type is ill-formed because the array element cannot be an array of unspecified size.
int *a[] This would be an array of unspecified number of pointers to int in all other contexts, but as a function parameter declarator, it is adjusted to be a pointer to pointer to int i.e. in that case it is same as if you had written int **a.
int (*a)[N] This is a pointer to an array of N ints.
int a[][N] This would be an array of unspecified number of arrays of N ints in all other contexts, but as a function parameter declarator, it is adjusted to be a pointer to an array of N int i.e. in that case it is same as if you had written int (*a)[N].
Some examples:
void fun_1D(int*); // argument is pointer to int
void fun_1D(int[]); // same as above
void fun_1D(int[10]); // same as above; note that 10 is ignored
int arr_1D[20]; // array of int
fun_1D(arr_1D); // implicit conversion
fun_1D(&arr_1D[0]); // same as above
void fun_2D(int (*)[20]); // note that 20 is not ignored
void fun_2D(int[][20]); // same as above
void fun_2D(int[10][20]); // same as above; note that 10 is ignored
int arr_2D[20][20]; // array of array of int
fun_2D(arr_2D); // implicit conversion
fun_2D(&arr_2D[0]); // same as above
fun_1D(arr_2D[i]); // implicit conversion
fun_1D(&arr_2D[i][0]); // same as above
void fun_ptrs(int**); // argument is pointer to pointer to int
void fun_ptrs(int*[]); // same as above
void fun_ptrs(int*[10]); // same as above; note that 10 is ignored
int *arr_ptr[20]; // array of pointers
fun_ptrs(arr_ptr); // implicit conversion
fun_ptrs(&arr_ptr[0]); // same as above
fun_1D(arr_ptr[i]); // no conversion needed
// broken examples
fun_2D(arr_ptr); // int*[20] is not int(*)[20]
fun_ptrs(arr_2D); // int[20][20] is not int**
Notice how a function parameter declared as an array is adjusted as the same pointer type to which an array will decay to upon lvalue to rvalue conversion.
Some simple rules of thumb to remember:
An array is not a pointer.
A pointer is not an array.
A function argument written as an array is actually not an array. It is actually adjusted to be a pointer to the element of such array. After this adjustement, a function argument is never an array. This does not apply to any other contexts, except for function arguments.
Not every type can be element of an array. Arrays of unspecified length are such types.
There are no objects of "array unspecified length" types. They can only be used in extern variable declarations which refer to an array defined elsewhere, or in a definition where the actual size is deduced from the initialiser of the array, or in a function parameter declaration where the array is adjusted to be a pointer to the element.
If I declare int a[6][6] in main and call a function that expects int** a, will it workd?
No, because int[6][6] is not an int** and neither does it decay to one. int[6][6] decays to int(*)[6] as I explained above. int(*)[6] and int** are not convertible to one another. One is pointer to an array, the other is pointer to a pointer.
And the other way around
No, because int[6][6] argument is adjusted to int(*)[6]. See previous paragraph for reason why these are incompatible.
seems int a[][] is not accepted
Correct. As I explained in the fourth paragraph from the top (not counting the quote).
If I have functions f1(int *a) and f2(int a[]) and f2(int a[6]) what would sizeof (a) return in those cases ?
As I explained above, all of those declare a parameter of type int*. sizeof a would be same as sizeof(int*) because that is the type.
int **a
This is pointer to pointer to int.
int a[][]
This is array of array of int but this is not valid because the dimension of the second array must be known at declaration time, i.e. the second array must be complete, as it cannot be completed afterwards, like that
int a[][DIM]
C++ inherited C's behavior of arrays decaying into a pointer.
This behavior is really helpful, until one hits a sharp edge and realizes something odd is going on, and then one tries to figure out what it does and test out the behavior and realize it is a bit crazy.
But keep calm, once you realize what it means for an array to decay into a pointer, everything makes sense again.
Here is an example to illustrate the differences.
static void func1(int** p) {
(void)p;
}
static void func2(int (&a)[2][2]) {
(void)a;
}
int main() {
// x is a 2-dimensional array of int objects.
int x[2][2] = {{10, 20}, {30, 40}};
// x[0][0] is 10, and is at 0x1000 (say, for example)
// x[0][1] is 20, and is at 0x1004
// x[1][0] is 30, and is at 0x1008
// x[1][1] is 40, and is at 0x100C
// y is a 1-dimensional array of pointers.
int* y[2] = { &x[0][0], &x[1][0] };
// y[0] is 0x1000, and is at 0x1010 (say, for example)
// y[1] is 0x1008, and is at 0x1018
// x cannot decay into an int**, because it is not an array of pointers.
// y can decay into an int**, because it is an array of pointers.
func1(y);
// x can be used for an int[2][2] reference parameter.
// y cannot be used for an int[2][2] reference parameter.
func2(x);
}
When you declare arrays in C++ you are allocating a contigious block of memory that holds the members of the array.
int data[6]; // Block of 6 integers.
This is true even for dimensional arrays.
int threeD[2][3][4]; // Allocates a Block of 24 integers.
// The compiler keeps track of the number of
// of dimensions and does the maths for you
// to calculate the correct offset.
Now when an array is passed to a function the array will decay into a pointer to the first element.
Now if you are dynamically allocating a multi-dimensional array. You tend to do this as arrays of arrays. These arrays are NOT in contiguous memory. But you need to do it this way to allow you to use the square bracket operator [] as you would do normally.
int*** dynamicThreeD = new int**[2];
for(int l1 = 0 ; l1 < 2 ; ++l1) {
dynamicThreeD[l1] = new int*[3];
for(int l2 = 0 ; l2 < 3 ; ++l2) {
dynamicThreeD[l1][l2] = new int[4];
}
}
int threedD[2][3][4];
Though these two types look the same in the way they are accessed:
dynamicThreeD[1][2][3] = 8;
threeD[1][2][3] = 8;
These are not the same. The dynamicThreeD at ache [] is accessing the array and retrieving the next pointer for referencing next. While the threeD object results in an index calculation (3) + (4 * 2) + (4*3 * 1) which is then used as an offset from the first element.
dynamicThreeD[1][2][3] = 8;
// Equivalent to
int** tmp1 = dynamicThreeD[1];
int* tmp2 = tmp1[2];
tmp2[3] = 8;
threeD[1][2][3] = 8;
// Equivalent to
int index = (3) + (4 * 2) + (4*3 * 1)
// OK The compiler does not take the address like this
// But I needed to do some type gymnastics to convert the type
// at the language level.
//
// But you get the same effect like this.
// As the value is just offset by an index from the beginning of
// the array.
(&threed[0][0][0])[index] = 8;
The side affect of this is that multi dimensional arrays can be much more efficient as 1) we only need to do one memory accesses (not 3) get/set the data. 2) Because of data locality and caching you get a much better hit ratio.
On the other hand multi-dimensional arrays in parallel systems can be a pain if you have multiple writers as cache consistency becomes an issue if all the members are all in the same cache line. Here the array of array has an advantage as each line can be in a distinct part of memory and worked on independently (Note: I am oversimplify a very complex issue).
Among the other well answers, I want to focus your last concern.
"And for int[][] it almost always asks to only let the first parameter void, like for int array[][][][] I need to put int array a[][n1][n2][n3] in the function parameter, right?"
Yes, only the first dimension can be left unspecified. But if you want to have the other dimensions to be variable/determined at runtime, at least in C you can use:
int foo (int n1, int n2, int a[][n1][n2]) { ...
With an additional level of indirection, you can pass a fixed size of dimensions, since sizeof(*x), sizeof(x[0][0]) etc. knows the dimensions of the indirect type.
But generally in C, the array [] is just a pointer * and the size, especially when dynamic, needs to be passed or known, as a second parameter, global, MACRO, or in a struct, until classes come along. Of course, there is the null term thing used so much with char* and char**argv. For pointers, you pay 8 bytes overhead.
I'm currently reading a tutorial on C++ which states the following:
When passing arrays of higher dimensions, only the first dimension can be open while the others must be known during compilation.
I need further explanation on this. When passing a 1D array (int a[]) to some function f() the arrays decays to a pointer int* a. I'm assuming that the above statement implies that a 2D array for example int a[][] decays to int *a[] or is it saying that only the first dimension can be dynamically allocated (which is nonsense). Specifically, what does "only the first dimension can be open" stating ? These are the only two explanations I was able to come up with.
Author is saying if a array is int a[5][10] and you want to pass that array to some function, then in function declaration you write the parameter accepting that array as int x[][10] and even that parameter i.e int x[][10] decays to int (*x)[10]
Anyways good way to pass 2D array is :
template<typename T, int first,int second>
void someFunction(T (&array)[first][second])
{....}
I'm assuming that the above statement implies that a 2D array for
example int a[][] decays to int *a[]
int a[x][y] decays to int (*a)[y] i.e a is pointer to array of y ints, which is different from int *a[y] which means a is array of y int pointers.
If you have a two-dimensional array as for example
int a[M][N];
then used in expressions as for example like in an argument expression if the corresponding function parameter is not a reference it is implicitly converted to pointer to its first element int ( * )[N].
So for example you can write
int ( *p )[N] = a;
You can imagine it the following way.
If you have an array
T a[M};
then it is implicitly converted in expressions with rare exceptions to pointer to its first element T *.
If you have a two-dimensional array
int a[M][N];
you can represent it the following way
typedef int T[N];
T a[M];
So the pointer will look like
T *p = a;
where the type T is one-dimensional array int [N]
In passing a 2-D array in functions, why are we giving its column number as follows:
int arr1[2][3] = {{1,2,3}, {4,5}};
void Array(const int [][3]); // prototype
Why we have to pass it column width? If i keep it empty it gives an error!
A two dimensional array is in fact a one-dimensional array elements of which are in turn one dimensional arrays.
You can consider an array declaration like this
int arr1[2][3] = {{1,2,3},{4,5}};
the following way
typedef int T[3];
T arr1[2] = {{1,2,3},{4,5}};
When you pass an array by value as an argument to a function it is converted to pointer to its first element.
So if you have a function like this
void Array(const T arr[] );
then this declaration is equivalent to
void Array(const T *arr );
and the both declare the same one function.
It is important to know the complete type T that you can use the pointer arithmetic with the pointer. For example when you use expression ++arr then it means that the current value in arr is increased by sizeof( T ). So the compiler need to know sizeof( T ). Otherwise it will be unable to do the pointer arithmetic.
So returning to your example the compiler need to know the type of element of the two dimensional array that is that its type T is int[3]
So this decleration
void Array(const int [][3]);
is equivalent to
void Array(const int ( * )[3]);
or to the following
typedef int T[3];
void Array(const T * );
In this case the compiler will know the size of the object (the first element of the passed array) pointed to by its parameter that is by the pointer.
One thing I noticed is that this initialization looks supicious:
int arr1[2][3] = {{1,2,3}, {4,5}};
arr1 has 2 rows and columns. Why is there no initialization of the 3rd column of row 2? (FYI the compiler will make it zero, but I'm not sure if that was the intent.)
In C, a multidimensional array is represented as a "flattened" 1 dimensional array. This is explained here: How Are C Arrays Represented In Memory?
In a 2 dimensional array the elements are stored in row major order. So these 2 declarations are conceptually the same:
int arr2[ROWS][COLS];
int arr1[ROWS*COLS];
And these 2 accesses are conceptually the same:
arr2[i][j]
arr1[(i*COLS)+j]
In in fact, in C, the second form is what's happening under the hood. This explains why your prototype needs to know how many columns - it's so the compiler can perform the multiplication to get the proper element in the linear array.
Lets assume there's variable of type int** which is the pointer to a 5x5 2D array:
int** ptr_array_5by5;
and a function with the following prototype:
void print2DArray_with5Columns(int (*ptr_row)[5]);
Is there a way to cast ptr_array_5by5 into some type which would match the type of the first argument of the function?
print2DArray_with5Columns( (<some casting type>) ptr_array_5by5)
The important thing to realize here is that int** is not
a pointer to a 2D array of anything. It is a pointer to a 1D
array of pointers. int (*)[5] is a pointer to a 2D array of
int (or more correctly, it is a pointer to the first element
of such an array). You can pretty much convert any pointer type
to any other pointer type using reinterpret_cast, but it is
also pretty much guaranteed not to work at runtime. (There are
special exceptions, but they are all very platform specific, and
very close to the hardware.)
If you really have an int** which points to 5 int*, each of
which points to 5 int, and you need an int (*)[5], the only
way you're going to be able to successfully convert is by doing
a conversion on the actual underlying data (which will involve
a copy), and not a conversion on the pointer. Something like:
int tmp[5][5];
for ( int i = 0; i != 5; ++ i ) {
int* row = ptr_array_5by5[i];
for ( int j = 0; j != 5; ++ j ) {
tmp[i][j] = row[j];
}
}
You can then pass tmp to your function without any casts: the
implicit conversion of array to pointer will convert
int [5][5] to int (*)[5].
Of course.
int** ptr_array_5by5;
void print2DArray_with5Columns(int (*ptr_row)[5]);
print2DArray_with5Columns( (int (*)[5]) ptr_array_5by5);
print2DArray_with5Columns( reinterpret_cast<int (*)[5]>(ptr_array_5by5));
The C language declaration syntax, for all its faults, lets you create casts by simply rewriting the declaration omitting any identifiers. It compiles, and it might even work.
There is a lot of confusion here because the descriptive wording does not match the C declarations. Here is some code that implements this (peculiar) cast and shows that it can work, just as I said.
void print2DArray_with5Columns(int (*ptr_row)[5]) {
for (int i = 0; i < 5; i++)
cout << (*ptr_row)[i] << " ";
cout << std::endl;
}
int main() {
int* a;
int** ptr_array_5by5;
a = new int[25];
for (int i = 0; i < 25; i++)
a[i] = i;
ptr_array_5by5 = (int**)a;
print2DArray_with5Columns((int (*)[5])(ptr_array_5by5));
return 0;
}
Please note that this declaration is not a 5x5 matrix. The cast is simply a pointer to an array of 5 ints, which decays to a simple array. This code generates a 5x5 flat matrix and prints the first row.
I suspect the real problem is that the cast is wrong and therefore the whole question is wrong.
The question has been asked whether this is the dreaded Undefined Behaviour. With suitable care it is not. The standard in effect allows any kind of a pointer-to-object to be cast to some other pointer-to-object or to a void pointer or to a large enough integer, and back again. [Pointer-to-function and pointer-to-member are treated a bit differently.] The round-tripped pointer is guaranteed to retain the same value. Therefore this cast is not UB provided the rules are followed, which is not that hard to do.
int** and int(*)[5] are different types (as n.m. pointed out)
You may treat an array as a pointer, e.g. int a[5]; *(a+1) = 6;
You may treat a pointer as an array, e.g. int *a = new int[5]; a[1] = 6;.
But treating object A as if it were an object B does not mean that it actually is object B.
What you can do though is declaring an int (*)[5], write the values of ptr_array_5by5 into it (after allocating memory, of course), and pass it to print2DArray_with5Columns.
On the other hand, yes there are casts that make your code compile. But I doubt that using one of them is getting you closer to your goal (see http://ideone.com/lVzNrN).
I think you are confused by assuming that pointers are arrays. Pointers are not arrays and vice-versa. If ptr_array_5by5 would be a 2D array then the parameter in prototype is fine for passing ptr_array_5by5 as argument. But you declared it as int **ptr_array_5by5, it is better to change the parameter to int ** type.
Why would you like to cast int **?
You use pointers to point to addresses.
So instead of type casting pointers, you have to point this pointer to some variable.
There is no meaning of type casting pointers because they themselves don't store values.
I read in a book that, int f (int P[2][4]) cannot accept A[2][3], but B[3][4] is fine. what is the reason for this?
Especially when we create dynamic allocation using pointers, this should not be a problem.
Thanks
The reason is that
int f( int P[2][4] );
is a synonym for
int f( int (*P)[4] );
The first dimension in a function declaration is just comments.
The reason is that function parameters never really have array type. The compiler treats the declaration
int f(int P[2][4]);
as though it really said
int f(int (*P)[4]);
P is a pointer to an array of four ints. The type int [3][4] decays to that same type. But the type int [2][3] decays to the type int (*)[3] instead, which is not compatible.
Dynamic allocation is another matter entirely, and it probably does not involve array-of-array types no matter how you do it. (Array of pointers, more likely.)
In C and C++, if you specify a function takes 2-D arrays, then it has to be given an explicit column size (the second []). The row size (first []) is optional.
It has todo with the memory layout. The first number is basically your number of rows and the second is how many elements are in each row. Rows are placed directly after each other. So there offset is determined by the number of elements in the rows multiplied by the number of rows preceding. The compiled function will be calculating row offsets based on 4 elements. When you pass an array with another row length these calculations will be wrong.
The reason why it's not allowed, because f(int P[2][4]) becomes f(int (*P)[4]) so you can pass int B[3][4] which can decay into int (*B)[4], but int A[2][3] cannot decay into int (*A)[4], so the latter will not be accepted by f(int (*P)[4]).
The type int (*)[3] is incompatible with int (*)[4]. One cannot convert to the other!
However there is a solution. You can do this:
template<size_t M, size_t N>
int f(int (&P)[M][N])
{
//Use M and N as dimensions of the 2D array!
}
//Usage
int A[2][3];
f(A); //M becomes 2, N becomes 3
int B[3][4];
f(B); //M becomes 3, N becomes 4
It will accept all two dimensional arrays!
The second parameter define the 2d array type since it's differs how many elements will be in every column.
The first parameter is only define the size of the array, and since you can send longer array to that function, it's like passing parameter with type: int*[3] which is similar to passing 1d array with size 10 to function that expect to get array with shorter size - and this is legal in c++.