Pointer Basics in C: What Is A Pointer?
Pointer Basics in C: What Is A Pointer?
Pointer Basics in C: What Is A Pointer?
The real power of C lies in pointers. The pointers are slightly difficult to grasp at first. After going
through the basics of pointers, you will get a better idea about what they are and how to use them.
What is a Pointer? #
A pointer is a variable used to store a memory address. Let's first learn how memory is organized
inside a computer.
int marks;
As we know an int occupies 4 bytes of data (assuming we are using a 32-bit compiler) , so compiler
reserves 4consecutive bytes from memory to store an integer value. The address of the first byte of
the 4 allocated bytes is known as the address of the variable marks. Let's say that address
of 4 consecutive bytes are 5004, 5005, 5006and 5007 then the address of the variable marks will
be 5004.
Address Operator (&) #
To find the address of a variable, C provides an operator called address operator (&). To find out the
address of the variable marks we need to place & operator in front of it, like this:
&marks
The following program demonstrates how to use address operator (&).
// Program to demonstrate address(&) operator
#include<stdio.h>
int main()
{
int i = 12;
**How it works ? **
To find the address of the variable, precede the variable name by & operator. Another important
thing to notice about the program is the use of %u conversion specification. Recall
that %u conversion specification is used to print unsigned decimal numbers and since the memory
addresses can't be negative, you must always use %uinstead of %d.
Address of operator (&) can't be used with constants or expression, it can only be used with a
variable.
&var; // ok
int *ip;
float *fp;
int *ip means that ip is a pointer variable capable of pointing to variables of type int. In other words,
a pointer variable ip can store the address of variables of type int only. Similarly, the pointer
variable fp can only store the address of a variable of type float. The type of variable (also known as
base type) ip is a pointer to int and type of fp is a pointer to float. A pointer variable of type pointer
to int can be symbolically represented as (int *). Similarly, a pointer variable of type pointer to float
can be represented as (float *).
Just like other variables, a pointer is a variable so, the compiler will reserve some space in memory.
All pointer variable irrespective of their base type will occupy same space in memory.
Normally 4 bytes or 2 bytes (On a 16-bit Compiler) are used to store a pointer variable (this may vary
from system to system).
Assigning Address to Pointer Variable #
After declaring a pointer variable the next step is to assign some valid memory address to it. You
should never use a pointer variable without assigning some valid memory address to it, because just
after declaration it contains garbage value and it may be pointing to anywhere in the memory. The
use of an unassigned pointer may give an unpredictable result. It may even cause the program to
crash.
ip = &i;
fp = &f;
Here ip is declared as a pointer to int, so it can only point to the memory address of an int variable.
Similarly, fp can only point to the address of a float variable. In the last two statements, we have
assigned the address of i and f to ip and fp respectively. Now ip points to variable i and fp points to
variable f. It is important to note that even if you assign an address of a float variable to
an int pointer, the compiler will not show you any error but you may not get the desired result. So as
a general rule always you should always assign the address of a variable to its corresponding pointer
variable of the same type.
We can initialize pointer variable at the time of declaration, but in this case, the variable must be
declared and initialized before the pointer variable.
You can assign the value of one pointer variable to another pointer variable If their base type is same.
For example:
p1 = &marks;
p2 = p1;
After the assignment p1 and p2 points to the same variable marks.
[img]
As already said when a pointer variable is declared it contains garbage value and it may be point
anywhere in the memory. You can assign a symbolic constant called NULL (defined in stdio.h) to
any pointer variable. The assignment of NULL guarantees that pointer doesn't point to any valid
memory location.
int i = 100, *iptr;
iptr = NULL;
Dereferencing Pointer Variable #
Dereferencing a pointer variable simply means accessing data at the address stored in the pointer
variable. Up until now, we have been using the name of the variable to access data inside it, but we
can also access variable data indirectly using pointers. To make it happen, we will use a new operator
called indirection operator (*). By placing indirection operator (*) before a pointer variable we can
access data of the variable whose address is stored in the pointer variable.
int i = 100, *ip = &i;
Here ip stores address of variable i, if we place * before ip then we can access data stored in the
variable i. It means following two statements does the same thing.
printf("%d\n", *ip); // prints 100
printf("%d\n", i); // prints 100
Indirection operator (*) can be read as value at the address. For example, *ip can be read as value at
address ip.
Note: It is advised that you must never apply indirection operator to an uninitialized pointer variable,
doing so may cause unexpected behavior or the program may even crash.
int *ip;
printf("%d", *p); // WRONG
Now we know by dereferencing a pointer variable, we can access the value at the address stored in
the pointer variable. Let's dig a little deeper to view how the compiler actually retrieves data.
char ch = 'a';
int i = 10;
double d = 100.21;
char *cp = &ch;
int *ip = &i;
double *ip = &d;
Let's say pointer cp contains the address 1000. When we write *cp the compiler knows that it has to
retrieve information from the starting address 1000. Now the question arises how much data to
retrieve from starting address 1000 ? 1 bytes, 2 bytes; What do you think ? To know how much
information to retrieve from starting address 1000, the compiler looks at the base type of pointer and
will retrieve the information depending upon the base type of pointer. For example, if the base type is
pointer to char then1byte of information from the starting address will be retrieved and if the base
type pointer tointthen4bytes of information from the starting address will be retrieved. It is important
to note that if you are on a system where the size ofintis2bytes then2` bytes of information from the
starting address will be retrieved.
So in our case, only 1 byte of data from starting address will be retrieved. i.e the data stored at
address 2000will be retrieved only.
Similarly, if ip points to the address 2000. On writing *ip compiler will retrieve 4 bytes of data
starting from address 2000.
In the following image, shaded portion shows the number of bytes retrieved.
#include<stdio.h>
int main()
{
int i = 12, *ip = &i;
double d = 2.31, *dp = &d;
Address of ip = 2686784
Address of dp = 2686772
Size of pointer ip = 4
Size of pointer dp = 4
Note: Memory address may vary every time you run the program.
There is nothing new in the above program that deserves any explanation. Before we proceed to next
chapter, always remember that the size of the pointer variables is same regardless of its base type but
the size of the memory address that will be accessed while dereferencing depends on upon the base
type of the pointer variable.
Pointer Arithmetic in C
You should know by now that a pointer is nothing more that a variable used to store a memory
address. And if this is new to you go back and read Pointer Basics before continuing with this
chapter. In this chapter, we will discuss arithmetic operations that can be performed on pointers.
We can't perform every type of arithmetic operations with pointers. Pointer arithmetic is slightly
different from arithmetic we normally use in our day to day life. The only valid arithmetic operations
applicable on pointers are:
#include<stdio.h>
int main()
{
int i = 12, *ip = &i;
double d = 2.3, *dp = &d;
char ch = 'a', *cp = &ch;
Value of ip + 1 = 2293320
Value of dp + 1 = 2293312
Value of cp + 1 = 2293304
Value of ip + 2 = 2293324
Value of dp + 2 = 2293320
Value of cp + 2 = 2293305
Pointer Arithmetic between Two Pointers #
If we have two pointers p1 and p2 of base type pointer to int with
addresses 1000 and 1016 respectively, then p2 - p1 will give 4, since the size of int type is 4 bytes. If
you subtract p2 from p1 i.e p1 - p2 then answer will be negative i.e -4.
The following program demonstrates pointer arithmetic between two pointers of the same type.
#include<stdio.h>
int main()
{
int i1 = 12, *ip1 = &i1;
int i2 = 12, *ip2 = &i2;
ip2 - ip1 = 2
ip1 - ip2 = -2
Combining Indirection operator (*) and Increment/Decrement
operator #
While processing elements of an array (as you will see in the next chapter) C programmers often mix
indirection operator (*) and increment/decrement operator( ++ and -- ).
Always remember that the precedence of indirection operator (*) and increment/decrement operator
are same and they associate from right to left.
Suppose x is integer variable and p is a pointer to int. Now consider the following statements and try
to interpret them.
Example 1:
x = *p++;
Since * and ++ operators have the same precedence and associate from right to left ++ will be
applied to p not to *p. Because the increment operator is postfix, so first the value of p is used in the
expression then it will be incremented. Therefore first integer pointed by p will be dereferenced and
assigned to x, then the value of p will be incremented by 1.
Example 2:
x = ++*p;
Here * operator will be first applied to p then ++ will be applied to *p. Therefore first integer pointer
is dereferenced, the value obtained from dereferencing is incremented and eventually assigned to x.
Example 3:
x = *++p;
++ operator is prefixed, so first, p will be incremented, then the value at the new address is
dereferenced and assigned to x.
Note: If you still have any confusion, you can always use () around expression which you want to
evaluate first.
Pointer Comparison #
You can use relational operators ( <, <=, >, >= , == , !=) with pointers. The == and !=operators are
used to compare two pointers whether they contain the same address or not. Two pointers are equal
when they both are null, or contains the address of the same variable. Use of these (i.e == and !=)
operators are valid only when pointers are of the same base type, or between a null pointer and any
other pointer, or void pointer(will be discussed later) and any other pointer. Use of other relational
operators (<, <=, >, >=) to compare two pointers is meaningful only when they both point to the
elements of the same array.
Pointer to Pointer #
As we know the pointer is a variable that contains the memory address. The pointer variable itself
occupies some space in memory and hence it also has a memory address. We can store the address of
pointer variable in some other variable, which is known as pointer to pointer. The syntax of declaring
pointer to pointer is as follows:
int i = 10;
int *ip = &i;
int **iip = &ip;
Here ip is of type (int *) or pointer to int, iip is of type (int **) or pointer to pointer to int.
We know that *ip will give value at address ip i.e value of i. Can you guess what value will **iip will
return ?
**iip
We know that indirection operator evaluated from right to left so **iip can also be written as
*(*iip)
*iip means value at address iip or address stored at ip. On dereferencing address stored at ip we will
get the value stored in the variable i.
*(*iip)
=> *ip
=> i
Therefore **iip gives the value stored in the variable i.
#include<stdio.h>
int main()
{
int i = 10;
int *ip = &i;
int **iip = &ip;
Address of i = 2293332
Value of ip = 2293332
Address of ip = 2293320
Value of iip = 2293320
Here the first element is at address 5000, since each integer takes 4 bytes the next element is
at 5004 and so on.
In C, pointers and arrays are very closely related. We can access the elements of the array using a
pointer. Behind the scenes compiler also access elements of the array using pointer notation rather
than subscript notation because accessing elements using pointer is very efficient as compared to
subscript notation. The most important thing to remember about the array is this:
The name of the array is a constant pointer that points to the address of the first element of the array
or the base address of the array.
We can use subscript notation (i.e using square brackets) to find the address of the elements of the
array. For example:
The following program demonstrates that the elements of an array are stored in contiguous memory
locations.
#include<stdio.h>
int main()
{
int my_arr[5] = {1, 2, 3, 4, 5}, i;
Similarly,
The following program prints value and address of array elements using pointer notation.
#include<stdio.h>
int main()
{
int my_arr[5] = {1, 2, 3, 4, 5}, i;
int *p;
int my_arr[] = {11, 22, 33, 44, 55};
p = my_arr;
Now you can use pointer p to access address and value of each element in the array. It is important to
note that assignment of a 1-D array to a pointer to int is possible because my_arr and p are of the
same base type i.e pointer to int. In general (p+i) denotes the address of the ith element
and *(p+i) denotes the value of the ith element.
There are some differences between the name of the array (i.e my_arr) and pointer variable (i.e p).
The name of the array is a constant pointer hence you can't alter it to point to some other memory
location. You can’t assign some other address to it nor you can apply increment/decrement operator
like you do in a pointer variable.
my_arr++; // error
my_arr--; // error
my_arr = &i // error
However, p is an ordinary pointer variable, so you can apply pointer arithmetic and even assign a
new address to it.
p++; // ok
p--; // ok
p = &i // ok
The following program demonstrates how you can access values as address of elements of a 1-D
array by assigning it to a pointer variable.
#include<stdio.h>
int main()
{
int my_arr[5] = {1, 2, 3, 4, 5}, i;
int *p;
p = my_arr;
// p = &my_arr[0]; // you can also do this
for(i = 0; i < 5; i++)
{
printf("Value of a[%d] = %d\t", i, *(p + i) );
printf("Address of a[%d] = %u\n", i, p + i );
}
A pointer that points to the 0th element of an array and a pointer that points to the whole array are
totally different. The following program demonstrates this concept.
#include<stdio.h>
int main()
{
int *p; // pointer to int
int (*parr)[5]; // pointer to an array of 5 integers
int my_arr[5]; // an array of 5 integers
p = my_arr;
parr = my_arr;
printf("Address of p = %u\n", p );
printf("Address of parr = %u\n", parr );
p++;
parr++;
Address of p = 2293300
Address of parr = 2293316
**How it works ? **
Here p is a pointer which points to the 0th element of the array my_arr, while parr is a pointer which
points to the whole array my_arr. The base type of p is of type (int *) or pointer to int and base type
of parr is pointer to an array of 5 integers. Since the pointer arithmetic is performed relative to the
base type of the pointer, that's why parr is incremented by 20 bytes i.e ( 5 x 4 = 20 bytes ). On the
other hand, p is incremented by 4bytes only.
The important point you need to remember about pointer to an array is this:
Whenever a pointer to an array is dereferenced we get the address (or base address) of the array to
which it points.
So, on dereferencing parr, you will get *parr. The important thing to notice is
although parr and *parr points to the same address, but parr's base type is a pointer to an array
of 5 integers, while *parr base type is a pointer to int. This is an important concept and will be used
to access the elements of a 2-D array.
Pointers and 2-D Array #
While discussing 2-D array in the earlier chapters, we told you to visualize a 2-D array as a matrix.
For example:
int arr[3][4] = {
{11,22,33,44},
{55,66,77,88},
{11,66,77,44}
};
While discussing array we are using terms like rows and column. Well, this concept is only
theoretical, because computer memory is linear and there are no rows and cols. So how actually 2-D
arrays are stored in memory ? In C, arrays are stored row-major order. This simply means that first
row 0 is stored, then next to it row 1 is stored, next to it row 2 is stored and so on.
The following figure shows how a 2-D array is stored in the memory.
Here is the most important concept you need to remember about a multi-dimensional array.
A 2-D array is actually a 1-D array in which each element is itself a 1-D array. So arr is an array of 3
elements where each element is a 1-D array of 4 integers.
In the previous chapter, we have already discussed that the name of a 1-D array is a constant pointer
to the 0th element. In the case, of a 2-D array, 0th element is a 1-D array. Hence in the above
example, the type or base type of arr is a pointer to an array of 4 integers. Since pointer arithmetic is
performed relative to the base size of the pointer. In the case of arr, if arr points to
address 2000 then arr + 1 points to address 2016 (i.e 2000 + 4*4) .
We know that the name of the array is a constant pointer that points to the 0th element of the array.
In the case of a 2-D array, 0th element is a 1-D array. So the name of the array in case of a 2-D array
represents a pointer to the 0th 1-D array. Therefore in this case arr is a pointer to an array
of 4 elements. If the address of the 0th 1-D is 2000, then according to pointer arithmetic (arr + 1) will
represent the address 2016, similarly (arr + 2) will represent the address 2032.
arr points to 0th 1-D array. (arr + 1) points to 1st 1-D array. (arr + 2) points to 2nd 1-D array.
So how you can use arr to access individual elements of a 2-D array?
Since *(arr + i) points to the base address of every ith 1-D array and it is of base type pointer to int,
by using pointer arithmetic we should we able to access elements of ith 1-D array.
*(arr + i) points to the address of the 0th element of the 1-D array. So,
*(arr + i) + 1 points to the address of the 1st element of the 1-D array
*(arr + i) + 2 points to the address of the 2nd element of the 1-D array
Hence we can conclude that:
*(arr + i) + j points to the base address of jth element of ith 1-D array.
On dereferencing *(arr + i) + j we will get the value of jth element of ith 1-D array.
*(*(arr + i) + j)
By using this expression we can find the value of jth element of ith 1-D array.
The following program demonstrates how to access values and address of elements of a 2-D array
using pointer notation.
#include<stdio.h>
int main()
{
int arr[3][4] = {
{11,22,33,44},
{55,66,77,88},
{11,66,77,44}
};
int i, j;
Here is an example:
int arr[2][3] = {
{33, 44, 55},
{11, 99, 66}
};
Always remember a 2-D array is actually a 1-D array where each element is a 1-D array. So arr as an
array of 2elements where each element is a 1-D arr of 3 integers. Hence to store the base address
of arr, you will need a pointer to an array of 3 integers.
Similarly, If a 2-D array has 3 rows and 4 cols i.e int arr[3][4], then you will need a pointer to an
array of 4integers.
int (*p)[3];
Here p is a pointer to an array of 3 integers. So according to pointer arithmetic p+i points to the ith 1-
D array, in other words, p+0 points to the 0th 1-D array, p+1 points to the 1st 1-D array and so on.
The base type of (p+i) is a pointer to an array of 3 integers. If we dereference (p+i) then we will get
the base address of ith 1-D array but now the base type of *(p + i) is a pointer to int or (int *). Again
to access the address of jth element ith 1-D array, we just have to add j to *(p + i). So *(p + i) +
j points to the address of jth element of ith 1-D array. Therefore the expression *(*(p + i) + j) gives
the value of jth element of ith 1-D array.
The following program demonstrates how to access elements of a 2-D array using a pointer to an
array.
#include<stdio.h>
int main()
{
int arr[3][4] = {
{11,22,33,44},
{55,66,77,88},
{11,66,77,44}
};
int i, j;
int (*p)[4];
p = arr;
The following is an instantiation of the various "just print it" suggestions. I found it
instructive.
#include "stdio.h"
int main() {
static int x = 5;
static int *p = &x;
printf("(int) p => %d\n",(int) p);
printf("(int) p++ => %d\n",(int) p++);
x = 5; p = &x;
printf("(int) ++p => %d\n",(int) ++p);
x = 5; p = &x;
printf("++*p => %d\n",++*p);
x = 5; p = &x;
printf("++(*p) => %d\n",++(*p));
x = 5; p = &x;
printf("++*(p) => %d\n",++*(p));
x = 5; p = &x;
printf("*p++ => %d\n",*p++);
x = 5; p = &x;
printf("(*p)++ => %d\n",(*p)++);
x = 5; p = &x;
printf("*(p)++ => %d\n",*(p)++);
x = 5; p = &x;
printf("*++p => %d\n",*++p);
x = 5; p = &x;
printf("*(++p) => %d\n",*(++p));
return 0;
}
It returns
(int) p => 256688152
(int) p++ => 256688152
(int) ++p => 256688156
++*p => 6
++(*p) => 6
++*(p) => 6
*p++ => 5
(*p)++ => 5
*(p)++ => 5
*++p => 0
*(++p) => 0
I cast the pointer addresses to ints so they could be easily compared.
I compiled it with GCC.