2D-array as argument to function - c++

Why can't you declare a 2D array argument in a function as you do with a normal array?
void F(int bar[]){} //Ok
void Fo(int bar[][]) //Not ok
void Foo(int bar[][SIZE]) //Ok
Why is it needed to declare the size for the column?

Static Arrays:
You seem not to have got the point completely. I thought to try to explain it somewhat. As some of the above answers describe, a 2D Array in C++ is stored in memory as a 1D Array.
int arr[3][4] ; //consider numbers starting from zero are stored in it
Looks somewhat like this in Memory.
1000 //ignore this for some moments 1011
^ ^
^ ^
0 1 2 3 4 5 6 7 8 9 10 11
|------------| |-----------| |-------------|
First Array Second Array Third Array
|----------------------------------------------|
Larger 2D Array
Consider that here, the Bigger 2D Array is stored as contiguous memory units. It consists of total 12 elements, from 0 to 11. Rows are 3 and columns are 4. If you want to access the third array, you need to skip the whole first and second arrays. That is, you need to skip elements equal to the number of your cols multiplied by how many arrays you want skip. It comes out to be cols * 2.
Now when you specify the dimensions to access any single index of the array, you need to tell the compiler beforehand exactly how much elements to skip. So you give it the exact number of cols to perform the rest of the calculation.
So how does it perform the calculation? Let us say it works on the column major order, that is, it needs to know the number of columns to skip. When you specify one element of this array as...
arr[i][j] ;
Compiler performs this calculation automatically.
Base Address + (i * cols + j) ;
Let us try the formula for one index to test its veracity. We want to access the 3rd element of the 2nd Array. We would do it like this...
arr[1][2] ; //access third element of second array
We put it in the formula...
1000 + ( 1 * 4 + 2 )
= 1000 + ( 6 )
= 1006 //destination address
And we reach at the address 1006 where 6 is located.
In a nutshell, we need to tell the compiler the number of cols for this calculation. So we send it as a parameter in a function.
If we are working on a 3D Array, like this...
int arr[ROWS][COLS][HEIGHT] ;
We would have to send it the last two dimensions of the array in a function.
void myFunction (int arr[][COLS][HEIGHT]) ;
The formula now would become this..
Base Address + ( (i * cols * height) + (j * height) + k ) ;
To access it like this...
arr[i][j][k] ;
COLS tell the compiler to skip the number of 2D Array, and HEIGHT tells it to skip the number of 1D Arrays.
And so on and so forth for any dimension.
Dynamic Arrays:
As you ask about different behavior in case of dynamic arrays which are declared thus..
int ** arr ;
Compiler treats them differently, because each index of a Dynamic 2D Array consists of an address to another 1D Array. They may or may not be present on contiguous locations on heap. Their elements are accessed by their respective pointers. The dynamic counterpart of our static array above would look somewhat like this.
1000 //2D Pointer
^
^
2000 2001 2002
^ ^ ^
^ ^ ^
0 4 8
1 5 9
2 6 10
3 7 11
1st ptr 2nd ptr 3rd ptr
Suppose this is the situation. Here the 2D Pointer or Array on the location 1000. It hold the address to 2000 which itself holds address of a memory location. Here pointer arithmetic is done by the compiler by virtue of which it judges the correct location of an element.
To allocate memory to 2D Pointer, we do it..
arr = new int *[3] ;
And to allocate memory to each of its index pointer, this way..
for (auto i = 0 ; i < 3 ; ++i)
arr[i] = new int [4] ;
At the end, each ptr of the 2D Array is itself an array. To access an element you do...
arr[i][j] ;
Compiler does this...
*( *(arr + i) + j ) ;
|---------|
1st step
|------------------|
2nd step
In the first step, the 2D Array gets dereferenced to its appropriate 1D Array and in the second step, the 1D Array gets dereferenced to reach at the appropriate index.
That is the reason why Dynamic 2D Arrays are sent to the function without any mention of their row or column.
Note:
Many details have been ignored and many things supposed in the description, especially the memory mapping just to give you an idea.

You can't write void Foo(int bar[][]), because bar decays to a pointer. Imagine following code:
void Foo(int bar[][]) // pseudocode
{
bar++; // compiler can't know by how much increase the pointer
// as it doesn't know size of *bar
}
So, compiler must know size of *bar, therefore size of rightmost array must be provided.

