[c] Create a pointer to two-dimensional array

I need a pointer to a static 2-dimensional array. How is this done?

static uint8_t l_matrix[10][20];

void test(){
   uint8_t **matrix_ptr = l_matrix; //wrong idea 
}

I get all kinds of errors like:

  • warning: assignment from incompatible pointer type
  • subscripted value is neither array nor pointer
  • error: invalid use of flexible array member

This question is related to c arrays pointers

The answer is


The basic syntax of initializing pointer that points to multidimentional array is

type (*pointer)[1st dimension size][2nd dimension size][..] = &array_name

The the basic syntax for calling it is

(*pointer_name)[1st index][2nd index][...]

Here is a example:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
   // The multidimentional array...
   char balance[5][100] = {
       "Subham",
       "Messi"
   };

   char (*p)[5][100] = &balance; // Pointer initialization...

   printf("%s\n",(*p)[0]); // Calling...
   printf("%s\n",(*p)[1]); // Calling...

  return 0;
}

Output is:

Subham
Messi

It worked...


In C99 (supported by clang and gcc) there's an obscure syntax for passing multi-dimensional arrays to functions by reference:

int l_matrix[10][20];

void test(int matrix_ptr[static 10][20]) {
}

int main(void) {
    test(l_matrix);
}

Unlike a plain pointer, this hints about array size, theoretically allowing compiler to warn about passing too-small array and spot obvious out of bounds access.

Sadly, it doesn't fix sizeof() and compilers don't seem to use that information yet, so it remains a curiosity.


To fully understand this, you must grasp the following concepts:

Arrays are not pointers!

