Why does this work as it does?
uint8_t array[4] = {1,2,3,4};
uint8_t* parray = array;
uint8_t (*p1)[4] = (uint8_t (*)[4])&array;
uint8_t (*p2)[4] = (uint8_t (*)[4])&parray;
uint8_t (*p3)[4] = (uint8_t (*)[4])parray;
uint8_t test1 = **p1; // test1 = 1
uint8_t test2 = **p2; // test2 = something random
uint8_t test3 = **p3; // test3 = 1
parray is obviously nearly the same as array. For example, array[0] == parray[0]. But when I want to get the pointer to the array as the pointer to the fixed size array, I must use & symbol. When I want to get the pointer to the parray, I must not.
Practical example.
There is the function that accepts a pointer to the fixed size array
void foo(uint8_t (*param)[4])
{
...
}
When I get the param in another function as a pointer, can I pass it to foo this way?
void bar(uint8_t param*)
{
uint8_t (*p)[4] = (uint8_t (*)[4])param;
foo(p);
}
Is there a better way?
This is a feature called array decaying. The array variable is said to decay into a pointer to first element, when the variable name is used in a value context.
Here the array is used in a value context: parray = array, so it decays. You could write the decay explicitly: parray = &(array[0]). Former (implicit decay) is just syntactic sugar for the latter.
Operand of the addressof operator is not a value context. As such, the array name does not decay.&array is different from &(array[0]). First takes the address of an array type, the latter takes an address of the element type. parray on the other hand is a completely different variable, and &parray returns the address where the pointer is stored, which is not the address where the array is stored.
uint8_t (*p1)[4] = (uint8_t (*)[4])&array;
This is correct, although the conversion is redundant because &array is already of type uint8_t (*)[4].
uint8_t (*p2)[4] = (uint8_t (*)[4])&parray;
This is wrong. parray is of type uint8_t* and the address where it is stored doesn't contain an object of type uint8_t[4]. Instead it contains the pointer.
uint8_t (*p3)[4] = (uint8_t (*)[4])parray;
This is a bit dubious. parray is a pointer to uint8_t, not a pointer to uint8_t[4]. However, it happens to point to an address that also contains a uint8_t[4] object, so this works.
parray is obviously nearly the same as array
But clearly not exactly same, as evidenced by the behaviour of your program.
array is an array of four uint8_t elements, and parray is a pointer to uint8_t, which points to the first element of array. This distinction is important to understand.
Conclusion: It is important to understand what array decaying is, and what is the difference between an array and a pointer, and most importantly: Explicit conversions can hide mistakes from the compiler - avoid them when you can.
For the edit:
When I get the param in another function as a pointer, can I pass it to foo this way?
Only if you can prove that param points to the first element of a
uint8_t[4]. That is essentially a pre-condition of bar.
However, it is better to not rely on verbal pre-conditions, when you could use the type system to communicate the requirements:
Is there a better way?
Change the parameter type of bar, so that users know to pass a pointer of correct type:
void bar(uint8_t (*param)[4]) {
foo(param);
}
Of course, this makes bar redundant in this simple example.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With