Pointer to 2D arrays in C
//defines an array of 280 pointers (1120 or 2240 bytes)
int *pointer1 [280];
//defines a pointer (4 or 8 bytes depending on 32/64 bits platform)
int (*pointer2)[280]; //pointer to an array of 280 integers
int (*pointer3)[100][280]; //pointer to an 2D array of 100*280 integers
Using pointer2
or pointer3
produce the same binary except manipulations as ++pointer2
as pointed out by WhozCraig.
I recommend using typedef
(producing same binary code as above pointer3
)
typedef int myType[100][280];
myType *pointer3;
Note: Since C++11, you can also use keyword using
instead of typedef
using myType = int[100][280];
myType *pointer3;
in your example:
myType *pointer; // pointer creation
pointer = &tab1; // assignation
(*pointer)[5][12] = 517; // set (write)
int myint = (*pointer)[5][12]; // get (read)
Note: If the array tab1
is used within a function body => this array will be placed within the call stack memory. But the stack size is limited. Using arrays bigger than the free memory stack produces a stack overflow crash.
The full snippet is online-compilable at gcc.godbolt.org
int main()
{
//defines an array of 280 pointers (1120 or 2240 bytes)
int *pointer1 [280];
static_assert( sizeof(pointer1) == 2240, "" );
//defines a pointer (4 or 8 bytes depending on 32/64 bits platform)
int (*pointer2)[280]; //pointer to an array of 280 integers
int (*pointer3)[100][280]; //pointer to an 2D array of 100*280 integers
static_assert( sizeof(pointer2) == 8, "" );
static_assert( sizeof(pointer3) == 8, "" );
// Use 'typedef' (or 'using' if you use a modern C++ compiler)
typedef int myType[100][280];
//using myType = int[100][280];
int tab1[100][280];
myType *pointer; // pointer creation
pointer = &tab1; // assignation
(*pointer)[5][12] = 517; // set (write)
int myint = (*pointer)[5][12]; // get (read)
return myint;
}
Dereferencing a pointer to 2d array
The address of an array is the same as the address of its first element.
Given the definition int array[2][2] = {{0,1},{0,1}};
, the compiler arranges some location in memory to contain the int
values 0, 1, 0, and 1. Let’s say that location has address 1000, and the int
value 0 is stored in bytes 1000-1003, 1 is stored in 1004 to 1007, 0 is stored in 1008 to 1011, and 1 is stored in 1012 to 1015.
Where does the element array[0][0]
start in memory? At location 1000.
Where does the array array
start in memory? At location 1000.
Where does the array array[0]
start in memory? At location 1000.
The array starts in memory at the same location its first element starts in memory. Also, array[0]
, which is itself an array, starts at the location 1000.
So, after p = array;
, p
points the location 1000. And the element array[0][0]
also starts at location 1000. So, when you print p
, as with printf("%p\n", (void *) p);
, it is unsurprising you get the same result as when you print the address of array[0][0]
, as with printf("%p\n", (void *) &array[0][0]);
.
An array is automatically converted to the address of its first element.
Next, let’s consider *p
. The type of p
is int (*)[2]
, a pointer to an array of 2 int
. Therefore, *p
is an array of 2 int
.
In particular, *p
is an array. Suppose we attempt to print it by passing it as an argument to printf
. What happens?
In C, when an array is used in an expression, it is automatically converted to the address of its first element (except when the array is the operand of sizeof
or unary &
or is a string literal used to initialize an array). So, if you use *p
as an argument to printf
, it initially means array[0]
, but that array is converted to the address of its first argument. So passing *p
as an argument actually passes &array[0][0]
(or, equivalently &(*p)[0]
).
Thus, printf("%p\n", (void *) *p);
will print the same address as printf("%p\n", (void *) &a[0][0]);
.
Create a pointer to two-dimensional array
Here you wanna make a pointer to the first element of the array
uint8_t (*matrix_ptr)[20] = l_matrix;
With typedef, this looks cleaner
typedef uint8_t array_of_20_uint8_t[20];
array_of_20_uint8_t *matrix_ptr = l_matrix;
Then you can enjoy life again :)
matrix_ptr[0][1] = ...;
Beware of the pointer/array world in C, much confusion is around this.
Edit
Reviewing some of the other answers here, because the comment fields are too short to do there. Multiple alternatives were proposed, but it wasn't shown how they behave. Here is how they do
uint8_t (*matrix_ptr)[][20] = l_matrix;
If you fix the error and add the address-of operator &
like in the following snippet
uint8_t (*matrix_ptr)[][20] = &l_matrix;
Then that one creates a pointer to an incomplete array type of elements of type array of 20 uint8_t. Because the pointer is to an array of arrays, you have to access it with
(*matrix_ptr)[0][1] = ...;
And because it's a pointer to an incomplete array, you cannot do as a shortcut
matrix_ptr[0][0][1] = ...;
Because indexing requires the element type's size to be known (indexing implies an addition of an integer to the pointer, so it won't work with incomplete types). Note that this only works in C
, because T[]
and T[N]
are compatible types. C++ does not have a concept of compatible types, and so it will reject that code, because T[]
and T[10]
are different types.
The following alternative doesn't work at all, because the element type of the array, when you view it as a one-dimensional array, is not uint8_t
, but uint8_t[20]
uint8_t *matrix_ptr = l_matrix; // fail
The following is a good alternative
uint8_t (*matrix_ptr)[10][20] = &l_matrix;
You access it with
(*matrix_ptr)[0][1] = ...;
matrix_ptr[0][0][1] = ...; // also possible now
It has the benefit that it preserves the outer dimension's size. So you can apply sizeof on it
sizeof (*matrix_ptr) == sizeof(uint8_t) * 10 * 20
There is one other answer that makes use of the fact that items in an array are contiguously stored
uint8_t *matrix_ptr = l_matrix[0];
Now, that formally only allows you to access the elements of the first element of the two dimensional array. That is, the following condition hold
matrix_ptr[0] = ...; // valid
matrix_ptr[19] = ...; // valid
matrix_ptr[20] = ...; // undefined behavior
matrix_ptr[10*20-1] = ...; // undefined behavior
You will notice it probably works up to 10*20-1
, but if you throw on alias analysis and other aggressive optimizations, some compiler could make an assumption that may break that code. Having said that, i've never encountered a compiler that fails on it (but then again, i've not used that technique in real code), and even the C FAQ has that technique contained (with a warning about its UB'ness), and if you cannot change the array type, this is a last option to save you :)
A pointer to 2d array
Rather than referring to int[2][3]
as a '2d array', you should consider it to be an 'array of arrays'. It is an array with two items in it, where each item is itself an array with 3 ints in it.
int (*p)[3] = a;
You can use p
to point to either of the two items in a
. p
points to a three-int array--namely, the first such item. p+1
would point to the second three-int array. To initialize p
to point to the second element, use:
int (*p)[3] = &(a[1]);
The following are equivalent ways to point to the first of the two items.
int (*p)[3] = a; // as before
int (*p)[3] = &(a[0]);
How do pointers to 2d arrays work in C++?
This is actually very interesting and, the more you learn about C++
the more completely irrelevant you find it becomes in all but rare situations.
int arr2d[3][2] = {
{1, 2},
{3, 4},
{5, 6}
};
So here you have an identifier arr2d
and that identifier has a type:
array of arrays
Now the type array in C++
relates to a contiguous block of objects. So you have a contiguous block of contiguous blocks.
However, when you use the identifier in an expression the rules of C++
say it decays into a pointer to the first element of the array for which it is type array (there is a subtle distinction between an array and a type called "array").
So when you do this:
auto a = arr2d;
Well the identifier a
is now a pointer because arr2d
has decayed into a pointer to the first element of the 2D Array you originally created. But what is the type of the pointer?
It is a pointer to the first element of an array of arrays. But the first element of an array of arrays is an array - so it is a pointer to an array. How is that written in C++
syntax?
Like this:
int (*)[2]; // see the right-left-rule
C++
syntax can be a dark art and none of it quite so dark as the declaration of types. The complexity of it is hidden for simple situations like an int
but shows its metal when things get complicated.
int (*)[2]; // see the right-left-rule
This means a pointer (now go right) to an array of 2 elements (now go left) of type int
.
C++
types are declared onion like, unwrapping right to left to allow a unified syntax capable of declaring anything from an int
to a function returning a function pointer to a function that returned a pointer to an array of function pointers... etc.
See: right-left-rule.
Incidentally you could avoid decaying to a pointer to array by using this syntax:
auto a = &arr2d; // get the address of the whole 2D array
Your IDE
should show something like:
int (*)[3][2]; // pointer to array-of-array-of-int
You would access it like this:
(*a)[0][0] = 1; // dereference pointer to access 2D array it points to
But I suspect a better remedy to your problem is to use std::array
. That will behave more predictably.
std::array<std::array<int, 2>, 3> arr2d = {{
{1, 2},
{3, 4},
{5, 6}
}};
auto a = &arr2d;
Now a
is type std::array<std::array<int, 2>, 3>*
Pointers to 2D arrays C, C++
this would make a pointer type (int) to first element ..
No.&x
is the address of array x
and is of type int (*)[10]
and you can't assign it to a int *
type. Both are incompatible types.
So, if I had 2D array, I need to use double pointer: first pointer would point to the second dimension of the array.
No.
In expressions, arrays converted to pointer to its first elements except when an operand of sizeof
and unary &
operator. Therefore, in this case the type of x
will be int (*)[4]
after conversion. You need a pointer to an array of 4 int
instead of an array of 4 pointers
int (*p)[4] = x;
How to pass a 2D array by pointer in C?
char **
doesn't represent a 2D array - it would be an array of pointers to pointers. You need to change the definition of printarray
if you want to pass it a 2D array:
void printarray( char (*array)[50], int SIZE )
or equivalently:
void printarray( char array[][50], int SIZE )
Pointer to 2D array with function
Allow me to say that you are making your life hard for no reason at all! :) You see when one wants to manipulate a 2D array, he can work on the array directly, by passing the array itself to the function, like this:
#include <stdio.h>
#include <time.h>
void init(int n, int m, char array[n][m]);
int main(int argc, char *argv[]) {
const int n = 21, m = 80;
char grid[n][m];
int i, j;
init(n, m, grid);
for (i = 0; i < n; i++) {
for (j = 0; j < m; j++) {
printf("%c", grid[i][j]);
}
printf("\n");
}
return 0;
}
void init(int n, int m, char array[n][m]) {
int i,j;
for (i = 0; i < n; i++) {
for (j = 0; j < m; j++) {
array[i][j] = ' ';
}
}
for (i = 0; i < n; i++) {
array[i][0] = '|';
array[i][m - 1] = '|';
}
for (i = 0; i < m; i++) {
array[0][i] = '-';
array[n - 1][i] = '-';
}
}
which gives this lovely rectangle:
C02QT2UBFVH6-lm:~ gsamaras$ gcc -Wall main.c
C02QT2UBFVH6-lm:~ gsamaras$ ./a.out
--------------------------------------------------------------------------------
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
--------------------------------------------------------------------------------
Now notice the changes I made to your code:
- I got rid of the pointer that refers to the array, since it's
redundant. - I passed the array itself as a parameter to the function, as
described here. - Instead of using magic numbers (21 and 80) all over the place, I
declared two new constant variables,n
andm
, which are the
dimension of your 2D array,n
rows xm
columns.* - I use the dimensions to implement the very same logic you had
implemented so far. Notice that I have to pass them as function
parameters too.
As for the error, it implies that you are not accessing what you think that you are accessing! But let me not expand on this and keep it minimal here. :)
*Now if you want to change the dimensions of your 2D array, you just need to change n
and/or m
once, not everywhere in your code (which is bug prone).
Related Topics
Why Are Redundant Scope Qualifications Supported by the Compiler, and Is It Legal
Nonfree Module Is Missing in Opencv 3.0
C++ Error: Undefined Reference to 'Main'
What Is the Optimal Algorithm for Generating an Unbiased Random Integer Within a Range
Why Is Sizeof(Std::String) Only Eight Bytes
C++ Templates, Undefined Reference
Does Moving Leave the Object in a Usable State
Changing the Value of Const Variable in C++
C++: Construction and Initialization Order Guarantees
What Are the Distinctions Between the Various Symbols (*,&, etc) Combined with Parameters
C++ Return Value Created Before or After Auto Var Destruction
What Is the Status of N2965 - Std::Bases and Std::Direct_Bases
Constant References with Typedef and Templates in C++
Are Multiple Mutations of the Same Variable Within Initializer Lists Undefined Behavior Pre C++11
Intrinsics for Cpuid Like Informations
How to Check If a Type Is an Instantiation of a Given Class Template