0% found this document useful (0 votes)
14 views

7 Pointers

The document discusses pointers in C programming. Pointers are variables that store memory addresses. They allow accessing and modifying values at specific memory locations indirectly through dereferencing. Pointer arithmetic and operations like addition, subtraction, increment and decrement on pointers are also covered.

Uploaded by

narasimhan
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
14 views

7 Pointers

The document discusses pointers in C programming. Pointers are variables that store memory addresses. They allow accessing and modifying values at specific memory locations indirectly through dereferencing. Pointer arithmetic and operations like addition, subtraction, increment and decrement on pointers are also covered.

Uploaded by

narasimhan
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 16

CS100 : Computer Programming

Chapter 11 - Pointers

Narasimhan T.

11.1 Introduction
Computer’s memory is a sequential collection of locations where each location can store 1 byte
of data. To distinguish among the locations, the locations are assigned unique addresses starting
from 0 to a maximum value that depends on the size of memory. Say, you have a 4GB (232
bytes) memory, then the addresses vary from 0 to 4294967295 (which is 232 − 1). When you
declare a variable, sufficient amount of memory is allocated for it depending on its type. For
example, an int variable takes 4 bytes. Thus to store an int value, you need 4 locations in
memory and typically 4 contiguous locations are allocated.

0
1
2
..
.
1004
1005
1006 45 a
1007

..
.
..
.

4008 A c

..
.
..
.
8190
8191

Figure 11.1: Memory view of a hypothetical computer

Assume we have a hypothetical computer with just 8KB memory. The locations are
assigned addresses from 0 to 8191 as shown in Figure 11.1. Now suppose we have the following
statements in a program:

1
int a = 45;
char c = 'A';
While executing these instructions, 4 bytes of memory (say locations 1004 to 1007) will be
allocated by the system for the variable a and the value 45 will be stored over these locations.
Similarly, for the variable c, one byte (say location 4008) will be allocated and the character ’A’
is stored in it. The address of a variable is the address of the first location in the set of locations
allocated to it. Thus, with the memory map as shown in Figure 11.1, address of a is 1004 and
that of c is 4008.
Now, the addresses are numbers (typically unsigned integers). So they can be stored in
some variable. To make things clear, let us store the address of a in another variable, say p. Thus
p contains the address of a and we say p points to a or p refers to a or p references
a. Such variables that store a memory address (typically location of some other variable) are
called pointer variables or simply pointers. Here p is a pointer variable.

11.2 Pointer declaration and initialization


The syntax for declaring a pointer variable is this:
type *pname;

where type is the data type of the pointer and pname is a pointer variable. The data type
of a pointer will be the data type of the variable whose address it stores. With the previous
discussion, p stores the address of a, thus the data type of p is int. (type of a). Thus you
declare p as
int *p;

Once a pointer variable is declared, you can store the address of a variable in it. To get the
address of any variable, C provides the & operator which is called reference operator or address
of operator. Thus to store the address of a in p, you write
p=&a;
One point to note is that the data types of the pointer and that of the variable whose address
is stored in the pointer should match. The following code will yield you an error for the fact
just mentioned.
int a;
float *fp;
fp=&a;
The format specifier to print address is %p. Thus to print the address of a, you write
printf("The address of variable a is %p",&a);
This will print the address in hexadecimal format.

11.3 Accessing a variable through pointer


Assume you have the following statements

2
int a,*p;
p=&a;
Thus p points to a. Now you can print a’s value using p. For this you use the * operator which
is called dereferencing operator or value at operator. Thus the statement
printf("%d", *p);
will print the value of a. The * operator is powerful in that you can use to change the value
stored at a particular location. See the example below.

Example 11.1. This example illustrates how you can use * operator to change the value at a
location.
int a,*p;
a=45;
p=&a;
printf("The value of a is %d\n",*p);
*p=25;
printf("The value of a now is %d\n",*p);
This code outputs
The value of a is 45
The value of a now is 25

11.4 Pointer Arithmetic


