Learning Objectives
After completing this chapter, you will be able to:
Explain the significance of pointers
Declare pointer
Initialize pointers
Use pointers with arrays
Implement dynamic memory allocation
List the advantages and disadvantages of pointers
Introduction
Pointer is a variable that contain the memory address of another variable. Pointers are one of the powerful and frequently used features of C, as they have a number of useful applications.
Variables contain the values and pointer variables contain the address of variables that has the value. Variable directly references the value and Pointer variable indirectly references the value. Referencing a value through a pointer is called Indirection.
Whenever a variable is declared, memory is allocated for the variable according to the data type specified.
int a = 5 ; 2 bytes of memory is allocated for variable ‘a’
printf(“ Value = %d”, a); prints the value 5 printf(“ Address of a = %u”, &a); prints the address 1000
Declaration and Initialization
A pointer variable is declared with an asterisk before the variable name. The type-specifiers determine that what kind of variable the pointer variable points to.
5
1000
Declaration General Form:
data-type *pointer-name;
C provides two operators, & and *, for pointer implementation.
& address operator. It is a unary operator that returns the address of its operand.
* Indirection or de-referencing operator. It returns the value of the variable to which its operand points.
* and & are inverse of each other.
Example 8.1 int x, *px; px = &x; x = 5 ; x px variables values 1000 3000 addresses Example 8.2
Now execute the following printf statements and observe the results.
printf(“ x = %d “ , x); prints 5 printf(” address of x = %d “ , &x); prints 1000
printf (“ address pointed by pointer = %u”, px); prints 1000
printf (“address of the pointer = %u”, &px); prints 3000
printf (“content pointed by pointer = %d”, *px); prints 5
Initialization
Pointer variables should be initialized to 0, Null or an address. No other constant can be initialized to a pointer variable. Pointer variable of a particular data type can, hold only the address of the variable of same data type.
Example 8.3
Valid and Invalid pointer assignments
int a , b , *p = &a , *q = NULL; valid.
q = p; valid - both p and q is pointing to the memory location of variable a b = &a; invalid – ordinary variables cannot hold address.
q = a; invalid - cannot assign value to the pointer variable
Pointer Arithmetic
Pointer Addition or subtraction is done in accordance with the associated data type.
int adds 2 for every increment
char adds 1 for every increment
float adds 4 for every increment
long int adds 4 for every increment
All the operations can be done on the value pointed by the pointer.
The following operations can be performed on pointer variables:
A pointer variable can be assigned the address of an ordinary variable or it can be a null pointer.
A pointer variable can be assigned the value of another pointer variable.
An integer quantity can be added to or subtracted from a pointer variable.
One pointer can be subtracted from another pointer variable provided both are pointing to same array.
Two pointer variables can be compared.
The following are the illegal operations on pointers variables:
Two pointer variables can not be added.
Pointer variable can not be multiplied or divided by a constant.
Example 8.4: Pointer arithmetic int * ptr , i=5;
ptr= &i; let ptr = 1000 (location of i) ptr ++; ptr = 1002 (+2 for integers)
++*ptr or (*ptr)++ increments the value of i by 1
Example 8. 5: Pointer operations Legal operations
p1 > p2 p1==p2 p1+2 p1-p2 (if p1, p2 points to same array)
Illegal operations
Pointers and Arrays
Arrays
Array is used to store the similar data items in contiguous memory locations under single name. Array addressing is in the form of relative addressing. Compiler treats the subscript as a relative offset from the beginning of the array. Array subscripting notation is converted to pointer notation during compilation, so writing array subscripting expressions using pointer notation can save compile time.
Pointers
Pointer addressing is in the form of absolute addressing. Exact location of the elements can be accessed directly by assigning the starting location of the array to the pointer variable. The pointer variable is incremented to find the next element.
C treats the name of the array as if it is a pointer to the first element. Thus, if v is an array, *pv is the same as v[0], *(pv+1) is the same as v[1], and so on.
Pointer pointing to an array Initialization
To initialize a pointer variable, conventional array is declared and pointer variable can be made to point to the starting location of the array. Array elements are accessed using pointer variable. General Form:
pointer_variable = &array_name [starting index]; OR
pointer_variable = array_name;
Example 8. 6
int a[5] = {1,2,3, 4,5} , *ptr , i ;
ptr = a ; similar to ptr = &a[0];
Assume that array starts at location 1000
&a[0] = 1000 a[0] = 1 ptr + 0 = 1000 *(ptr+0) = 1 &a[1] = 1002 a[1] = 2 ptr + 1 = 1002 *(ptr+1) = 2 &a[2] = 1004 a[2] = 3 ptr + 2 = 1004 *(ptr+2) = 3 &a[3] = 1006 a[3] = 4 ptr + 3 = 1006 *(ptr+3) = 4 &a[4] = 1008 a[4] = 5 ptr + 4 = 1008 *(ptr+4) = 5
Accessing value Example 8.7
printf (“%d “,*(ptr+i)); displays the a[i] value printf (“%d “,*ptr); displays the a[0] value printf (“%d “,*(a+i)); displays the a[i] value
Accessing address Example 8.8
printf (“%u “, (ptr+i)); displays address of a(i)
Pointers and Multi Dimensional Arrays
As the internal representation of a multi dimensional array is also linear, a pointer variable can point to an array of any dimension. The way in which the pointer variable used, varies according to the dimension.
General Form:
ptr_vble = &array_name [starting index1]…[starting indexn]; OR
ptr_vble = array_name;
Example 8.9
int a[2][2] = {1,2,3,4} , *ptr ; ptr = &a[0][0] ;
Assume that the array starts at location 1000
&a[0][0] = 1000 a[0][0] = 1 ptr+0 = 1000 *(ptr+0) = 1 &a[0][1] = 1002 a[0][1] = 2 ptr+1 = 1002 *(ptr+1) = 2 &a[1][0] = 1004 a[1][0] = 3 ptr+2 = 1004 *(ptr+2) = 3 &a[1][1] = 1006 a[1][1] = 4 ptr+3 = 1006 *(ptr+3) = 4
If the pointer to the array is accessed with 2 subscripts, it results in a problem. For example,
(p+0) + 1 if it is used to represent 0th row and 1st column and
(p+1) + 0 if it is used to represent 1st row and 0th column results in p+1. So, multi dimensional arrays can be represented by pointer in the following two ways:
Pointer to a group of arrays
Array of pointers
Pointer to a group of arrays
A two dimensional array, for example, is a collection of one dimensional array. Therefore, a two- dimensional array is defined as a pointer to a group of one dimensional array and in the same way three dimensional arrays can be represented by a pointer to a group of two dimensional arrays. int a[3][2] can be represented by a pointer as follows:
int (*p)[2] p is a pointer points to a set of one dimensional array, each with 2 elements.
Note: First dimension need not be specified but the second dimension has to be specified. Here, a single pointer is used and it needs to know how many columns are there in a row.
The following representations are used when a pointer is pointing to a 2D array:
ptr+i is a pointer to ith row.
*(ptr+i) refers to the entire row - actually a pointer to the first element in i th row.
(*(ptr + i) +j) is a pointer to jth element in ith row
*(*(ptr+i) + j)) refers to the content available in ith row, jth column
Accessing value Example 8.10
printf (“%d “,*(*(ptr + i) +j); displays the x(i,j) value
printf (“%d “,*(a[ i ] + j); displays the x(i,j) value
printf (“%d “,*(a + i)[ j ]; displays the x(i,j) value
Example 8.11 main() { int i, j; int a[2][3]={1,2,3,4,5,6}; int *pa=&a[0][0]; for (i=0;i<2;i++) { for (j=0;j<3;j++) printf(“\t%d”,*(*(pa+i)+j)); printf(“\n”); } } Output: 1 2 3 4 5 6 Array of Pointers
Multi dimensional array can also be expressed in terms of an array of pointers. int a[2][2] can be represented as int *ptr[2]
Here, we have 2 pointers ptr[0], ptr[1] and each pointer can point to a particular row . Thus, only one indirection is enough to represent a particular element.
Example 8.12
int a[2][2] = {1,2,3,4} , *ptr[2] ;
ptr[0] = a[0]; /* ptr[0] is now pointing to the 0th row ( & a[0][0]) */
ptr[1] = a[1]; /* ptr[1] is now pointing to the 1st row ( & a[1][0]) */
ptr[0] + 0 = 1000 *(ptr[0] + 0) = 1 ptr[0] + 1 = 1002 *(ptr[0] + 1) = 2 ptr[1] + 0 = 1004 *(ptr[1] + 0) = 3 ptr[1] + 1 = 1006 *(ptr[1] + 1) = 4
Example 8.13
(1) *p[3] declares p as an array of 3 pointers
(2) (*p)[3] declares p as a pointer to a set of one dimensional array of 3 elements
Pointers and Strings
Character pointer is a pointer, which can hold the address of a character variable. Suppose, if we have a character array declared as:
char name[30] = {“Data Structures”};
We can declare a character pointer as follows: char *p = NULL;
Once the pointer is declared, the address of the array is assigned to this pointer. When an array is referenced by its name, it refers to the address of the 0th element.
p = name;
The statement assigns the address of the 0th element to p.
Now issue the following printf statements and check the output: printf(“Character output = %c\n”, *p);
printf(“String output = %s”, *p);
When a pointer variable is referred with the indirection operator, it refers the content of the address pointed by the pointer variable. The above printf statements produce the outputs as follows: Character output = D
String output = Data Structures
The reason for the output produced by the second printf statement is because of the %s format specifier, which will print the string till it encounters a ‘\0’ character. Pointer automatically gets incremented to the next location.
Character-type pointer variable can be assigned an entire string as a part of its variable declaration.
char *p = “string” ; valid
int *p = {0,1,2,3} ; invalid
Thus, string can be represented by either as a one-dimensional character array or a character pointer.
An array of character pointers offers a convenient method for storing strings. Each pointer is used to represent a particular string.
Conventional array declaration: char name[10][10]; Array of character pointers : char *name[10];
Ragged Arrays
Consider the following array declaration.
char names[3][10] = { “abcde”, “rstu”, “xyz”};
This array occupies 30 bytes and the row length is fixed. Instead of making each row a fixed number of characters, make it a pointer to a string of varying length.
If the elements of array are string pointers, a set of initial values can be specified as part of the array declaration.
char *name[4] = { “A” , “AB” , “ABC” , “ABCD”} ;
An advantage is that a fixed block of memory need not be reserved in advance. The above statement allocates variable length block of memory and occupies only 14 bytes. It declares 4 pointers each pointing to a string. Thus, substantial saving in memory. Arrays of this type are referred as Ragged arrays (used only in the initialization of string arrays).
In the above example,
*(name + 1) will access the string AB * (name + 2) will access the string ABC
*(*(name + i) +j) refers the jth character in ith string *(*(name+3)+3) refers D in the string “ABCD”
Memory organization – String Pointers
Example 8.14
(1) char *ps = “xyz”;
pointer ‘ps’ is stored in 2 bytes and ‘ps’ contains the address of the string that requires 4 bytes. (2) char s[ ] = “xyz”;
Pointer to a constant
The address of a constant variable can be assigned to a pointer variable. The following example explains the pointer variable to a constant variable:
Example 8.15 const int a=10; int *pa = &a;
/* suspicious pointer conversion. Wise to avoid such assignments */ Variable ‘a’ is a constant variable. The value cannot be modified.
Pointer variable ‘pa’ can take any other address and value of ‘a’ can be changed using pointer even though it is constant variable.
Constant Pointer
The pointer variable can be a constant. A pointer variable can take the address of a non-constant data and constant data.
Constant pointer to non-constant data always points to the same memory locations and the data at that location can be modified through the pointer. Pointers variables that are declared ‘const’ must be initialized when they are declared.
Example 8.16 int a;
int *const pa = &a;
Constant pointer to constant data always points to the same memory location and the data at that memory location cannot be modified.
Example 8.17 int b;
const int * const pb = &b;
Generic Pointer (void Pointer / Pointer to void)
The type void * is used to declare generic pointers. The generic pointer can be made to point any data type. Type casting is not needed during address assignment. But it is needed, when dereferencing the content using void pointer, in order to know the size and value of the data item.
Example 8.18 int a; float b; void *pab; pab=&a; *(int *) pab =100; pab=&b; *(float *) pab = 105.55;
Dynamic Memory Allocation
Conventional arrays are static in nature, because size has to be mentioned in the declaration statement itself and fixed block of memory is reserved during the compilation.
C supports dynamic memory allocation through the following functions: malloc(), calloc () , free()
These functions provides the ability to reserve as much memory as may required during program execution, and then release this memory when it is no longer required.
Thus, arrays can be represented in terms of pointers and an initial memory location can be allocated to pointer variable by means of this memory allocation functions.
int *p;
p = (int *) malloc ( 10 * sizeof(int)) ;
The above program constructs will return memory block of 20 bytes, which can hold 10 integers. The starting address is pointed by the pointer ‘p’.
A one dimensional dynamic array can be declared using pointers as follows: int *p;
p = (int *) calloc (10, sizeof(int));
This will return 10 continuous memory blocks of 2 bytes each and initializes them to 0. This can be used to allocate space for arrays and structures.
free(p) will release the memory pointed by a pointer variable ‘p’. free() will take a void pointer.
Example 8.19: Program for adding two matrices using array of pointers void main()
{
int *a[3] , *b[3] , *c[3]; int i,j;
for(i=0 ; i<3; i++) {
a[i] = (int *)malloc( 3 * sizeof(int));
/* memory is allocated to individual pointers */ b[i] = (int *)malloc( 3 * sizeof(int));
c[i] = (int *)malloc( 3 * sizeof(int)); }
printf(" \n enter the values of matrix 1 \n"); for(i=0; i<3; i++)
for(j=0; j<3; j++)
scanf("%d", a[i]+j);
for(i=0; i<3; i++)
for(j=0; j<3; j++)
scanf("%d", b[i]+j); for(i=0; i<3; i++
for(j=0; j<3; j++)
*(c[i]+j) = *(a[i]+j) + *(b[i]+j); for(i=0; i<3; i++)
for(j=0; j<3; j++)
printf("\t%d", *(c[i]+j)); }
Chain of Pointers
Multi dimensional arrays can be declared using pointer to pointer representation and memory can be allocated dynamically.
int **p; represents 2 dimensional array
In the above declaration p is a pointer variable, which holds the address of another integer pointer. As such, there is no restriction imposed by the compiler as to how many levels we can go about in using a pointer.
The following declaration is perfectly valid: int *****p;
However, beyond 3 levels, it will make the code highly complex and un-maintainable.
Example 8.20
addr.ptr2 addr.ptr1 value
int x,*p1,**p2; x=100;
p1=&x; p2=&p1;
To access the value we can use either **p2 or *p1
Advantages
It gives direct control over memory and thus we can play around with memory by all possible means. For example, we can refer to any part of the hardware like keyboard, video memory, printer, etc directly
As working with pointers is like working with memory, it will provide enhanced performance
Pass by reference is possible only through the usage of pointers. Useful while returning multiple values from a function
Allocation and freeing of memory can be done wherever required and need not be done in advance(Dynamic Memory Allocation)
Limitations
If the allocated memory is not freed properly, it cause memory leakages
If not used properly, it makes the program difficult to understand and may cause the illegal memory references
Summary
Pointer is a variable which can hold the address of another variable.
& operator is used to refer the address of a variable and * operator is used for dereferencing the pointer.
Pointer can point to an array of any dimensions.
There are two ways to represent multi dimensional arrays by means of pointers: o Single pointer points to set of arrays
o Array of pointers
Strings can easily be represented using pointer – Ragged arrays.
malloc(), calloc() functions are used to allocate memory dynamically.
free() function is used to de-allocate the memory.
Test your Understanding
1. State whether the following are true or false
a. Pointer variable can only contain an address
b. Address of the memory location can be assigned to ordinary variables c. Pointer can refer to the content of the memory location by & operator d. Size of the pointer variable is equivalent to the size of the data item it points.
2. What is the use of generic pointers? 3. What is the output of the following code?
main() { int n[25]; n[0]=100; n[24]=200; printf("\n%d,%d", *n, *(n+24)+*(n+0) ); }
4. Given the following declaration: int a, *b = &a , **c = &b;
What is the output of the following statements?
5. What is the output of the following code? main( )
{
char *str1=”abcd”; char str2[]=”abcd”;
printf(“%d %d %d”, sizeof(str1),sizeof(str2), sizeof(“abcd”)); }
6. Differentiate malloc() , calloc(). Answers:
1. True, false, false, false
2. Generic pointers (void pointers) can point to data items of any type. 3. 100, 300
4. The first statement assigns 4 to a. The second statement assigns 5 to the location pointed to by the location pointed to by c. Since c points to b, this is same as assigning 5 to the location pointed to by b. Since b points to a, this statement is equivalent to assigning 5 to a. The third statement castes **c, which is value of a, into type int *, assign the value to a. The result is meaningless, because values cannot be assigned to pointers.
5. 2 5 5
6. malloc(), calloc() will both allocate the memory dynamically, but the difference is calloc() will return a contiguous memory location and initializes it to 0.