There were
questions from multiple persons in the earlier article, all were about pointer
typecasting and dereferencing. So I thought of writing about the same, let’s
get started. Before that if you haven`t checked out those articles, please do
so, the link is at the end of this article. It`s better to read this first
before going through those links if you`re not familiar with pointer
typecasting.
The most asked question
is how this below code snippet ( and similar ) works?
uint32 u32Destination;
uint8 u8Source[ 12 ] ;
u8Source[ 0 ] = 0x01 ;
u8Source[ 1 ] = 0x23 ;
u8Source[ 2 ] = 0x45 ;
u8Source[ 3 ] = 0x67 ;
As you may
know, I used this statement to copy the four byte value from long integer to the
byte array contiguously. We can consider pointers which point to variables of
different datatype to get to know how this works.
Before that let
me start from basics, as you would have known pointer is just a ( integer )
variable which is used to hold the address of any other variable. To be
precise it will have the address of the variable which it points to.
Let’s see what that means with the example. Consider
the below code snippet,
uint32_t u32Integer = 0xAABBCC;
uint32_t * p32Pointer =
& u32Integer.
Let’s assume
the long integer variable u32Integer is located at 0x20000, then the pointer
p32Pointer will be assigned the value of 0x20000 ( the address of
u32Integer ). If you dereference the p32Pointer, the controller will read four consecutive
bytes from the location 0x20000 and will combine those four one byte values based
on endianness of the architecture and will give you the four byte long integer
value. So the statement ( * p32Pointer ) will give you the value of
0xAABBCC.
Let`s look at another example with a pointer to a short
integer as shown below,
uint16_t u16Integer = 0xDDEE;
uint16_t * p16Pointer =
& u16Integer.
For the sake of
simplicity assume this u16Integer variable is also located in 0x20000,
then the pointer p16Pointer will have the value of 0x20000 as well. In
this case, if you dereference the p16Pointer then it will read two
consecutive bytes from the location 0x20000 and will combine those two one byte
values based on endianness of the architecture and will give you the two byte short
integer value.
Note that there
is no difference in the value ( address ) stored in the p16Pointer ( which
is a pointer to a short integer ) and p32Pointer ( which is a pointer to a long integer ), both are having the value of 0x20000 which is the address of
the variables u16Integer and u32Integer.
So during
dereferencing, how the controller gives you the four byte value for p32Pointer
and two byte value for p16Pointer? The difference is in the compiler
interpretation while dereferencing the pointers. If the pointer points to the
short integer then the compiler reads two bytes from the address the pointer
points to. If the pointer points to the long integer then the compiler reads
four bytes from the address the pointer points to.
So If you want
to extract the lower two bytes from u32Integer and store it in uint16_t
variable, you can achieve that with conventional AND method or typecasting, but
you can also achieve the same using pointer as shown below,
uint16_t u16Integer = *( (
uint16_t * ) p32Pointer )
Have you got how this works? As we saw earlier, there is no difference in the value stored in
the pointer variable, the pointer p32Pointer will have the value of
0x20000. By default, the compiler will interpret the p32Pointer as a
pointer to the uint32_t as that`s what the datatype used in the declaration
of p32Pointer. So if you want to read just two bytes from 0x20000, then you
have to make that pointer as a pointer to uint16_t, this can be done
through typecasting. So prefixing p32Pointer with ( uint16_t * )
will tell the compiler that the p32Pointer is a pointer to uint16_t,
then the compiler will interpret the p32Pointer as a pointer to uint16_t.
Now if we
dereference it, it will read two one byte values from the location 0x20000 and will
combine those two bytes to give you the two byte short integer value. So as a result, u16Integer will have the value of 0xBBCC. Here you`ve to consider another subtlety,
this result will come only in little endian based system, and will be different
in big endian systems.
As I mentioned
earlier, the value will be stored in memory based on the endianness of the
system. For the example above, for the little endian based system, the value
will be stored in memory as shown below,
0x20000 = 0xCC
0x20001 = 0xBB
0x20002 = 0xAA
0x20003 = 0x00
So in this case, if we read two bytes from 0x20000, the result we get is 0xCC and 0xBB, if we concatenate
these two values based on little endian format the resultant two byte value is
0xBBCC.
So that`s what
will be the value of *( ( uint16_t * )
p32Pointer ) statement.
Now let’s
assume the endianness of the system architecture is big endian, the value will
be stored in memory as shown below,
0x20000 = 0x00
0x20001 = 0xAA
0x20002 = 0xBB
0x20003 = 0xCC
So in this case, if we read two bytes from 0x20000, the result we get is 0x00 and 0xAA, if we
concatenate these two values based on little endian format the resultant two
byte value is 0x00AA.
So that`s what
will be the value of *( ( uint16_t * )
p32Pointer ) statement.
I hope you
understood how it works. Now let`s go back to the main question,
u32Destination = *((
uint32_t * ) ( & u8Source [ 0 ] ) );
Here ( &u8Source[
0 ] ) gives the address of the first element of u8Destination array. Let`s
suppose it is located at 0x30000. Prefixing this ( &u8Source[ 0 ] ) with
( uint32_t * ) makes it the pointer to the unsigned long integer. So if
you dereference it, the controller will read four one byte values starting from the
location 0x30000 and will combine those values into one four byte value, which
will be stored in u32Destination.
So If you are working with little endian system
and the buffer data is also aligned in little endian format, then you can use
the dereference method discussed in my previous article. In big endian systems, your
buffer data has to be aligned in the big endian format for this dereference
method to work.
Refer the below link to know about the usage of
dereferencing method,
No comments:
Post a Comment