A pointer is an address, which is a numeric value. Therefore, you can perform arithmetic
operations on a pointer just as you can do on any numeric value. But pointers do not allow all
arithmetic operations.

11.4.1 Addition
Assume you have a pointer variable p. To add a constant k to p, you add k × n to p, where n
denotes the size of the data type of p. The following example should make the ideas concrete.

Example 11.2. Suppose a program has the following statements


int a,*iptr;
double b,*dptr;
iptr=&a;
dptr=&b;
iptr=iptr+3;
dptr=dptr+1;
The values of the pointer variables before and after the addition operations are shown in Ta-
ble 11.1. In the table, Initial value and Final value represent the pointer value before and after
the addition respectively. Note that addresses are typically represented in hexadecimal format.
So when you add 12 to 4008, you get 4014 (4008, 4009, 400A, 400B, ...400F, 4010, 4011, ...4014).

The following points are noteworthy regarding addition operation on pointers:

3
1. Only a constant can be added to a pointer variable.

2. Addition of two pointers is forbidden.

3. Pointer addition is commutative i.e., ptr + 3 is equivalent to 3 + ptr, ehere ptr is a


pointer variable.

Table 11.1: Addition of pointer and a constant

Value of pointer
Expression
Initial value Final value
4014
iptr+3 4008
( = 4008 + 3 × 4)
100A
dptr+1 1002
( = 1002 + 1 × 8)

11.4.2 Subtraction
To subtract a constant k from a pointer p, you subtract k × n from p, where n denotes the size
of the data type of p. See Example below:

Example 11.3. Suppose a program has the following statements


int a,*iptr;
char c,*cptr;
iptr=&a;
cptr=&c;
iptr=iptr-2;
cptr=cptr-4;
The values of the pointer variables before and after the subtraction operations are shown in
Table 11.2. ■

Table 11.2: Subtraction of a constant from a pointer

Value of pointer
Expression
Initial value Final value
1000
iptr-2 1008
( = 1008 − 2 × 4)
4002
cptr-4 4006
( = 4006 − 4 × 1)

Subtraction operation is not commutative, i.e., ptr - 5 and 5 - ptr are not equivalent.
Moreover, the second case : subtraction of pointer from a constant is not permitted.
Two pointers of same type can be subtracted. Let p1 and p2 be two pointers of same type. To
p1 − p2
subtract p2 from p1, you perform , where n denotes the size of the data type of p1 or
n
p2. The operation is meaningful only if the pointers point to elements of same array. In this
case, the result of the operation is the difference between the subscripts of the two elements. In
other words, the result denotes the number of elements between the pointers.

4
p1 p2

1.2 5.1 8.3 12.9


2000 2004 2008 200C
Figure 11.2: Memory view of array in Example 11.4

Example 11.4. Consider the code snippet


float *p1,*p2,arr[]={1.2,5.1,8.3,12.9};
int a;
a=p2-p1;
If the array is stored in memory as shown in Figure 11.2, then the value of a is found as
2008 − 2000
a= = 2. Thus you have 2 elements between the pointers.
4

11.4.3 Increment and decrement


Incrementing a pointer is equivalent to adding 1 to it and decrementing a pointer is equivalent
to subtracting 1 from it.

Example 11.5. Suppose you have the following statements in a program


float *ptr1,*ptr2,*rptr,*optr;
rptr=++ptr1;
optr=ptr2++;
The values of these pointers after executing the code is tabulated below. The table also shows
the initial values of the pointers.
Pointer Initial value Final value
ptr1 2000 2004
ptr2 6000 6004
rptr 5004 2004
optr 4002 6000

Example 11.6. Suppose you have the following statements in a program


float *ptr1,*ptr2,*rptr,*optr;
rptr=--ptr1;
optr=ptr2--;
The values of these pointers after executing the code is tabulated below. The table also shows
the initial values of the pointers.
Pointer Initial value Final value
ptr1 8006 8002
ptr2 1024 1020
rptr 4004 8002
optr 2008 1024

