Every variable in a firmware has a name and a value associated with it. When a variable is declared, a specific block of memory within the microcontroller is allocated to hold the value of that variable. The size of the allocated block depends on the data type.
This works the same way with computers.
Consider the following statement.
When this statement executes, the compiler sets aside 1 byte or 8-bits of memory to hold the value 10.
It also sets up a symbol table in which it adds the symbol x and the relative address in the memory X where that 1 byte was set aside.
Thus every variable has a value and also a memory location commonly known as address associated with it.
Lets use the term rvalue, meaning real value to denote the variable and the term lvalue, meaning location value to denote the address of the variable.
The rvalue appears on the right side of the assignment statement (10 in the above statement)
and cannot be used on the left side of the assignment statement.
This brings us to pointers. A pointer is a variable that contains the memory location of another variable.
Therefore, a pointer is a variable that represents the location of a data item, such as a
variable or an array element. We use pointers quite a lot in C.
- Pointers are used to pass information back and forth between functions.
- Pointers enable the programmers to return multiple data items from a function via function
- Pointers provide an alternate way to access the individual elements of an array.
- Pointers are used to pass arrays and strings as function arguments. We will discuss this in
- Pointers are used to create complex data structures, such as trees, linked lists, linked stacks,linked queues, and graphs.
- Pointers are used for the dynamic memory allocation of a variable.
In these examples a pointer variable is declared to point to a variable of the specified data type.
Although all these pointers
point to different data types,
they will occupy the same amount of space in the memory.
t how much space they will occupy
will depend on the platform where the code is going to run. On ARM microcontrollers they will occupy 4bytes, in other words 32-bits. Remember 1 byte is equal to 8-bits.
In this statement, ptr is the name of the pointer variable. The * informs the compiler that
ptr is a pointer variable and the int32_t
specifies that it will store the address of a 32-bit integer variable.
A 32-bit integer pointer variable, therefore, ‘points to’ a 32-bit integer variable. In the last statement,
ptr is assigned the address of x. The & operator retrieves the lvalue (meaning address) of x, and copies that to the
contents of the pointer ptr. Consider the memory cells
Lets take a look at this diagram. Since x is a 32-bit or 4-bytes integer variable it will be allocated 4byts of memory. Assuming that the compiler
assigns it memory locations 1003,1004,1005 and 1006,
the address of x (written as &x) is equal to 1003, that
is the starting address of x in the memory. When we write, ptr = &x, then ptr = 1003.
We can ‘dereference’ a pointer, meaning, we can refer to the value of the variable to which it points
by using the unary * operator as in *ptr. That is, *ptr = 10, since 10 is the value of x.
Like other variables, pointer variables can also be used in expressions. For instance, if ptr1 and ptr2 are pointers, then following statements here are valid.
We can add integers or subtract integers from pointers as well as subtract one pointer from another.
We can also compare pointers by using relational operators in the expressions, such as, is pointer1 > pointer2 and so on.
Postfix unary increment which is written as plus-plus and decrement which written as minus-minus operators have greater precedence than the dereference operator which is written as asterisk.
Therefore, the expression *ptr++ is equivalent to *(ptr++), as plus-plus has greater operator precedence than asterisk. Thus, the expression will increase the value of ptr so that it now points to the next memory location. This means that the statement *ptr++ does not do the intended task. Therefore, to increment the value of the variable whose address is stored in ptr, we must write (*ptr)++.
A generic pointer is a pointer variable that has void as its data type. The void pointer, or the generic
pointer, is a special type of pointer that can point to variables of any data type. It is declared like
a normal pointer variable but using the
keyword as the pointer’s data type. Like this is :
Since we cannot have a variable of type void, the void pointer will therefore not point to any data and, thus cannot be dereferenced. We need to cast a void pointer to another kind of pointer before using it.
We use this type of pointer when we want a pointer to point to data of different types at different times.
We can also use pointers that point to pointers. The pointers in turn point to data or even other pointers. To declare pointers to pointers, we simply need to add an asterisk for each level of reference.
If we assume the memory locations of x,px and pxx are as shown in this diagram, then when we execute asterisk-asterisk ppx we get the answer 10.