First of all (And it's been preached enough), arrays are not pointers. Instead, in most uses, they 'decay' to the address to their first element, which can be assigned to a pointer:

int a[] = {1, 2, 3};

int *p = a; // p now points to a[0]

I assume it works this way so that the array's contents can be accessed without copying all of them. That's just a behavior of array types and is not meant to imply that they are same thing.



Multidimensional arrays

Multidimensional arrays are just a way to 'partition' memory in a way that the compiler/machine can understand and operate on.

For instance, int a[4][3][5] = an array containing 4*3*5 (60) 'chunks' of integer-sized memory.

The advantage over using int a[4][3][5] vs plain int b[60] is that they're now 'partitioned' (Easier to work with their 'chunks', if needed), and the program can now perform bound checking.

In fact, int a[4][3][5] is stored exactly like int b[60] in memory - The only difference is that the program now manages it as if they're separate entities of certain sizes (Specifically, four groups of three groups of five).

Keep in mind: Both int a[4][3][5] and int b[60] are the same in memory, and the only difference is how they're handled by the application/compiler

{
  {1, 2, 3, 4, 5}
  {6, 7, 8, 9, 10}
  {11, 12, 13, 14, 15}
}
{
  {16, 17, 18, 19, 20}
  {21, 22, 23, 24, 25}
  {26, 27, 28, 29, 30}
}
{
  {31, 32, 33, 34, 35}
  {36, 37, 38, 39, 40}
  {41, 42, 43, 44, 45}
}
{
  {46, 47, 48, 49, 50}
  {51, 52, 53, 54, 55}
  {56, 57, 58, 59, 60}
}

From this, you can clearly see that each "partition" is just an array that the program keeps track of.



Syntax

Now, arrays are syntactically different from pointers. Specifically, this means the compiler/machine will treat them differently. This may seem like a no brainer, but take a look at this:

int a[3][3];

printf("%p %p", a, a[0]);

The above example prints the same memory address twice, like this:

0x7eb5a3b4 0x7eb5a3b4

However, only one can be assigned to a pointer so directly:

int *p1 = a[0]; // RIGHT !

int *p2 = a; // WRONG !

Why can't a be assigned to a pointer but a[0] can?

This, simply, is a consequence of multidimensional arrays, and I'll explain why:

At the level of 'a', we still see that we have another 'dimension' to look forward to. At the level of 'a[0]', however, we're already in the top dimension, so as far as the program is concerned we're just looking at a normal array.

You may be asking:

Why does it matter if the array is multidimensional in regards to making a pointer for it?

It's best to think this way:

A 'decay' from a multidimensional array is not just an address, but an address with partition data (AKA it still understands that its underlying data is made of other arrays), which consists of boundaries set by the array beyond the first dimension.

This 'partition' logic cannot exist within a pointer unless we specify it:

int a[4][5][95][8];

int (*p)[5][95][8];

p = a; // p = *a[0] // p = a+0

Otherwise, the meaning of the array's sorting properties are lost.

Also note the use of parenthesis around *p: int (*p)[5][95][8] - That's to specify that we're making a pointer with these bounds, not an array of pointers with these bounds: int *p[5][95][8]



Conclusion

Let's review:

  • Arrays decay to addresses if they have no other purpose in the used context
  • Multidimensional arrays are just arrays of arrays - Hence, the 'decayed' address will carry the burden of "I have sub dimensions"
  • Dimension data cannot exist in a pointer unless you give it to it.

In brief: multidimensional arrays decay to addresses that carry the ability to understand their contents.


You can do it like this:

uint8_t (*matrix_ptr)[10][20] = &l_matrix;

G'day,

The declaration

static uint8_t l_matrix[10][20];

has set aside storage for 10 rows of 20 unit8_t locations, i.e. 200 uint8_t sized locations, with each element being found by calculating 20 x row + column.

So doesn't

uint8_t (*matrix_ptr)[20] = l_matrix;

give you what you need and point to the column zero element of the first row of the array?

Edit: Thinking about this a bit further, isn't an array name, by definition, a pointer? That is, the name of an array is a synonym for the location of the first element, i.e. l_matrix[0][0]?

Edit2: As mentioned by others, the comment space is a bit too small for further discussion. Anyway:

typedef uint8_t array_of_20_uint8_t[20];
array_of_20_uint8_t *matrix_ptr = l_matrix;

does not provide any allocation of storage for the array in question.

As mentioned above, and as defined by the standard, the statement:

static uint8_t l_matrix[10][20];

has set aside 200 sequential locations of type uint8_t.

Referring to l_matrix using statements of the form:

(*l_matrix + (20 * rowno) + colno)

will give you the contents of the colno'th element found in row rowno.

All pointer manipulations automatically take into account the size of the object pointed to. - K&R Section 5.4, p.103

This is also the case if any padding or byte alignment shifting is involved in the storage of the object at hand. The compiler will automatically adjust for these. By definition of the C ANSI standard.

HTH

cheers,


You want a pointer to the first element, so;

static uint8_t l_matrix[10][20];

void test(){
   uint8_t *matrix_ptr = l_matrix[0]; //wrong idea 
}

You could also add an offset if you want to use negative indexes:

uint8_t l_matrix[10][20];
uint8_t (*matrix_ptr)[20] = l_matrix+5;
matrix_ptr[-4][1]=7;

If your compiler gives an error or warning you could use:

uint8_t (*matrix_ptr)[20] = (uint8_t (*)[20]) l_matrix;

You can always avoid fiddling around with the compiler by declaring the array as linear and doing the (row,col) to array index calculation by yourself.

static uint8_t l_matrix[200];

void test(int row, int col, uint8_t val)

{

   uint8_t* matrix_ptr = l_matrix;
   matrix_ptr [col+y*row] = val; // to assign a value

}

this is what the compiler would have done anyway.


In

int *ptr= l_matrix[0];

you can access like

*p
*(p+1)
*(p+2)

after all 2 dimensional arrays are also stored as 1-d.


Examples related to c

conflicting types for 'outchar' Can't compile C program on a Mac after upgrade to Mojave Program to find largest and second largest number in array Prime numbers between 1 to 100 in C Programming Language In c, in bool, true == 1 and false == 0? How I can print to stderr in C? Visual Studio Code includePath "error: assignment to expression with array type error" when I assign a struct field (C) Compiling an application for use in highly radioactive environments How can you print multiple variables inside a string using printf?

Examples related to arrays

PHP array value passes to next row Use NSInteger as array index How do I show a message in the foreach loop? Objects are not valid as a React child. If you meant to render a collection of children, use an array instead Iterating over arrays in Python 3 Best way to "push" into C# array Sort Array of object by object field in Angular 6 Checking for duplicate strings in JavaScript array what does numpy ndarray shape do? How to round a numpy array?

Examples related to pointers

Method Call Chaining; returning a pointer vs a reference? lvalue required as left operand of assignment error when using C++ Error: stray '\240' in program Reference to non-static member function must be called How to convert const char* to char* in C? Why should I use a pointer rather than the object itself? Function stoi not declared C pointers and arrays: [Warning] assignment makes pointer from integer without a cast Constant pointer vs Pointer to constant How to get the real and total length of char * (char array)?