5
11.4.4 Relational operators
A pointer can be compared with another pointer of same type or with zero. It cannot be
compared with any non-zero constant. The result of comparison is either 0 (FALSE) or 1
(TRUE).
Example 11.7. Assume you have the statements in a program
float *p1,*p2;
int p,q,r;
p=p1!=p2;
q=p1==0;
r=p1>p2;
If p1 has the value 4000 and p2 has 4008, the values of p, q, r will be
p = 1, q = 0, r = 0

Note:

• Multiplication, division, modulus operations are not allowed on pointers.

• Bitwise operations are also not allowed on pointers.

11.5 Chain of pointers


Just as an ordinary variable has its address, a pointer variable too have its own address which
can be assigned to another pointer variable. Thus you can have a pointer point to another
pointer that points to a target variable. This is called chain of pointers or multiple indirection.
Figure 11.3 helps clarify the concept of multiple indirection. As you can see, the value of a
normal pointer is the address of the variable that contains the desired value. In the case of a
pointer to a pointer, the first pointer contains the address of the second pointer, which points
to the variable that contains the desired value.
Multiple indirection can be carried on to whatever extent desired, but more than a pointer
to a pointer is rarely needed. In fact, excessive indirection is difficult to follow and prone to
conceptual errors.
A pointer to a pointer is declared using **. To access the target value indirectly pointed to by
a pointer to a pointer, you must apply the asterisk operator twice, as in Example 11.8.
Example 11.8. This program illustrates pointer to pointer. The printf() prints 100 100 on
the screen.
main()
{
int x,*p1,**p2;
x=100;
p1=&x;
p2=&p1;
printf("%d %d",*p1,**p2)
}

6
Pointer Variable
address value
Single indirection

Pointer Pointer Variable


address address value

Multiple indirection
Figure 11.3: Single and multiple indirection

11.6 Pointer and arrays


When you declare an array, the system allocates sufficient amount of memory to store all the
elements. The address of the zeroth element in an array is known as the base address of the
array. You can manipulate the array elements using its base address. This is the topic of
discussion for this section.

11.6.1 Pointers and one dimensional arrays


For an 1D array, the array name itself represents the base address of the array, i.e., an array
name is really a pointer to the zeroth element in the array. Suppose you have an 1D array
x. Then x represents &x[0]. Thus if p is a pointer, then the following two statements are
equivalent:
p = x;
p = &x[0];
Now suppose if x = {1.2, 5.1, 8.3, 12.9} and is stored in memory as shown in Figure 11.2.
Now the expression x + 1 evaluates to 2004, which is the address of x[1]. In other words x
+ 1 is equivalent to &x[1]. In general x + i is the address of x[i]. Now since (x + 1) is
the address of x[1], to print the value of x[1] you can write *(x + 1) or in general *(x + i)
gives the value x[i]. The facts so far conceived are summarized in Table 11.3.

Table 11.3: Manipulating array elements


using pointer

Expression Equivalent to Gives what


(x + i)† &x[i] address of x[i]
*(x + i) x[i] value of x[i]
† - x is an 1D array.

Example 11.9. This example exploits the concept that array name denotes the base address,
to input and print array elements.
#include<stdio.h>
main()

7
{
int a[10],n,i;
printf("How many elements you want?\n");
scanf("%d",&n);
printf("Enter the elements\n");
for(i=0;i<n;i++)
scanf("%d",a+i);
printf("The array elements are\n");
for(i=0;i<n;i++)
printf("%d ",*(a+i));
}

11.6.2 Pointers and strings


Recall that strings are 1D char arrays. Assume you have a statement
char str[11]="someString";
in your program. This can be written using pointers as
char *str="someString";
To print the string, you just write
printf("%s",str);
Although str is a pointer, you need not write *str to print the string as str is also the name
of the string. [*str will actually print the zeroth character of the string.]

Example 11.10. This program finds the length of a string using pointers.
#include<stdio.h>
main()
{
char *cptr,str[10];
int length=0;
printf("Enter the string\n");
scanf("%s",str);
cptr=str; //pointer to first character in the string
while(*cptr!='\0')
{
length++;
cptr++; /*incrementing the pointer so that it points to
next character in the string*/
}
printf("The length of the string %s is %d\n",str,length);
}

