If you ask programmers what is the most confusing thing in C programming, I’m sure that they will say ‘pointers’! Indeed pointers are difficult to master at the beginning. In fact, many programmers hate C because of pointers.
Surprisingly to placate them modern programming languages such as Visual Basic, Java etc. removed explicit pointer feature. (Though they handle internally everything using pointers!)
In this chapter I’ll discuss various topics about pointers and shall try to clarify some of your doubts. Although all C/++ programming books deal with pointers not all of them provides sufficient insight. You may consult Ref. 9 for a dedicated treatment of pointers in C.
Did you remember the quadratic equation function in code 3-1? Look, here we write the function in slight different manner.
Code 8-1
/* a quadratic equation solver in C++ */
#include <iostream.h>
#include <math.h>
int solve_quad(int p, int q, int r, float &q1, float &q2);
void main(void) {
int a,b,c;
float root1,root2;
int status;
cout << "Enter a,b,c for ax²+bx+c=0" << endl;
cin >> a >> b >> c;
status = solve_quad(a,b,c,root1,root2);
if(status==1) {
cout << "The solution is" << endl;
cout << root1 << " " << root2 << endl;
} else
cout << "No real root" << endl;
}
int solve_quad(int p, int q, int r, float &q1, float &q2) {
float determ;
determ=q*q-4*p*r;
if(determ<0) /* no real root */
{
q1=0;
q2=0;
return 0;
}
else /* real root exists */
{
q1=((-q)+sqrt(determ))/(2*p);
q2=((-q)-sqrt(determ))/(2*p);
return 1;
} }
Observe that here we passed the root1 and roo2 values by reference (in chapter 3, we did that by pointer). Here we didn’t have to use ‘*’ anywhere in the program except only an ‘&’ in front of the variable that we want to pass by reference but that too only in function prototype. C++ is smart enough to do the rest of work by itself. Note that this code works only in C++ and not in C.
However, the assembly language code generated by the compiler for the
solve_quad function in code 3-1 and 24-1 are same.
So we can summarize the C/++ function calling syntax in following table.
Code element By value (C/++) By pointer (C/++) By reference (C++ only)
Function call Var &Var Var
Function prototype
Var *Var &Var
Function body Var *Var Var
Now I am going to tell another nice feature of pointer – the ‘void pointer’! But before telling you what it is, I like you to think about something else.
When you define something like int a what happens? The compiler reserves 4 bytes of space for variable ‘a’ because size of integer is 4 bytes. What happens when you write int *a? Here the compiler sees that ‘a’ is an address which can content an integer. If you try to assign any other data type say float in address
‘a’, the compiler screams. Don’t you wonder that if ‘a’ is just an address why I can’t store address of any data type? Well, this is for your own safety! We have to define functions like int Func(float a) because the compiler checks that we are not mingling inappropriate data types inside anywhere in our programs.
If we do so accidentally, not only it will give erroneous result but also it would be very difficult to debug the program to find out what went wrong.
Ok, you may argue that for data types, the byte sizes are different. But for address, I can store any address at any pointer variable because bytes required to store all addresses are the same. Yes, it is! We can achieve the feat by using void pointer!
A void pointer variable can hold address of any data type. We define it as void
*a. Now take a look at the next program.
Code 8-2
// demo of void pointer & template
#include<iostream.h>
void swap(void **x, void **y);
template <class T>void swap_t(T& x, T& y);
void main(void)
cout << "a=" << a << endl;
cout << "b=" << b << endl;
char p,q;
p='P'; q='Q';
// swap function used for char swap((void**)&p,(void**)&q);
cout << "p=" << p << endl;
cout << "q=" << q << endl;
int m,n;
m=50;n=100;
swap_t(m,n);
cout << "m=" << m << endl;
cout << "n=" << n << endl;
}
void swap(void **x,void **y)
template <class T> void swap_t(T& x, T& y) {
The first part of the program gives you an example of simple void pointer operation. In the next part we find two interesting swap functions. The first swap function swap uses void pointer. I know you’re going to ask why do we need ‘**’ in swap function? The answer lies in next line void *temp. This declaration tells the compiler that temp is an address which can contain the address of any data type. We can write void temp only because the compiler has no way to understand how much bytes of memory it need to reserve because we didn’t specify any particular data type for temp. So to pass a pointer variable by reference we needed ‘**’. Clear now? Observe another point. In first part of the program, we wrote j=(int*)gp. Why? Had we written j = gp then the compiler would have complaint because it would see that gp contains address of generic data type which we were being trying to assign an address j which contains address of an integer. So by writing j=(int*)gp we are actually telling the compiler ‘hey, I know gp contains address of any data type but now I want it to assign to j which contains address of an integer. I know what I am doing so don’t panic’. So the compiler obeys the master and works fine.
However, in Unix, you can write j = gp without any compilation error. The same logic holds for swap((void**)&p,(void**)&q)as well.
Let’s now venture the second swap function swap_t. What does the word
‘template’ mean? It is too actually for generalization of data types. I can call this template function swap_t to swap integer, float, double, character anything! So what is the difference between template function and void pointer function.
They are almost the same. When the template function gets compiled, the compiler generates code for all possible data types (remember function overloading in chapter 7) an appropriate version is called during runtime depending on the parameter passed.
Of course, programmers find the template functions are easier to write than that of void pointers! Do you find this concept similar to that of Variants in Visual Basic?
Next we’ll learn about linked list. Tell me what does the following structure do?
typedef struct employee {
int empid;
char name[20];
struct employee *next;
} emp;
It means employee is a structure. It holds one integer, one character array of length 20 and an address next, which stores another employee data type. This type of structure is called “Self Referential Structure” because at least one element of this structure contains an address, which stores same structure data type. If you still find the explanation garbled then read the explanation of void pointer again. Using self-referential structure, we can write linked list, binary search tree and many other data structures!
I am showing here you an example of linked list program. Tries running this code in debug mode (using line by line execution) and observe the content of pointer variables.
Code 8-3
/* linked list */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct employee {
int empid;
char name[20];
struct employee *next;
} emp;
/* function prototypes */
emp* create(struct employee *ptr);
void display(struct employee *ptr);
void search(struct employee *ptr);
void erase(struct employee *ptr);
/* main function */
void main(void)
{
int choice;
employee *p=NULL;
printf("Enter your choice\n");
printf("1. Create link list\n");
printf("2. Display link list\n");
printf("3. Search an item in list\n");
printf("4. Delete an item from list\n");
scanf("%d",&choice);
printf("Enter your choice\n");
printf("1. Create link list\n");
printf("2. Display link list\n");
printf("3. Search an item in list\n");
printf("4. Delete an item from list\n");
scanf("%d",&choice);
} while(choice ==1 || choice==2 || choice==3 ||
choice==4);
}
/* create link list */
emp *create(struct employee *ptr) {
emp *first=ptr;
emp *curr;
int EmployeeId;
char EmployeeName[20];
printf("Enter employee number\n");
scanf("%d",&EmployeeId);
printf("Enter name\n");
fflush(stdin);
scanf("%[^\n]s",EmployeeName);
if(ptr==NULL) // no item so far
{
curr=(emp*)malloc(sizeof(emp));
first=curr;
curr->empid=EmployeeId;
strcpy(curr->name,EmployeeName);
curr->next=NULL;
return first;
}
curr=ptr;
while(curr->next!=NULL) // traverse entire list to find out last item
curr=curr->next;
curr->next=(emp*)malloc(sizeof(emp));
curr->next->empid=EmployeeId;
strcpy(curr->next->name,EmployeeName);
curr->next->next=NULL;
return first; // returns beginning address of list
}
/* display entire link list */
void display(struct employee *ptr) {
while(ptr!=NULL) {
printf("%d\t",ptr->empid);
printf("%s\n",ptr->name);
ptr=ptr->next;
} }
/* search link list */
void search(struct employee *ptr) {
int empnum;
emp *ser=ptr; /* assigning beginning address of the list
*/
printf("Enter employee number to search\n");
scanf("%d",&empnum);
do
{
if(ser->empid==empnum) {
printf("%s\n",ser->name);
return;
}
ser=ser->next;
} while(ser!=NULL);
/* this point will be reached if no match found */
if(ser==NULL) {
printf("No match\n");
} }
/* delete an item form list */
void erase(struct employee *ptr) {
printf("Enter employee number to delete\n");
scanf("%d",&empnum);
while(temp!=NULL) // traverse list until last item {
if(temp->empid==empnum) // item found {
if(temp==ptr) // item to be deleted is first item
{
ptr=temp->next;
printf("Deleted\n");
flag=1;
printf("Deleted\n");
flag=1;
return;
}
}
else // item not found {
old=temp;
temp=temp->next; // point to next item }
if(flag=0)
printf("Not found\n");
} }
I know you still have a lot of doubts about pointers (which is not embarrassing in any way). The best way to learn pointers is to write some programs of your own and running them in debug mode to examine contents of memory locations.
You may be wondering whether it is possible to write linked list in VB. Yes, you can. Though VB does not support self-referential structures, it does have provision for self-referential classes!