Because when you pass an array, it decays to a pointer, so excluding the outer-most dimension is ok and that's the only dimension you can exclude.
void Foo(int bar[][SIZE])
is equivalent to:
void Foo(int (*bar)[SIZE])

The compiler needs to know how long the second dimension is to calculate the offsets. A 2D array is in fact stored as a 1D array.
If you want to send an array with no known dimensions, consider using pointer to pointers and some sort of way to know the dimension yourself.
This is different from e.g. java, because in java the datatype also contains the dimension.

Since static 2D arrays are like 1D arrays with some sugar to better access data, you have to think about the arithmetic of pointers.
When the compiler tries to access element array[x][y], it has to calculate the address memory of the element, that is array+x*NUM_COLS+y. So it needs to know the length of a row (how many elements it contains).
If you need more information I suggest this link.

there are basically three ways to allocate a 2d array in C/C++
allocate on heap as a 2d array
you can allocate a 2d array on the heap using malloc such as:
const int row = 5;
const int col = 10;
int **bar = (int**)malloc(row * sizeof(int*));
for (size_t i = 0; i < row; ++i)
{
bar[i] = (int*)malloc(col * sizeof(int));
}
this is actually stored as an array of arrays therefore isn't necessarily
contiguous in memory. note that this also means there will be a pointer for
each array costing yout extra memory usage (5 pointers in this example, 10
pointers if you allocate it the other way around). you can pass this array to
a function with the signature:
void foo(int **baz)
allocate on heap as 1d array
for various reasons (cache optimizations, memory usage etc.) it may be
desirable to store the 2d array as a 1d array:
const int row = 5;
const int col = 10;
int *bar = (int*)malloc(row * col * sizeof(int));
knowing second dimension you can access the elements using:
bar[1 + 2 * col] // corresponds semantically to bar[2][1]
some people use preprocessor magic (or method overloading of () in C++) to
handle this automatically such as:
#define BAR(i,j) bar[(j) + (i) * col]
..
BAR(2,1) // is actually bar[1 + 2 * col]
you need to have the function signature:
void foo(int *baz)
in order to pass this array to a function.
allocate on stack
you can allocate a 2d array on stack using something like:
int bar[5][10];
this is allocated as a 1d array on the stack therefore compiler needs to know
the second dimension to reach the element you need just like we did in the
second example, therefore the following is also true:
bar[2][1] == (*bar)[1 + 2 * 10]
function signature for this array should be:
void foo(int baz[][10])
you need to provide the second dimension so that compiler would know where to reach in memory. you don't have to give the first dimension since C/C++ is not a safe language in this respect.
let me know if I mixed up rows and columns somewhere..

Related

Why we don't need number of column when passing the dynamic 2d array?

