A Pointer to 2D Array

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:

  1. I got rid of the pointer that refers to the array, since it's
    redundant.
  2. I passed the array itself as a parameter to the function, as
    described here.
  3. Instead of using magic numbers (21 and 80) all over the place, I
    declared two new constant variables, n and m, which are the
    dimension of your 2D array, n rows x m columns.*
  4. 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



Leave a reply



Submit