11.6.3 Pointers and 2D arrays


Recall that a 2D array x can be viewed as an array of 1D arrays wherein each row of x is an
1D array. In this regard, x[0] denotes its zeroth 1D array. To be concise, x[0] denotes the

8
base address of its zeroth 1D array. In general, x[i] is the base address of ith 1D array of a
2D array x. Equivalently *(x+i) gives the base address of ith 1D array in a 2D array x.
If x[i] is the base address of ith 1D array, then x[i]+j should give you the address of jth
element of ith 1D array. In other words, x[i]+j is the address of the element x[i][j]. Thus
&x[i][j] and x[i]+j are equivalent. Moving on, *(x[i]+j) should then give the value of the
element x[i][j]. Now x[i] and *(x+i) are equivalent. So *(*(x+i)+j) should also give the
value of x[i][j]. Thus the following expressions are equivalent and will print the value of
x[i][j].

⇒ x[i][j]

⇒ (*(x+i))[j]

⇒ *(x[i]+j)

⇒ *(*(x+i)+j)

Example 11.11. This program inputs and prints a 2D array using pointers.
#include<stdio.h>
main()
{
int a[5][5],m,n,i,j;
printf("How many rows?\n");
scanf("%d",&m);
printf("How many columns?\n");
scanf("%d",&n);
printf("Enter the elements\n");
for(i=0;i<m;i++)
for(j=0;j<n;j++)
scanf("%d",a[i]+j);
printf("The array elements are\n");
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
printf("%d ",*(a[i]+j));
printf("\n");
}
}

11.7 Array of pointers


Just as you have array of integers or of any other type, you can have an array of pointers
too. Since pointer contains an address, array of pointers would be collection of addresses.
For example, if you want to store the addresses of four int variables; instead of declaring four
pointers, you declare an array of four pointers. This could be accomplished as
int *parr[4];
To assign the address of an integer variable called var to the second element of the pointer
array parr, write

9
parr[2] = &var;
To find the value of var, you write
*parr[2]

Example 11.12. This illustrates the concept of array of pointers.


#include<stdio.h>
main()
{
int i=31,j=5,k=7,l=19,m;
int *parr[4];
parr[0]=&i;
parr[1]=&j;
parr[2]=&k;
parr[3]=&l;
for(m=0;m<4;m++)
printf("%d ",*(parr[m]));
}

An array of pointers can be used to point to an array of data items with each element of the
pointer array pointing to an element of the data array. This is depicted in Figure 11.4. darr
is the int array {55, 457, 25, -874, 0}. parr is a pointer array with the elements pointing
to the elements of darr. Thus each element in parr stores the address of an element in darr.
The elements in darr can be accessed either directly through their subscripts, or indirectly by
dereferencing the elements of the pointer array parr. See Example 11.13.

Example 11.13. This example illustrates how to access the elements of a data array using an
array of pointers.
#include<stdio.h>
main()
{
int darr[10],i,n;
int *parr[10];
printf("How many elements?\n");
scanf("%d",&n);
printf("Enter the elements\n");
for(i=0;i<n;i++)
{
scanf("%d",&darr[i]); //Inputting the array element
parr[i]=darr+i; //Storing the address of array element in pointer array
}
printf("The array elements are \n");
for(i=0;i<n;i++)
printf("%d ",*(parr[i]));
}

Pointer arrays are often used to handle table of strings much effectively. Assume you need

10
darr 55 457 25 -874 0
6000 6004 6008 600C 6010

parr 6000 6004 6008 600C 6010

Figure 11.4: Array of pointers

