Pointers Introduction: A variable is a named memory location that holds some value. Each variable has some address associated with it. Till now we only worked on the values stored in the variables and didn t deal with the addresses of these variables. Sometimes we need to deal with the address of a variable. In earlier programs we use address of a variable in scanf () function. The '& known as address operator in C language can be used to retrieve address of any variable but if we want to store the address of any memory location then we have to use the concept of pointer. A pointer is a variable that holds the memory address of other variables. Instead of storing values, we store address in the pointer. The address can be assigned to the pointer using & operator. While dealing with pointers we must be very careful. As we can store memory addresses in pointers, and addresses can t be negative. Besides addresses can t be fractional and have only integral values. If a memory address of one location is x, then address of next location will be x+1, not x+0.5, because each location is of 1 byte width, so every pointer stores unsigned integer values only. As every pointer has to store only unsigned integer address, each pointer occupies only two bytes of storage in memory irrespective of the type of the pointer. It means that float pointer, double pointer, char pointer or any other pointer occupies only two bytes of memory. Another important point to note is that we can t assign address of one type of variable to pointer of different type, i.e. an integer pointer can store integer variable s address, float pointer can store float variable s address, and soon. Now we know that a pointer can be of any type char, int, float, or double, or user defined types and can store address of variables which are of the same type as the
pointer itself. If we have a float pointer, which holds address of a float variable, then it means that pointer itself occupies two bytes of storage only, but the address it contains is the address of a variable of four bytes. The pointer don t store address of all the four bytes but only stores the base address of a variable, i.e. the address of first byte of variable. Similarly the double pointer that holds address of a double variable will hold only the address of first byte of the double variable. If we increment the pointer by 1, it is always incremented by the width, in bytes, of the object it was point at. For example if a double pointer holds the address x in it and we increment it by 1, the pointer will now store the address x+8 (if double occupies 8 bytes of memory). Similarly if a float pointer holds the address y and we increment it by 1, the new address it holds will be x+4 (if float occupies 4 bytes of memory). If we increment float pointer by 3 i.e., y+3, the new address it will hold will be y+4*3 = y+12. The reason for this is that if an integer pointer holds address p which is an address of some integer variable and we assume that an integer variable occupies two bytes of memory locations. By incrementing the integer pointer by 1, the new address it will hold will be p+2 which will be the address of some next integer variable, instead of pointing to second byte of previous integer variable whose address was stored in it. Declaring pointer variables One of the most powerful features of C, and one that make it quite close to assembly language, is its ability to refer to the addresses of program variables. In C one can in fact declare variables to be pointers, i.e. variables which will hold the memory addresses of other variables. To declare a pointer variable of given data type, the syntax is: type * identifier; for example: int *p;
float *q; double *r; char *x; Consider the statements: int *x,y=25; The above statement declares one pointer to integer named x and one integer variable y. We can assign any integer value to y as 25 in above statement, but we can t assign a value to x, as x is a pointer and can only hold address of some integer variable. To store the address of variable y in the pointer x, we can use below statement: x=&y; The above assignment statement assigns address of y to x, in other words we can say that x points to y. If we use pointer as lvalue, then rvalue must be memory address; In above statement we retrieve address of y using address operator & and store that address in pointer x. Now if want to determine the contents at that address, we can use value at address operator (*). The asterisk (*) has multiple meanings in C language. If used with variables inside the expression as binary operator, it acts as multiplication operator. If used in declaration statement in front of the variable, then it means that the given variable is a pointer variable and is going to store the memory address as in above statement. However if we use * as unary operator in front of any pointer variable, then it means value at address operator. For example: int x,y,z; x=10; y=3;
z=x*y; /* here * acts as multiplication operator*/ int *p; /* here * indicates p is a pointer variable*/ p=&x; /* p holds address of x*/ (*p)++; /* here *p means value at address stored in p. as p stores address of x, *p means value of variable x i.e 10. Therefore (*p)++ changes the value of variable x to 11. The address in p will remain unchanged*/ Now consider the following program segment to clear the pointer concepts. /*Prog6a.c Pointer Example*/ #include<stdio.h> #include<conio.h> void main() { int x,y; int *p,*q; clrscr(); x=20; y=30; printf("\nvalue in Variable x = %d",x); printf("\nvalue in Variable y = %d",y); printf("\naddress of Variable x = %u",&x); printf("\naddress of Variable y = %u",&y); p=&x; /*pointer p points to base address of x*/
} q=&y; /*pointer q points to base address of y*/ printf("\nvalue in p = Address of Variable x = %u",p); printf("\nvalue in q = Address of Variable y = %u",q); printf("\nvalue at Address in p = Value of x =%d",*p); printf("\nvalue at Address in q = Value of y =%d",*q); printf("\naddress in q before incrementing by 1 is %u",q); q++; printf("\nnow Address in q after incrementing by 1 is %u",q); q--; printf("\nnow Address in q after decrementing by 1 is %u",q); (*p)++; /*increment value at address in p by 1 that is contents of x*/ (*q)++; /*increment value at address in q by 1 that is contents of y*/ printf("\nnow Value in Variable x = %d",x); printf("\nnow Value in Variable y = %d",y); printf("\naddress of Variable x = %u",&x); printf("\naddress of Variable y = %u",&y); printf("\nvalue in p = Address of Variable x = %u",p); printf("\nvalue in q = Address of Variable y = %u",q); printf("\nvalue at Address in p = Value of x =%d",*p); printf("\nvalue at Address in q = Value of y =%d",*q); getch(); The Output of above program is:
For the understanding of above program consider the following figure: Memory Addresses 8 7 6 5 4 3 2 1 variable Decimal value Remarks 65529 1 1 1 1 1 1 1 1 65528 1 1 1 1 0 0 1 0 65527 1 1 1 1 1 1 1 1 65526 1 1 1 1 0 1 0 0 65525 0 0 0 0 0 0 0 0 65524 0 0 0 1 0 1 0 0 65523 0 0 0 0 0 0 0 0 65522 0 0 0 1 1 1 1 0 q 65522 p 65524 x 10 y 30 Address of y Address of x Value of x Value of y &X means address of x i.e. 65524 &y means address of y i.e. 65522 p=&x means p=65524 q=&y means q=65522 *p means value at address p =value at address (65524) =20.
If you will check the value stored at memory location 65524 in above figure. You will find 20. *q means value at address q =value at address (65522) =30. If you will check the value stored at memory location 65522 in above figure. You will find 30. (*p)++ means (value at address p) ++=value at address(65524)++=x++=21. (*q)++ means (value at address q) ++=value at address(65522)++=y++=31. The p=&x; means p stores the reference (address) of variable x. Therefore & is also known as reference operator. The statement *p retrieves the contents stored at reference in p. Therefore * is also known as dereference operator. Functions and Pointers: In last chapter, we saw when we call a function and pass some variable as arguments, it is not the actual parameters /variable itself that are passed to the called function, but only the values of the variables are copied into the dummy variables. In memory the actual and dummy variables are different storage locations. If we made any change in the dummy variables within the called function, the changes are not reflected in the calling function. if we want that the change made in variable in the called function should be reflected in calling function, then we have to return the value of changed variable from called function into the variable that we want to change in the calling function. But the problem is that if a called function changes more than one dummy parameters/ variables and we want all changes should be reflected in calling function actual parameters. This can t be achieved by simply returning value from function because a function can return only one value.
One solution to the problem is to use global variables, as the global variables are accessed by any function and changes made in any function are reflected in every function, however the indiscriminate use of global variables is not recommended. For one thing, functions have the power to change them. That makes global variables vulnerable and are thus more prone to errors. The better solution in this case is the use of pointers. Call by value In call by value, when a function is called, we pass one or more variables as parameters to the called function. Actually it is the values that are copied from the variables in calling function into the dummy parameters of called function. For better understanding consider the following program segment: /*Prog6e.c swapping values of variables using call by value*/ #include<stdio.h> #include<conio.h> void swap(int,int); void main() { int num1,num2; clrscr(); printf("\nenter Two Numbers:\n"); scanf("%d%d",&num1,&num2); printf("\nbefore Function Call num1 = %d, num2 = %d",num1,num2); swap(num1,num2); printf("\nafter Function Call num1 = %d, num2 = %d",num1,num2); getch(); } void swap(int n1,int n2)
{ int temp; temp=n1; n1=n2; n2=temp; } The output of program is The memory locations before and after function swapping are shown below: Memory Addresses 8 7 6 5 4 3 2 1 variable Decimal value Remarks 65529 0 0 0 0 0 0 0 0 65528 0 0 0 0 0 0 0 0 65527 0 0 0 0 0 0 0 0 65526 0 0 0 1 0 1 0 0 65525 0 0 0 0 0 0 0 0 65524 0 0 0 0 1 0 1 0 65523 0 0 0 0 0 0 0 0 65522 0 0 0 1 0 1 0 0 Temp 0 0 N2 20 20 N1 10 10 Num2 20 20 65521 0 0 0 0 0 0 0 0 Num1 10 10 65520 0 0 0 0 1 0 1 0 Memory Map before swapping Values
Memory Addresses 8 7 6 5 4 3 2 1 variable Decimal value Remarks 65529 0 0 0 0 0 0 0 0 65528 0 0 0 0 1 0 1 0 65527 0 0 0 0 0 0 0 0 65526 0 0 0 0 1 0 1 0 65525 0 0 0 0 0 0 0 0 65524 0 0 0 1 0 1 0 0 65523 0 0 0 0 0 0 0 0 65522 0 0 0 1 0 1 0 0 Temp 10 10 N2 10 20 N1 20 10 Num2 20 20 65521 0 0 0 0 0 0 0 0 Num1 10 10 65520 0 0 0 0 1 0 1 0 Memory Map After swapping Values As seen from the above two tables, the values of num1 and num2 before and after swapping are same. The values of n1 and n2 are swapped in function swap, but these are local variables and their scope and life ends with the termination of function swap(). Therefore when control returns back to main () function, the values of num1 and num2 are same as they were before swapping. This is an example of call by value. Call By Reference. In call by reference, when a function is called, we pass one or more variable s references/addresses as parameters to the called function. Actually it is not the values of the variables that are copied from calling function into the dummy parameters of called function, but the addresses of actual parameters are passed into the called functions dummy pointer variables. Then any change made in called function is made directly at references, and hence changes are reflected in calling function also. For better understanding consider the following program segment: /*Prog6f.c swaping values of variables using call by reference*/ #include<stdio.h>
#include<conio.h> void swap(int*,int*); void main() { int num1,num2; clrscr(); printf("\nenter Two Numbers:\n"); scanf("%d%d",&num1,&num2); printf("\nbefore Function Call num1 = %d, num2 = %d",num1,num2); swap(&num1,&num2); printf("\nafter Function Call num1 = %d, num2 = %d",num1,num2); getch(); } void swap(int *n1,int *n2) { int temp; temp=*n1; *n1=*n2; *n2=temp; } The output of program is
The memory locations before and after function swapping are shown below: Memory Addresses 8 7 6 5 4 3 2 1 variable Decimal value Remarks 65529 0 0 0 0 0 0 0 0 65528 0 0 0 0 0 0 0 0 65527 1 1 1 1 1 1 1 1 65526 1 1 1 1 0 0 1 0 65525 1 1 1 1 1 1 1 1 65524 1 1 1 1 0 0 0 0 65523 0 0 0 0 0 0 0 0 65522 0 0 0 1 0 1 0 0 Temp 0 0 N2 20 20 N1 10 10 Num2 20 20 65521 0 0 0 0 0 0 0 0 Num1 10 10 65520 0 0 0 0 1 0 1 0 Memory Map before swapping Values Memory Addresses 8 7 6 5 4 3 2 1 variable Decimal value Remarks 65529 0 0 0 0 0 0 0 0 65528 0 0 0 0 1 0 1 0 Temp 10 10 65527 1 1 1 1 1 1 1 1 65526 1 1 1 1 0 0 1 0 65525 1 1 1 1 1 1 1 1 65524 1 1 1 1 0 0 0 0 N2 65522 N1 65520 Address of num2 Address of num1 65523 0 0 0 0 0 0 0 0 65522 0 0 0 1 0 1 0 0 Num2 20 20 65521 0 0 0 0 0 0 0 0 Num1 10 10 65520 0 0 0 0 1 0 1 0 Memory Map After swapping Values In above program, we pass references of num1 and num2 into the pointers n1 and n2 respectively. In called function swap () we made changes at addresses, and as we
know that addresses are unique and any change made at address is reflected in any other function if it has access available to that memory location.