Lets say that I have two arrays and I am passing them to a function :
void func(int arr1[][4], int **arr2) { // <- I need to give n in only one, why?
...
}
int main() {
int n = 5, m = 4;
int arr1[n][m];
int **arr2 = (int**)malloc(n * sizeof(int*));
for(int i = 0;i < n;i++)
arr2[i] = (int*)malloc(m * sizeof(int));
func(arr1, arr2);
return 0;
}
Why can't we treat both the array passing in a similar way?
Edit : There was an error in the code.
Acually the opposite of what you're saying is the case: You don't have to pass the number of rows. Assume that array indices work like this:
int arr[MAX_ROW][MAX_COL]; /* with both 3 */
col
--------------->
| 0,0 0,1 0,2
row | 1,0 1,1 1,2
V 2,0 2,1 2,2
When you pass int arr[][MAX_COL] the compiler know where the next row will begin when you address like arr[row][col] for example.
If you would do that manually with a pointer it would look like: &arr[0][0] + row * MAX_COL + col. In that example you also have to know the column size MAX_COL of the array to calculate the next row.
The reason for this is, that an array is continuous in memory. The above array is represented in memory like:
| row = 0 | row = 1 | row = 2 |
| 0,0 0,1 0,2 | 1,0 1,1 1,2 | 2,0 2,1 2,2 |
The compiler does also have to know the row offset because when you pass an array declared as int arr[MAX_SIZE] to the function void foo (int arr[]), it decays into a pointer to the beginning of the array int* arr. In case of arrays of arrays (2D arrays), it decays to a pointer to its first element as well, which is a pointer to a single array int (*arr)[MAX_COL].
In short: With int arr[][MAX_COL] the compiler have all informations needed to address the array with arr[row][col].
Actually it's the opposite, you can omit only one of the index (in case of a multi-dimensional array), the innermost one.
This is because, arrays, while passed as function arguments, decays to a pointer to the first element. Quoting C11, chapter §6.3.2.1
Except when it is the operand of the sizeof operator, the _Alignof operator, or the
unary & operator, or is a string literal used to initialize an array, an expression that has
type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points
to the initial element of the array object and is not an lvalue. [...]
thus, a notation like
void func(int arr1[][5], int **arr2) //an array of array of 5 ints
and
void func(int (*arr1) [5], int **arr2) //pointer to the starting element of an array of 5 ints
are equivalent.
You actually have only one array of ints (i.e. int arr1[][5]) and one pointer to a pointer of an int, i.e. int **arr2. Even if an array like arr1[10][5], when passed as argument to a function, decays to a pointer to the beginning of the memory where the elements reside, there is a (big) difference in memory layout and in the way how a compiler treats access to these pointers.
BTW, in main it should be int n=5,m=4;int arr1[m][n], not int n=5,m=4;int arr1[n][m].
Concerning memory layout:
A 2D integer array of the form int [10][5] is represented as 10 consecutive "rows", each comprising 5 "columns" (i.e. integral values). The size of this array is 10 * 5 * sizeof(int), and the size of one "row" is 5 * sizeof(int).
A pointer to a pointer to int int **p is just a single pointer; its size is sizeof(int**), even if you have "malloced" a sequence of integral pointers lile p = malloc(10 * sizeof (int*)); Note "*" in sizeof(int *), as you create a sequence of pointers to integers, not a sequence of integers. That's the main difference in memory layout: It's not a 2D array of integers, but a 1D array of pointers to integers. And if one actually had allocated 10 "rows" for "10" integers, each row could be located in a different portion of memory. The space needed for managing such a (spreaded) amount of 10x5 integral values is "10*sizeof(int*) + 10*5*sizeof(int)".
Concerning access:
Let's assume a variable of type int arr[][5], which is a 2D-array of integers, where the size of a column is 5 and the number of rows is not determined. Informally, an access like int x = arr[3][4] is translated into an access the (3*5 + 4)th element of the array, i.e. "row times rowsize plus column"; Note that - based on this formula - the compiler does not need to know how many rows the the array actually has.
In contrast, let's assume a variable of type int **p. You can think of an access like x = p[3][4] as being equivalent to int *r = p[3]; int x = r[4]; Note that r is of type int *, i.e. it is a pointer, and r[4] then dereferences this pointer and returns an integral value.
This is rather informally described.
Yet the main issue is that the memory layout of an arr[][5] contains consecutive integral values only, whereas int **arrr may be a seqence of pointers (or even just one such pointer), each of them probably pointing to a sequence of integral values (or just one integral value).

Why is a hard coded width required when declaring a two dimensional array?

Pointers to pointers seem to be the only way to dynamically declare a two dimensional (or multidimensional) array besides doing the leg work myself and coding the pointer arithmetic to create a "fake" multidimensional array out of a one dimensional array. I understand this is the way it is but why?!
First of all, a two dimensional array is not the same as a pointer to a pointer.
While the latter is a pointer to an array of pointers (to arrays), the former looks like this in memory:
char v[2][3] = {{1,3,5},{5,10,2}};
Content: | 1 | 3 | 5 | 5 | 10 | 2
Address: v v+1 v+2 v+3 v+4 v+5
To access v[x][y], the compiler rewrites it as: *(v + y * WIDTH + x)
So as you can see, WIDTH (the second dimension specified) is needed to do the calculation.
You can omit the first dimension in function parameters since they are adjusted to pointers:
int f (int v[3][10])
is the same as
int f (int v[][10])
and the same as
int f (int (*v)[100])
(Since arrays are adjusted to pointers in function parameters)
but not the same as
int f (int *v[100])
(Since this is an array[100] (that is
adjusted to a pointer) of pointers)
Anyway I suggest you to use std::vector<std::vector<Type>> if you need a two-dimensional array.
Why is a hard coded width required when declaring a two dimensional array?
C++ arrays require their dimensions to be constant expressions. For 2D arrays (really arrays of arrays), this applies to both dimensions. There is no exception for the "width" or the "height".

Designating a pointer to a 2D array