to store the strings “India”, “Singapore”, “USA”, “UK”, “Japan” and “China”. One way is to
declare a 2D char array
char countries[6][10];
The number of rows is 6 to store the names of 6 countries. The size of each row is set to 10 so
as to accommodate the largest string in the list. But this results in huge wastage of memory
space as many strings in the list are very short. To handle situations like this, you can use array
of pointers. Instead of declaring a 2D char array, you declare an array of char pointers. Each
pointer in the array points to a string in the list:
char *countries[6]={"India",
"Singapore",
"USA",
"UK",
"Japan",
"China"
};
Thus countries[0] will point to “India”, countries[1] to “Singapore” and so on. In general
counties[i] points to ith string in the list. This gives you a more precise way to handle the
table of strings. Only sufficient memory enough to store the strings will be allocated resulting
in better utilization of memory.

11.8 Structure and pointers


The address of a structure variable can be assigned to a pointer in the same manner as address
of variable of any other type is assigned to pointers. Thus you can declare a structure pointer –
pointer to a structure variable. Like other pointers, structure pointers are declared by placing
* in front of a structure variable’s name.

Example 11.14. Suppose you have the following structure template:


typedef struct
{
char name[25];
int accNum;
float balance;
}account;
account acc,*sptr;
Here acc is a structure variable of type account and sptr is a pointer that can point to a
variable of type account. To assign the address of acc to sptr, you write

11
sptr=&acc;

To access the members of a structure using a pointer to that structure, you must use the
-> operator which is called arrow operator. Thus to access accNum, you write
sptr->accNum
The same can be written also as
(*sptr).accNum
although the first notation is more common. This is illustrated in the example below:
Example 11.15. This program illustrates how to access the structure members through a
structure pointer.
#include<stdio.h>
typedef struct
{
char name[25];
int accNum;
float balance;
}account;
main()
{
account acc, *sptr;
sptr=&acc;
printf("Name please\n");
scanf("%s",sptr->name);
printf("Account number please\n");
scanf("%d",&sptr->accNum);
printf("Balance please\n");
scanf("%f",&sptr->balance);
printf("ACCOUNT DETAILS\n");
printf("Name:%s\n",sptr->name);
printf("Account number:%d\n",sptr->accNum);
printf("Balance:%f\n",sptr->balance);
}

This idea can be extended to an array of structures as in the following example.


Example 11.16. In this example, you input the account details of two customers using an
array of structures. These details are then printed using a structure pointer.
#include<stdio.h>
typedef struct
{
char name[25];
int accNum;
float balance;
}account;
main()
{

12
account acc[2], *sptr;int i;
for(i=0;i<2;i++)
{
printf("CUSTOMER %d\n",i+1);
printf("Name please\n");
scanf("%s",acc[i].name);
printf("Account number please\n");
scanf("%d",&acc[i].accNum);
printf("Balance please\n");
scanf("%f",&acc[i].balance);
}
for(i=0;i<2;i++)
{
sptr=&acc[i];
printf("ACCOUNT DETAILS OF CUSTOMER%d\n",i+1);
printf("Name:%s\n",sptr->name);
printf("Account number:%d\n",sptr->accNum);
printf("Balance:%f\n",sptr->balance);
}
}

11.9 void pointer and null pointer


A void pointer is a pointer that has no associated data type with it. Thus it is a generic pointer
that can hold address of any type of variable. A void pointer is declared like a normal pointer,
using the void keyword as the pointer’s type.
void *vptr;
Recall that pointers of one data type cannot hold the address of a variable of some other type,
for example, it is incorrect in C to assign the address of an integer variable to a pointer of type
float. Now, suppose you need an integer pointer, a character pointer and a float pointer; then
you need to declare 3 pointer variables. But instead of declaring different types of pointers, it
is possible to declare a single void pointer which can act as integer pointer, character pointer
and float pointer. This is illustrated below:

Example 11.17. Consider the program below:


#include<stdio.h>
main()
{
int i=5;
float f=7.37;
char c='D';
void *vptr;
vptr=&i;
printf("%d ",*((int*)vptr));
vptr=&f;
printf("%f ",*((float*)vptr));
vptr=&c;

13
printf("%c ",*((char*)vptr));
}
The program declares three variables of int, float and char types and a void pointer. When
you assign address of integer to the void pointer, pointer will become integer pointer. When
you assign address of character to the void pointer, pointer will become character pointer. The
point to note is that when you try to print the variable’s value using void pointer, you need
typecasting. void pointer cannot be dereferenced without typecasting as it has no data type on
its own. If the void pointer contains the address of a float variable, then you need to typecast
the pointer to (float*) before printing the variable’s value. Thus the expression *((float*)vptr)
should correctly print the value of the target float variable. ■

