[size="5"]Introduction
Hi, welcome to the crazy land of C++ pointers. This article exists as a reference and tutorial for those who are learning C++ and need a little extra help in the area of pointers. It is not an all-encompassing treatise by any means. It assumes you have basic knowledge of variables (including how they're stored in memory), arrays (including null-terminated char arrays), and classes. File I/O experience would be nice, but is not required.
[size="5"]"What are pointers, and what are they good for?"
One of the large issues with pointers is that it's hard to create large enough examples to justify the use of them while still keeping everything neat and explicable. Combine that with the quirky notation, and it's enough to give any newbie a headache. We won't cover all the in-depth and technical topics, but we'll hopefully get enough information to clear up the confusion. The best cure is time (along with plenty of reading Anyway, here goes.
A pointer is basically a variable whose value is a memory address. Its size is 4 bytes (in 32-bit OS's anyway), in accordance with the amount of space required to represent a location in memory. The power of pointers becomes apparent when you want to have access to multiple objects of one type while your application is running (a problem normally tackled with plain arrays), but you don't know while coding just how many of them you'll need. For example, if I have a CEnemy class that stores the attributes of my enemies, and I want to be able to read a file that tells me how many enemies there are, their hit points, their locations, etc, I can do it one of two ways:
- Allocate a static array (e.g. CEnemy enemyList[255]) that has enough slots for the maximum number of enemies I'll ever have in the game at one time.
- Dynamically allocate an array with the number of enemies I read in from the file (e.g. CEnemy *enemyList[numEnemiesInFile])
Number two is a good solution to the problem. I can read in the number of enemies via an ifstream (InFile >> numEnemiesInFile), and simply "allocate" (make room for in memory) an array of the perfect size.
[size="5"]Pointer Syntax and Memory Allocation
Onto syntax. We declare a pointer like this (where [lessthan]data type> is a data type, and ...yeah):
* = 0;
We make use of the pointer by giving it a memory address to point to. We can either allocate memory using the 'new' keyword, or we can assign it the memory address of an object we already know. Here are two examples:
(1)
CEnemy *enemyList = new CEnemy[5];
(2)
CEnemy *someEnemy = &anEnemy
Example 2 requires a little more explanation. You'll notice that the pointer someEnemy is declared in exactly the same as enemyList was in the previous example. However, it doesn't point to an array like enemyList; instead it points to a single CEnemy object named anEnemy. Here's the difference: in reality, all pointers point to a single object, since they can only store a single memory address. The internal mechanics of a pointer allow you to access the elements of an array to which it points, but the pointer itself is blind to how many objects it's responsible for - that is, the number of elements in the array it's pointing to, or even if it's pointing to an array at all (as opposed to a single object). Sorry to ramble on that topic, but it's a stumbling block for many people to mentally separate the concept of the pointer itself from the memory it points to. Perhaps this is because there's no real-world analogue.
Anyway, here's something else interesting about example 2: the entrance of the "address of" operator - '&'. The ampersand preceding anEnemy's name means "the location at which this object exists." This is very convenient, since it allows us to make a pointer point to a static object.
I keep using the words "static" and "dynamic," and you're probably missing something if you're not firm on their meaning. When I say static, I mean something that doesn't change during "run-time," (while the program is running). Dynamic is the opposite, meaning "on-the-fly" so to speak. All objects take up memory, but static objects have memory given to then automatically when they are declared. Objects in dynamic memory must be allocated their memory at run-time, because before then, the computer doesn't know how many objects there will be. Pointers exist as the interface between the programmer and the dynamic memory they use.
[size="5"]Using The Data Stored in the "Pointed-to" Object
So, what good are pointers if all they do is point to memory? In this section we'll learn the ways of accessing the data that a pointer points to.
You should already be familiar with the dot operator used to access data members of classes and structs. Its usage looks like this:
anObject.aMember = aValue;
Explicit dereferencing looks like this:
(*aPointer).aMember = aValue;
aPointer->aMember = aValue;
Something to take note of: when accessing an individual object in a dynamic array, use the following syntax:
aPointer[elementNum].dataMember;
And there you have it - data access via pointers.
[size="5"]Deleting Dynamically Allocated Objects
You must release all the memory that you dynamically allocate! That is, since you set aside memory for objects in your code, you have to tell the operating system when you're done with it. You do this by way of the 'delete' keyword. Here are a couple of examples:
(3)
delete [] enemyList;
(4)
delete aRandomEnemy;
[size="5"]Pointer Caveats
Ah, to every good thing there is a bad side. Pointers are invaluable when used properly, but can be a complete pain when used even slightly improperly. Let's discuss some ways pointers can be abused. Example 5:
(5)
CEnemy *enemyList = new CEnemy[5];
enemyList = new CEnemy[3];
(6)
CEnemy staticEnemy;
CEnemy *pointerToEnemy = &staticEnemy
delete pointerToEnemy;
(7)
CEnemy *enemyList = new CEnemy[12];
...
delete enemyList;
(8)
CEnemy *runningOutOfPointerNames = new CEnemy;
Okay, that's enough of what you can do wrong. Since this is already way longer than I intended in the first place, let's discuss
[size="5"]Additional Uses For Pointers
There are, of course, many things you can do with pointers that I haven't even mentioned yet. One of these is creating arrays of base types with new. The syntax is the same:
(9)
int *intPointer = new int[100];
//or
char *charPointer = new char[fileSize];
Okay, one final example summarizing what we've learned.
(10)
char *string = new char[80];
strncpy(string, "Hi, I'm a string!", 18);
char *temp = string;
do
{
std::cout << (*temp);
} while(*(++temp));
(*(++temp))
[size="5"]Epilogue (or, "Is it really over?")
Well, that's just about everything I can say about pointers. There are many other references about pointers available on the 'net; some of them are probably much better than this one. The best thing you can do for yourself is simply to read as much as you can, and of course code as much as you can, until it clicks. And it will. You will eventually get everything if you just keep at it. If you have any specific questions, my preferred mode of contact is e-Mail; my address is someone_at_gdnmail.net Thanks for reading, I hope you learned something, and bye-bye for now.
The author would like to thank Scott Hilbert and Greg Rosenblatt for their corrections and proofreading.