If I declare a 2D array
int A[sz][sz];
How can I create a pointer to this object?
I ask because I want to return an array via pointer to a pointer, int**, from a function but I want to build the array without knowing the size beforehand. The size will be passed as an argument. I want to know if there is a way to do this without using dynamic allocation.
The problem is if I do something like int** A inside the function this gives A no information about the size I want.
How can I create the array and then assign a pointer to this array, if it's a 2D array.
I should be more clear. I want return a pointer to a pointer so it wouldn't be a pointer to the 2D array but a something like int**.
Your problem is, that a 2D array in the form int** requires an array of int* for the two step dereferencing, which simply does not exist when you declare an array with int A[sz][sz];.
You can build it yourself like this:
int* pointers[sz];
for(size_t i = sz; i--; ) pointers[i] = A[i];
This might seem absurd, but is rooted in the way C handles arrays: A[i] is of type int ()[sz], which is the subarray of row i. But when you use that array in the assignment, it decays to a pointer to the first element in that subarray, which is of type int*. After the loop, A and pointers are two very different things (the type of A is int ()[sz][sz])
Sidenote: You say that you want to return this from a function. If your array is allocated on the stack, you must not return a pointer to its data, it will disappear the moment your function returns. You can only return pointers/references to objects that have either static storage or are part of another existing object. If you fail to comply with this, you are likely to get stack corruption.
Edit:
A little known fact about C is, that you can actually pass around pointers to real C arrays, not just the pointer types that an array decays to. Here is a small program to demonstrate this:
#include <stddef.h>
#include <stdio.h>
int (*foo(int size, int (*bar)[size][size], int y))[] {
return &(*bar)[y];
}
int main() {
int mySize = 30;
int baz[mySize][mySize];
int (*result)[mySize];
result = foo(mySize, &baz, 15);
printf("%ld\n", (size_t)result - (size_t)baz);
}
The expected output of this example program is 1800. The important thing is that the actual size of the array must be known, either by being a compile time constant, or by being passed along with the array pointer (and if it's passed along with the array pointer, the size argument must appear before the array pointer does).
Let me flesh out your question a little bit. You mention:
I ask because I want to return an array [...] from a function but I
want to build the array without knowing the size beforehand. The size
will be passed as an argument. I want to know if there is a way to do
this without using dynamic allocation.
For the I want to return an array from a function [...] size passed as an argument, it seems reasonable to me that you can use std::vector everywhere, and call its .data() method when you need access to the underlying array (which is guaranteed to be contiguous). For example:
std:vector<double> myfun(size_t N) {
std::vector<double> r(N);
// fill r[0], r[1], ..., r[N-1]
return r;
}
// later on:
r.data(); // gives you a pointer to the underlying double[N]
And for the I want to to do this without dynamic allocation, that is not possible unless you know the size at compile time. If that is the case, then do exactly as before but use std::array, which can implement optimizations based on known compile-time size:
std::array<double, N> myfun() {
std::array<double, N> r;
// fill r[0], r[1], ..., r[N-1]
return r;
}
// later on:
r.data(); // gives you a pointer to the underlying double[N]
And to be generic, I would actually use a template function capable of working with arbitrary containers:
template<typename T>
void myfun(T& data) {
for(int k=0; k<data.size(); k++) {
// do stuff to data[k]
}
}
// call as, for example:
std::vector<double> data(10);
myfun(data);
// or equally valid:
std::array<double, 10> data;
myfun(data);
Finally, if you are working with two-dimensional data, please remember that when you store the Matrix in row-major order that is:
Matrix [1, 2; 3 4] is stored as [1 2 3 4]
then you can refer to element (i, j) of the matrix by calling data[i * ncols + j]. For example: consider a three by four matrix:
a b c d
e f g h
i j k l
The element (2, 2) (that is: third row, third column because we assume zero-based C-type indexing) is calculated as: M[2][2] = M[2 * 4 + 2] = M[10] = k. This is the case because it was stored as:
[a b c d e f g h i j k l]
[0 1 2 3 4 5 6 7 8 9 10 11]
and k is the element with index 10.
the responses to your question are weird. Just do this:
int A[2][2];
int**p =NULL;
*p = A[0]; // **p==A[0][0] , *(*p+1)==A[0][1]

Can I access elements of a 2D array using pointers in C++?

For 1D array, I can use array name as a pointer and add offset to it to access each element of the array. Is there something similar for 2D arrays?
I defined a 2D array as follows
int arr[2][3] = {{1,2,3}, {4,5,6}};
int** arrPtr = arr;
but I got compiler error for the second line. Shouldn't 2D array have type int**?
I came across another thread here:
C++ Accessing Values at pointer of 2D Array
and saw this:
2dArray = new int*[size];
Could someone please tell me what int*[size] means? (size is an int, I presume).
Thanks a lot.
A multidimensional array defined as yours is is only a single pointer, because the data is encoded in sequence. Therefore, you can do the following:
int arr[2][3]={{1,2,3},{4,5,6}};
int* arrPtr = (int*)arr;
In general, the pointer to the element at arr[a][b] can be accessed by arrPtr + a*bSize + b where bSize is the size of the first array dimension (in this case three).
Your second question relates to dynamic memory allocation - allocating memory at runtime, instead of defining a fixed amount when the program starts. I recommend reviewing dynamic memory allocation on cplusplus.com before working with dynamically allocated 2D arrays.
int* array[10] means an array of 10 pointers to integer.
You can access a 2D array with a simple pointer to its first entry and do some maths exploiting the spacial location principle.
int array[2][2] = {{1,2}, {3, 4}};
int* p = &array[0][0];
for(int i=0; i<2*2; i++)
printf("%d ", *(p++));
If you have a matrix:
1 2
3 4
in memory it is encoded as 1 2 3 4 sequentially ;)

