To share this page click on the buttons below;
Pointers
Keep in mind that everything (variables, functions, structures, unions and so on) in a C program is just an address and an associate space in memory: that would simplify the comprehension of pointers.
Pointers are a very important concept of the C language and, after all, they are not a complex argument although they are often hard to be understood.
A pointer is a variable which contains an address of memory. Since all the elements of a program (variables, functions, structures and so on) are stored somewhere in memory at a certain address (that identifies every particular element we use), having a variable containing an address, means that a pointer is a sort of handle through which you can access whatever you want within a program.
The address itself though is not enough, every element has an address and a size, which depends on the type of the element, for this reason you have different types of pointers to deal with that. That is probably still unclear but we are going to clarify all the details just below.
Declaring a pointer
Remember the simple definition of a pointer: a pointer is a variable which contains the address of some element of the program.
Since a pointer is a variable you need to declare it, as for every variable.
You declare a certain type of pointer by adding the symbol * after the type .
So starting from the declaration of an integer variable like int a;
we obtain the declaration of the pointer to an integer pt_int
in the follow way: int *pt_int
.
As you can see the declaration of the pointer is very simple: just add an * after the variable type and before the variable name (there is always a space between the type and the * and there is usually no space between the * and the variable name).
Defining a pointer
The variable a
can contain integers values, what can contain the pointer pt_int? Simple: the address of a variable which contains an integer.
This leads to some questions, the first one probably is: how can I get the address of a variable? In a pretty simple way: using the operator & before the name of the variable. So you can define a pointer to an integer with something like:
int a;
int *pt_int;
a = 5;
pt_int = &a; /* pt_int will contain the address of a */
Now the pointer pt_int points to the variable a. So the pointer gives you an handle to that variable. In this way you can access the variable a through the pointer and to do that you apply the operator * (the same you used for the declaration of the pointer) to the pointer:
/* both print the value of 5 */
printf("%i\n", a);
/* use the * to access the value of variable pointer by pt_int */
printf("%i\n", *pt_int);
In the second printf
we access the value of the variable pointed by pt_int
(which is a
in this case) by writing *pt_int
, but we can also modify the value hold by a
with_ *pt_int = 12;
, after this statement the content of a
is no more 5 but 12.
If you are thinking that you might obtain the same result by simply printing the variable a
and by changing the value of a
itself, you are missing the power of the pointers: a pointer to an integer can access whatever integer variable, not only the one is pointing to, because it can be redirected to whatever other integer variable.
Pointers to other types
Just for completeness I add here the declarations of pointers for every basic C type:
char c;
char *pt_char = &c;
float fl;
float *pt_float = &fl;
double dbl;
double *pt_double = &dbl;
Obviously a pointer to a char can contain the address of any char variable, a pointer to float can contain the address of any float variable and a pointer to a double can contain the address of any double variable.
The operator & always gives the address of a certain variable (whatever is the type) and through the operator * applied to a pointer (which has previously directed to a variable) you access the value of the variable pointed by the pointer.
NULL
A pointer that is not assigned to any particular variable (for example after its declaration and before its definition) where does it point? Probably to some random address which was the content of the memory where the pointer is allocated by the compiler (because, obviously, also a pointer resides somewhere in memory and it has its own address).
Using a not initialized pointer is very dangerous because you can access a random portion of the memory and change it: you can change the value of some variable or, even worst, you can change the code of some function.
In this case you need something to remember that a pointer is not initialized yet. This something is NULL
. NULL
is a constant (of value 0) which is defined in some header file of the standard C library (it is defined in more than 1 header file) and you usually get its definition by including <sdtio.h>
.
All the pointers before being assigned to the address of an actual variable are initialized to NULL
. That means: the pointer is not ready, you have an handle which is not attached to anything yet.
You cannot access a pointer to NULL (i.e. you cannot use the operator * to manipulate the value of the variable pointed by the pointer).
If you do that you program will crash because this is equivalent to read or write a variable that does not exist.
So here it comes 2 important rules to safely deal with pointers:
- always initialize your pointer to
NULL
(or to some actual variable) - always check if the pointer is NOT
NULL
before to access it.
A small terminological note: the action to access the variable content through a pointer using the* operator is called indirection or dereferencing the pointer.
The size of a pointer
Which is the size of a pointer variable? It depends on how big is an address (because a pointer always contains an address) and that depends on the processor. Roughly speaking (there might be some particular cases) the size of an address is equal to the "number of bit" of the processor. So on a 8 bits processor addresses are stored in just one byte, on a 32 bits processor you need 4 bytes (32 bits indeed) and on a 64 bits processor you need 8 bytes (64 bits indeed).
Why? Well the answer to that question is a little bit long, so I will try to keep it short: because the processor addresses the memory (remember that the memory is just a set of boxes numbered with an unsigned integer address) using its registers. Registers are particularly locations inside the processor that are used to configure the processor or to run a certain program: the processor fetches the memory to read what it has to do and put the content of the memory inside its registers to actual execute what is contained in the memory. The "number of bit" of the processor defines the maximum length in bit of its registers. When the process access a particular location of the memory it writes the address it wants to access in one of each register, then here we are: the maximum dimension of an address that a processor can address is equal to the dimension of its registers.
That also tell us another important thing: the maximum addressable memory space of a processor (i.e. how big is the memory the processor can use) depends on the "number of bit" of the processor. A 32 bit processor can access up to 4 gigabytes of memory because 4 gigabytes is the maximum unsigned number you can write using 32 bits (obviously it makes no sense to have a 32 bit processor with more than 4 gigabyte, to use more memory you need a 64 bits processor).
However the important statement is that when you fix the processor, the size of a pointer variable is always the same whatever is the type of the pointer. On a 32 bit processor a pointer to integer or a pointer to char or a pointer to double have all the same size (32 bit) but the variables they are pointing have very different sizes (8 bits for a char, 32 bits for an integer and 64 bits for a double). You can easily verify that using the sizeof operator (which can be applied to a pointer too of course).
Mixing up pointers
What happens if you make a pointer to a double to point to an integer variable? (Of course the question is valid for each pointer type made to point to a different variable type). You obtain a warning during compiling time (which is just a shorthand to indicate the moment in which you are compiling your code) which says that you might have been done something wrong, nevertheless you got your executable anyway. In this case what truly happen is that when you access the integer variable through a double pointer the pointer traits the memory as it was a double. That is something you usually do not want to happen, but it has surprisingly some important applications we are going to see in the future.
To share this page click on the buttons below;