Note that no arithmetic operations can be performed on void pointer.


A pointer that does not currently point to a valid memory location is given the value null (which
is zero). Such a pointer that does not hold the address of any element is called null pointer.
A null pointer does not point to anywhere. One way to give a pointer a null value is to assign
zero to it. For example, the following initializes p to null.
char *p=0;
Equivalently you could also write
char *p=NULL;
where NULL which is called the null pointer constant.
When a null pointer is compared with pointer of any other type, result is always 0 (FALSE).
Two null pointers when compared results in 1 (TRUE). Since null pointer does not hold any
valid address, trying to dereferencing it will result in run time error. The following code will
compile successfully but will crash on execution.
int *p=0;
*p=10;

R null pointer is a value whereas void pointer is a type.

11.10 Constant pointer, pointer to a constant and con-


stant pointer to a constant
A constant pointer is a pointer that cannot change the address it is holding. In other words,
once a constant pointer points to a variable, then it cannot point to any other variable. Trying
to do so will result in error.
Example 11.18. In the program below, ptr is a constant pointer. It first points to var1.
Thus, later when you try to point it to var2, you will get an error.
#include<stdio.h>
main()
{
int var1 = 15, var2 = 20;
int * const ptr = &var1; // This is how a constant pointer is declared

14
printf("%d ",*ptr);
ptr = &var2; //Illegal as ptr is a constant pointer
printf("%d ",*ptr);
}

A pointer through which one cannot change the value of variable whose address it contains
is known as a pointer to constant. As expected, trying to change the variable value through
pointer will result in an error.

Example 11.19. In the program, ptr is a pointer to a constant. It points to var. Later when
you try to change the value, you will get an error.
#include<stdio.h>
main()
{
int var = 20;
const int * ptr = &var; /* This is how you declare a pointer to a
constant */
*ptr = 10; // Illegal. You cannot change the value of var through ptr
printf("%d", *ptr);
}

You have seen constant pointer and pointer to a constant. The next question would be
“Can I have a constant pointer to a constant?”. The answer is YES. A constant pointer to
constant is a pointer that can neither change the address it contains nor it can change the value
kept at that address. Trying to do either of these will lead you to errors.

Example 11.20. In the program, ptr is a constant pointer to a constant. It first points to
var1. Trying to change the value of var1 through ptr is illegal. Similarly, when you try to
point it to var2, you will get an error as it is already pointing to var1.
#include<stdio.h>
main()
{
int var1 = 10,var2 = 20;
const int * const ptr = &var1; // ptr is a constant pointer to a constant
*ptr = 15; // Illegal
ptr = &var2; // Illegal
printf("%d\n", *ptr);
}

11.11 Programming examples


Program 11.1. Define a structure to store the details (title, edition and price) of a book.
Input the details of 5 books using array of structures. Now declare an array of pointers to store
the addresses of these structure variables. Print the book details through the array of pointers.
#include<stdio.h>
typedef struct
{

15
char title[25];
int edition;
float price;
}book;
main()
{
book b[5], *sptr[5];int i;
for(i=0;i<5;i++)
{
printf("BOOK %d\n",i+1);
printf("Title please\n");
scanf("%s",b[i].title);
printf("Edition please\n");
scanf("%d",&b[i].edition);
printf("Price please\n");
scanf("%f",&b[i].price);
}
for(i=0;i<5;i++)
sptr[i]=&b[i];
for(i=0;i<5;i++)
{
printf("DETAILS OF BOOK%d\n",i+1);
printf("Title:%s\n",sptr[i]->title);
printf("Edition:%d\n",sptr[i]->edition);
printf("Price:%f\n",sptr[i]->price);
}
}

16

You might also like