tags:

views:

5104

answers:

7

Hi,

i need a ptr 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 dont get it, but 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
+1  A: 

You can do it like this:

uint8_t (*matrix_ptr)[10][20] = &l_matrix;
Nick D
doesnt this occupy 10 * 20 bytes of ram?(im on a microcontroller)
Dill
It will occupy 4 byte or whatever size a pointer is big in your box. But remember if you have this one, you have to index with matrix_ptr[0][x][y] or (*matrix_ptr)[x][y] . It's the direct and word-by-word interpretation of "pointer to two-dimensional array" :p
Johannes Schaub - litb
Thanks litb, I forgot to mention how to access it. There is no point to edit my answer since you did a great job with your answer :)
Nick D
A: 

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.

gnosis
This is what the C compiler does anyway. C doesn't really have any real notion of an "array"-- the [] notation is just syntactic sugar for pointer arithmetic
Ken Keenan
This solution has the disadvantage of never finding out the right way to do it.
Craig McQueen
+15  A: 

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 :)

Johannes Schaub - litb
+1 - nice info on the int (*)[][20] breakdown - can't do that in C++
Faisal Vali
@litb, i'm sorry but this is wrong as your solution does not provide any allocation of storage for the array.
Rob Wells
@Rob, i don't quite understand you. the storage in all these cases is provided by the array l_matix itself. The pointers to them take storage from where-ever they are declared in and as (stack, static data segment, ...).
Johannes Schaub - litb
I think uint8_t (*matrix_ptr)[10][20] = Makes the most sense...
Mark Renouf
+2  A: 

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 x 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,

Rob Wells
+1  A: 

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 }

Ken Keenan
A: 

Instead of

static uint8_t l_matrix[10][20];
uint8_t **matrix_ptr = l_matrix; //wrong idea

use

uint8_t *matrix_ptr = l_matrix;

Multi-dimensional arrays in C are in truth only 1-dimensional.

Georg
A: 

In the general case, a pointer to an 2-D array is declared like this:

int ***matrix_ptr = &l_matrix;

Pointer definitions are resolved from the "inside out" - or the notation that is closest to the variable name, and getting farther away with each step. From left to right:

The third "" is the "pointer". The second "" is for the first dimension of the array. The first "" is for the second dimension of the array.
You can continue to add "
" for any additional dimensions you want to add to the array.

It is not necessary to know the dimensions of the array in order to declare the pointer to that array.

The [] notation is syntactic sugar for the calculation of array offsets, but it is nice sugar:

matrix_ptr[0][1] = ...;

There is no requirement that the second dimension of the 2-D matrix have the same number of elements at every position.

semiuseless