How to use (2d) arrays with negative index?

In 1D you can simulate x-coordinate in such a way:
int temp[1000];
int *x = a+500;
How can we have a grid now? (Something like a[10][-13].)
You can easily convert -ve and +ve integers into just +ve integers as an index into an array as you are unable to use -ve indexes.
Here is how
if (index < 0)
then index = -index * 2 - 1
else index = index * 2
i.e. -ve indexes use the odd numbers, +ve use the even numbers. 0 stays at 0.
Don't confuse mathematics with array dimensions in C/C++, those are different things. If you have a mathematical matrix with indices -500 to 500, you use a C array with indices 0 to 1000 to store it in.
However you can access an array by using a negative index, as long as you make sure you aren't accessing the array out of bounds. For example:
int arr[1000];
int* ptr = &arr[499];
printf("%d", ptr[-100]);
2D arrays work in the very same way, although strictly speaking you can still not access a sub array out of bounds and expect to end up in an adjacent array, this is undefined behavior in C/C++. But in real world implementations static 2D arrays are always allocated using adjacent memory cells, so one can often safely assume they are, no matter what the C standard says.
You just have to calculate the offsets yourself, for instance
int grid[400]; // twenty by twenty grid, origin at (10, 10)
int get_grid_value(int x, int y)
{
return grid[20*(x + 10) + (y + 10)];
}
Of course in real code you shouldn't use so many magic numbers.
First of all, this only works if the memory allocated for the array is contiguous. Then you can find out the "middle point" of the array by
int temp[5][5];
int *a = temp[2] + 2;
Or, in more general terms
int len
int *temp = malloc(len * len * sizeof(int));
int *a = temp + (len/2)*len + len/2;
If you want to simulate geometry using arrays ... you could do something like
have a variable with maximum number of points and assign a pointer to the middle value. So with that pointer you could have negative indeces.
A sample program.
int main() {
int c[10000];
int *a = &c[5000];
for(int i=-5000;i<5000;i++)
a[i] = i;
for(int i=-5000;i<5000;i++)
cout<<a[i]<<" ";
cout<<endl;
return 0;
}
Hope this was helpful ..
To use it in a more proper way, you could have a class which internally manages this. Or you could have your template.
I'm not sure you can do that with a simple 2-D array without invoking the gremlins of undefined behavior, but you could set it up as an array of pointers. Create an array of pointers to int, then set a pointer to point into the middle of the array; that gives you signed indices for the first dimension. Then set each element of the pointer array to point to an array of int, and advance each to point to the middle of that array; that gives you signed indices for the second dimension. You can use the same arr[x][y] syntax you'd use for an actual 2-D array, but the second [] applies to an actual pointer, not an array that decayed to a pointer.
If any of these arrays are allocated with malloc(), you must pass the original pointer to free().
If there's sufficient interest, I'll try to post some code later.
BTW, I'm not at all convinced this would be worth the effort. You could easily fake all this with ordinary 0-based arrays, at the cost of a little syntactic sugar.