ADVANCED TAKE ON
POINTERS IN C
by: Sven Gregori
101 Comments
April 19, 2018
In our first part on pointers, we covered the basics and common pitfalls of
pointers in C. If we had to break it down into one sentence, the
main principle of pointers is that they are simply data types storing
a memory address, and as long as we make sure that we have
enough memory allocated at that address, everything is going to
be fine.
The one proverbial exception to the rule that pointers are just
memory addresses is the most (in)famous pointer of all:
the NULL pointer. Commonly defined as preprocessor macro (void
*) 0 , we can assign NULL like any other pointer.
POINTER ARITHMETIC
Other than NULL , the concept remains that pointers are simply
memory addresses — in other words: numbers. And like any other
number, we can perform some basic arithmetic operations with
them. But we wouldn’t talk about it if there wasn’t more to it, so
let’s see for ourselves what happens when we add 1 to a couple of
different pointer types.
1 char *cptr = (char *) 0x1000;
2 int *iptr = (int *) 0x2000;
3 struct foo *sptr = (struct foo *) 0x3000;
4
5 printf("char 0x%02lx %p %p\n", sizeof(char), cptr, (cptr + 1
6 printf("int 0x%02lx %p %p\n", sizeof(int), iptr, (iptr + 1)
7 printf("struct 0x%02lx %p %p\n", sizeof(struct foo), sptr, (sp
Since an int was four bytes, we can fully fit two of them in the 8
bytes offset, therefore the subtraction will output 2 . Note that
the sizeof operator is one exception that doesn’t follow pointer arithmetic rules, but only deals in
bytes. As a result, the second output will show the full 8 bytes of the offset. The result of such
a subtraction will be of type ptrdiff_t , a platform dependent
integer type defined in stddef.h . The sizeof operator will output
its size accordingly, for example 8 bytes.
That’s pretty much all there is to know about the basics of pointer
arithmetic. Trying anything other than addition with an integer, or
subtraction with either an integer or another pointer of the same
type will result in a compiler error.
Note that all this applies only to already declared arrays. Once an
array is declared, pointers give us an alternative way to access
them, but we cannot replace the array declaration itself with a
simple pointer because the array declaration also reserves
memory.
POINTERS TO POINTERS
As we have well established, pointers can point to any kind of data
type, which includes other pointer types. When we declare char
**ptr , we declare nothing but a pointer whose underlying data
type is just another pointer, instead of a regular data type. As a
result, dereferencing such a double pointer will give us a char
* value, and dereferencing it twice will get us to the actual char .
The other way around, &ptr gives us the pointer’s address, just
like with any other pointer, except the address will be of type char
*** , and on and on it goes. As stated earlier, C uses call by value when
passing parameters to a function, but adding an extra layer of
pointers can be used to simulate call by reference.
Along with argv , we get argc passed to main() , which tells us the
number of entries in argv . And as a reminder about array
decay, argv[i] is equal to &argv[i][0] .
TO BE CONTINUED
To summarize our second part on pointers in C: pointer arithmetic
happens always relative to the underlying data type, operator
precedence needs to be considered or tackled with parentheses,
and pointers can point to other pointers and other pointers as deep
as we want.
In the next and final part, we are going to have a look at possibly
the most exciting and most confusing of pointers: the function
pointer.