Coder Profile - Show off your skills, get a coder profile.  
 
 
 
source codes Categories
Browse All
Databases (1)
Design & Creativity (1)
Internet & Web Sites (1)
Operating Systems (2)
Programming (29)
Security (7)
Web Development (4)
search Search Inside
Business & E-Commerce
10.00
out of 10

[C++] Pointers and their practical uses in programming.


Intro

One of the most handiest features of C++ (and C) are pointers. Many beginner programmers learn about pointers and don't see the practical uses for them. This tutorial will go over pointers and point out multiple practical uses of them in C++. Some experience in C or C++ programming would be helpful, but not mandatory.

What's a pointer?

A pointer is a variable, like any other, only that a pointer variable holds a memory address. That is an integer will hold a integer, while an integer pointer will hold a memory address. At the memory address, the computer will find a pointer. Let's look at an example


#include <iostream>

using namespace std;

int main()
{
int *pnt = 0;
int x = 2;
pnt = &x;

cout<<pnt<<": "<<*pnt;

cin.get();
return 0;
}


The code might seem a little confusing at first so I'll clarify.

The first line creates a variable that will be a pointer to an integer. You can tell that it is a pointer, because of the * (asterisk) after the type. When you declare a pointer, you must tell the compiler what it will point to. In this case, our pointer pnt will point to an integer. You can have pointers to any data type, including void and aggregate data types (like a class or struct). Note that in the declaration of the pointer, I give it a starting value of 0. When a pointer holds the value of 0, it is said to be a null pointer. You should get into the habit of initializing your pointers to 0 to avoid messy problems (which I'll discuss later in this article).

The second line simply creates an integer variable called x.

The third line is very important. Recall that a pointer is any variable that holds a memory address. The & operator, when used in this context, returns the address of the variable after it. That is &x; will return the memory address when x is, not the value of x. So in this line we assign the memory address of our variable to our pointer. Now we can say that pnt points to x. All pointers must point to something before you can use them. This is critical. If you your pointer isn't pointing to anything, then trying to work with it will cause an error. Always make sure to assign a variable for a pointer to point to before you use it.

The next line shows you how to use a pointer. When the pointer is accessed on it's own, it will return a memory address. In this case, the memory address will the the memory address of x (&x;). When a pointer is accessed after the * (asterisk) operator it is said to be "dereferenced". This means that instead of returning the memory address of x, the pointer will return the value of whatever it is pointing to. In this case, *pnt will return the value stored in x (2, in this case).

Note:
After this line:
pnt = &x;
The following is true:
pnt returns the same as &x;
x returns the same as *pnt

Furthermore, we can change the value of a variable without actually using that variable, but instead, changing the pointer. For instance:

float *pnt = 0;
float var = 2.3;
pnt = &var; //pnt now points to var.
*pnt = 3.14;

Once this code executes, var is 3.14, not 2.3. This is because we dereferenced the pointer and changed the value of what it pointer to.

Pass by Reference

I'll now introduce the first practical use of pointers. Also, I think that this is one of largest uses of pointers. When you pass a parameter to a function, the compiler makes a copy of your variables for the function to play with. This is called pass by value. When you make a change with one of the parameters, it will not affect the outside variable. For example,

#include <iostream>

using namespace std;

void f(int x)
{
x = 4;
}

int main()
{
int x = 0;
f(x);
cout<< x <<endl;
cin.get();
return 0;
}


The following code outputs 0, not 4. This is because the line x=4 inside the function operates on a copy of our original variable, not the actual variable. But what happens when we pass a pointer to an integer as an argument instead? Recall the a pointer is simply a variable that holds a memory address:


#include <iostream>

using namespace std;

void f(int *x)
{
*x = 4;
}

int main()
{
int x = 0;
f(&x;);
cout<< x <<endl;
cin.get();
return 0;
}


The output of this code is 4, not 0. Let's analyze it. First, the function is declared as to take a pointer to an integer now. Not an integer. This means that when we work on x inside the function, we need to dereference it first, hence the asterisk before x in x = 4; One more change should be noted, since the program expects a pointer to an integer (or a memory address of an integer), I pass the memory address of x to the function, not the value of x. This is now a pass-by-reference, not a pass-by-value parameter.

Now when the code tries to make the change, it is operating on a unique memory address, not a copy. In this way, any changes made inside the function effects that variable outside the function. Further, no copy is made. This is good for two main reasons. First, the smaller reason, sometimes we want a function to not return a value, but instead, modify it's arguments. The main reason, is efficiency. Take this next example:


#include <iostream>

struct Xstruct
{
double a;
double b;
double c;
double d;
}

using namespace std;

void f(Xstruct x)
{
x.a = 4.0;
}

void g(Xstruct *x)
{
x->a = 4.0;
}

int main()
{
Xstruct x;
f(x);
g(&x;);
cin.get();
return 0;
}


Our structure is fairly large in size. It holds 4 doubles. When we call f(), the compiler will create a copy of this large structure. When we call g(), no copy is made, so space is conserved. In g(), our parameter is declared as a pointer to a Xstruct. A call to g() is more efficient than a call to f(). This is why almost every time you see a user-defined parameter, you see it passed as a pointer.

One important thing to note is the way g() dereferences it's parameter. Note, that instead of a period, an arrow is used (->). The arrow performs dereferencing on an aggregate data type (class, struct, union, ext.) The function g() could have been rewritten like this:

void g(Xstruct *x)
{
(*x).a = 4.0;
}


No professional programs use this notation though. Almost all programmers use -> to perform dereferencing on pointers to aggregate data types.

I should add, that C++ offers a slightly different way of handling pass by reference. C++ supports references, which ultimately just offers a little syntactic bonus when it comes to passing values by reference. Although you can use references at any time in your C++ program, I don't really see a benefit in using a reference rather than a pointer when it comes to any other situation. Let me show you an example using references and you'll see why it's a little easier to use for pass-by-reference functions.


#include <iostream>

using namespace std;

void f(int &x;)
{
x = 4;
}

int main()
{
int x = 0;
f(x);
cout<< x <<endl;
cin.get();
return 0;
}


As you can see, I declare a variable to be a reference (to an integer) in a similar way as pointers. Instead of the asterisk, I use an ampersand (&). Now I don't have to dereference the reference to change the value. I can simple operate on it. Similarly, when I call the function, I need not pass the memory address, but rather the variable's value. The reference automatically handles low-level things like dereferencing and assigning pointers.

Although references are easier to use, the ultimately do not have as much power. For example, you can not change what a reference "points to", while you can change what a pointer points to. There are a couple other limitations, but you don't need to worry about them if you generally use references for function parameters only.

So now you've seen one of the biggest benefits of using pointers. Let's continue.

Polymorphism

Another very common use for pointers in C++ is for polymorphism. Let's see an example:


class Person
{
std::string firstName;

public:
virtual std::string Talk() { return "Hello, dear sir."; }
}

class Baby : public Person
{
public:
virtual std::string Talk() { return "Goo goo gaa gaa"; }
}

int main()
{
Person father;
Baby son;
Person *basepnt = &father;

std::cout<< basepnt->Talk() <<std::endl; //Outputs "Hello, dear sir."

basepnt = &son;

std::cout<< basepnt->Talk() <<std::endl; //Outputs "Goo goo gaa gaa"

}


Recall that in C++, Object polymorphism works with points. In the above example, we have a basepnt, which is a pointer to a Person, which is defined above. The baby class is derived from Person. Anyway, depending on the type of whatever basepnt points to, C++ will decide which version of Talk() needs to be called. This is another use for pointers.

Array Indexing

A lot of beginner programmers don not realize that arrays are really pointers. When you create an array of 10 elements, your really creating space for 10 elements of a certain type in the same region of memory. That is, space for all 10 elements is in a row. This allows one to use pointer arithmetic to transverse that array. Let me explain with an example:

int *pnt;
int start(0);
pnt = &start;
pnt++;
pnt--;


The beginning of this example is pretty simple, then you see pnt++. This line moves the pointer to the next memory space that it can point to. The ++ effects the memory address, not the value of start. if you recall, this is because we do not reference the pointer. That is, if we did *pnt++, then we would be adding one to start. One important thing here is that ++ on a pointer does not necessarily add 1 to the memory address. It adds the size of it's type, in this case, int. So pnt++ in the above example moves the pointer either 2 or 4 bytes, depending on your computer. Conversly, pnt-- moves the pointer backwards 2 or 4 bytes.

When you index an array using array subscripting (ie. array[2] ), you are really using pointer arithmetic and dereferencing. The subscripting is simply syntactical sugar for the base pointer work.

Dynamic Memory Allocation
This is probably one of the biggest and best uses for pointers in C++. With the use of pointers, you can dynamically allocate memory as it is needed in your program. This is an infinitely handy feature if you want to create efficient code. Let's look at a quick example. I won't get into dynamic memory allocation to much, as it is above the scope of this article.

struct Node
{
int x;
Node *nxt;
Node *prv;
}
int main()
{

Node *pnt;
pnt->nxt = new Node();
pnt->prv = 0;
pnt->x = 23;

pnt->next->prev = pnt;
pnt = pnt->nxt;
pnt->x = 42;

//Delete the list
pnt = pnt->prev;
delete pnt->next;
delete pnt;

return 0;
}


This is basically the beginning of a doubly linked list. As you can see, the Node structure allows us to allocate memory using new whenever we should need it. This is much better than using an array, as it is more efficient and sophisticated. If you want to learn how to implement a full linked list, I suggest you search online. It's a good way to learn pointer manipulation, although you really have to know what you're doing. Anyway, as you can see, dynamic memory allocation relies on pointers.

Conclusion

That's about all I'm going to get into. If I forgot something important, feel free to tell me and I might add it in. Otherwise, I'd love to hear what you guys think about it. Feel free to comment on my page or send me a pm.

What would you rate this article?
Please login to rate and upload coding articles. To start registering a free account with us, click here..
More Articles By This Author
Recently Posted "Programming" Articles
Recently Rated "Programming" Articles
 
  Part of the MyPingle Network
Copyright � 2007, Scott Thompson, All Rights Reserved
Development Blog :: Contact Me :: Terms & Conditions :: Privacy Policy :: Documents