Understanding C++ Pointers

Pointers can be singled out as the most powerful feature of C/C++ programming. They are also regarded as the dreaded one, though unnecessarily. Modern programming languages have sidelined this idea, considering it to be more bane than boon. The apprehension seems logical from the perspective of rush-hour productivity. The hazards of using pointers is that one must be correct to the teeth and take full responsibility of their misuse. Removing pointers allayed one of the stakes in programming no doubt, but sacrificed the means of efficient tuning we can have. This is the reason there is no alternative to C/C++ when it comes to writing low-level programs. However, it is quite possible not to use pointers (visibly) at all in C++ and achieve the same result, but then, where is the fun? The idea of using pointers can really boost up the knowledge of the intricacies at work in general. This article is an attempt to provide a pointer (pun intended) to this concept, and carry it further to a more lower (sic) level.

Variables

In the context of C/C++ programming, a variable is a name given to a memory location that we can manipulate through programs. It is attributed a particular type, such as int, char, float, double, short, long, Employee, Item, and so forth. The type defined provides some information: firstly the size of the memory used and secondly, the range of values that can be stored. For example, when we declare a simple character variable, we get different variations of its supported range of values without necessarily any change in the size of the memory used.

char ch;            // size=1 byte/8 bit, range=-128 to127,
                    // equivalent to signed char ch;
unsigned char uch;  // size=1 byte/8 bit, range=0 to 255

Similar reasoning can be inferred with respect to other data types in C/C++.

C++ Pointers and References

Pointers are variables that store the addresses of values rather than the values themselves. This is one of the compound types used in C++. Another is called References. References are basically an alias or alternative name used for the same memory location. Pointers, on the other hand, are used to access a variable indirectly. The main difference with a reference object is that a pointer is a separate entity and can be treated separately, like assignment, copying, and manipulating it to point to different locations during its life time. References, on the other hand, are quite rigid in this matter. We cannot even declare an unassigned reference.

int val=10;           // integer variable with a initialized value
int &refval1;         // uninitialized, Not OK
int &refval2=val;     // initialized, OK
int *pval1;           // uninitialized, OK
int *pval2=0;         // initialized, OK, equivalent to nullptr
int *pval3=nullptr;   // initialized, OK
int *pval4=&val;      // initialized OK, pointing to the
                      // location designated by val
Note: The identifier nullptr is a pointer literal in C++. Literals are nothing but a self-evident value. A literal has a type, such as true and false; literals for a bool type, an integer, say 12, is a literal for the int type, and so on.

Dealing with C++ Pointer Variables

It is clear that a pointer is used to refer to an address in memory because it holds the address of a memory location. Another thing is that the pointer variable also must be of the same type as the variable it points to. That means, an integer pointer can hold the address of an integer variable or a float pointer holds the address of a float variable.

For the following observation, remember that, * (star/asterisk) means dereference and & (ampersand) means an address. Therefore, when we declare:

int ival=12;   // integer variable initialized with value 12
int *pi=&i;    // integer pointer holding the address of ival,
               // & means the address

In memory it is represented as follows. The memory address is represented in hexadecimal numbers. The actual address may vary every time the code is run.

Addresses can change
Figure 1: Addresses can change

Now, observe the code below and tally with the comments given in the code:

#include<iostream>
using namespace std;

int main()
{
   int ival=12;

   int *pi=&ival;

   cout<<ival<<endl;       // prints the value of ival, 12

   cout<<*pi<<endl;        // prints the value of ival, 12
                           // through pointer dereference

   cout<<&ival<<endl;      // address of ival

   cout<<pi<<endl;         // address of ival or the contents
                           // of pointer pi

   cout<<&pi<<endl;        // address of pi

   cout<<*(&ival)<<endl;   // address of ival dereferenced,
                           // i.e. the value of ival, 12
   return 0;
}

It also is possible to create a pointer that points to another pointer. This is called double pointer. In a similar manner, there may be a triple pointer, and so on. Add the following code snippet to the preceding program and tally the output according to the comments given.

A double pointer
Figure 2: A double pointer

int main(){
   // ...

   int **ppi=&pi;

   // ...
   cout<<**ppi<<endl;   // contents of ival
   cout<<&*ppi<<endl;   // address pi
   cout<<*ppi<<endl;    // contents of ppi=address of pi
   cout<<&ppi<<endl;    // address of ppi
   // ...
}

There are many other concepts involved with pointers; hopefully, we’ll deal with them in another article. But, this is the basic idea of dealing with a pointer variable.

Now, let’s jump to another interesting feature with pointers, called pointer to function.

Pointer to Function

The versatility of the concept of the pointer can take another step ahead, called the pointer to function, or function pointer, that points to a function rather than an object. The practical use of it is determined by the flexibility it offers in programming. Similar to other pointers, it also has a type determined by the return type and the types of the parameters declared by the function signature. For example, a function declaration as

int len(const string &);

has a return type of int and parameter as const string&. Now, if we want a pointer that points to this function, we may declare it as follows:

int (*pf) (const string &);   // pointer to function
                              // that returns an integer value
int* func(const string &);    // a function that returns
                              // an integer pointer

The parenthesis (*pf) is necessary; otherwise, it would convey a different connotation, as illustrated above. Now, to initialize it, we may write it as follows. Both are correct ways to initialize.

pf=len;
pf=&len ;

We, thus, can use any of the following ways to invoke the function:

int i1=pf("hello");
int i2=(*pf)("hello");
int i3=len("hello");

Here is a complete code to illustrate the idea.

#include<iostream>
using namespace std;

int len(const string &str){
   return str.length();
}

int main()
{
   int (*pf)(const string &);
   pf=&len;
   cout<<(*pf)("hello")<<endl;
   cout<<len("hello")<<endl;
   cout<<pf("hello")<<endl;
   return 0;
}

Pointer to Overloaded Function

Overloaded functions can be a little confusing at first glance, but they are very intelligently handled by the compiler.

#include<iostream>
using namespace std;

int add(int a, int b){
   return a+b;
}

float add(float a, float b){
   return a+b;
}

int main()
{
   int (*pf1)(int,int);
   float (*pf2)(float,float);
   pf1=add;
   pf2=add;
   cout<<pf1(10,20)<<endl;
   cout<<pf2(3.4,1.2)<<endl;

   return 0;
}

The compiler derives from the context the type of pointer that is declared and the exact function it must point to.

Function Pointer as Parameter

Interestingly, a pointer to a function can be used as a parameter to another function. For example, we can add to the preceding code a function that takes a pointer to a function as a parameter.

float product(float f1,float(*pf)(float,float)){
   return f1 * pf(2.5,2.5);
}

// ...

cout<<product(2.6,add)<<endl;

Conclusion

Strictly speaking, the concept of pointers emerged from the days of assembly language; the language C acclimatized it for general-purpose programming and C++ nurtures it even today. The power of pointers in C/C++ make using them versatile for any type of development, from high-level application to low-level drivers. The intricacies involved with pointers is vast. This article just provides the basis or a glimpse of it. A lot more writeup like this is required to draw an overall picture. However, the terse idea given here is perhaps good to begin with.

Manoj Debnath
Manoj Debnath
A teacher(Professor), actively involved in publishing, research and programming for almost two decades. Authored several articles for reputed sites like CodeGuru, Developer, DevX, Database Journal etc. Some of his research interest lies in the area of programming languages, database, compiler, web/enterprise development etc.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read