After my tutorial on classes, I thought it was time to post a tutorial on another very important aspect of both C and C++: pointers. I also strongly recommend you have a firm grasp of what classes are before advancing to pointers, because I will use classes and objects to explain pointers. However, even without that knowledge you will probably be able to understand the basics of pointer usage, because it is definitely not limited only to OOP (C has them as well).
Pointers can be very confusing to people new to intermediate-level languages. They are also a very rich source of bugs and crashes in a program...
I think that the easiest way of understanding and explaining pointers is not to first explain what they technically are, but present the problematics from which the need for them arises (this is the part with which I and some people I know struggled the most when learning about pointers).
Imagine that you are creating a game. You are using a class called Humanoid to represent an individual in your game.
Now, lets say that in your main function, you create two Humanoid objects: a player and an enemy.
What would you want your player and your enemy to do? Battle, of course! You need a battle function, which takes in two humanoids as arguments, and makes them fight to death. That function could also return a winner Humanoid object for convenience.
You could do something like this:
Humanoid battle(Humanoid h1, Humanoid h2) { Humanoid winner; //battle, set the winner return winner; }
However, you wouldn't get far with that. Arguments to functions get copied, which means that if you were to do something like battle(player, enemy);, the function would receive copies of those objects and wouldn't alter the originals (in which you are interested) at all! Your player and your enemy objects would be left unchanged!
The solution: (you've guessed it) pass a pointer to the object instead of the object itself.
A pointer variable is a just another variable, which can contain a memory address of any other variable or object. So, instead of copying your player and enemy objects, doing something to the copies, and then destroying the copies after the function returns, you could tell your function where in memory the objects you want to change reside, and then it could operate on them directly.
Not only is this a more convenient method of treating objects, but it is also a great way to optimize (copying large objects can be expensive).
Now, the details and syntax. For any type (int, char, float, Humanoid, YourClass, etc), there is an adequate pointer type. A pointer to an integer and a pointer to your Humanoid object do not actually differ in size most of the time, as they contain addresses, but C++ and C still differentiate between those two types of pointers (and this is great for type safety).
Here's some basic code for manipulating pointers (the syntax is explained below):
int x = 42; //A variable of type int with a value of 42. int * a; //A pointer named "a" to a variable of type int. This doesn't yet point to anything (it is null)! &x; //The address of the integer x. a = &x; //Get the address of the variable x and store it in the integer pointer a. int y = a + 1; //Does not work! a is not an integer but a pointer to an integer (a mere memory address)! int y = *a + 1; //Does work! *a gets the value at the memory address to which a points to (which is x, with its value of 42). y is now 43.
Please note that the first * and the second one do not do the same thing at all!
On the second line, the first time * is used, it means "a is a pointer which points to an integer". To declare a pointer of any type, just write <type * name>. For example, a pointer to our Humanoid would be written as Humanoid * player;.
The second * is the dereferencing operator. It is completely different than the first one, it means "fetch me the value of the variable/memory address to which this pointer points to", in other words: dereference it. If you want to "get to" your actual player object from just that Humanoid pointer named player, you'd need to dereference it with *player, and then you would be able to access all the goodies this object hides. Otherwise you could just pass the pointer around for virtually no cost and let everyone know of where your player object actually is in memory.
The last operator is &. This has the same meaning as "get me the address of". If you created an actual object with Humanoid player;, instead of just a pointer with Humanoid * player;, you could use & in order to get the address of player.
The following can clear things up:
Humanoid player(100, 2, "Billy"); //Initializes a player object in memory. Humanoid * pointer_to_humanoid; //Creates a pointer of type Humanoid. WARNING: it doesn't point to anything and trying to dereference it would cause an error. pointer_to_humanoid = &player; //Gets the address of the player object and stores it into the pointer_to_humanoid. You can now pass this pointer around (although you could just pass &player). if (pointer_to_humanoid) { //Execute this block if the pointer is not null. //These checks should be used when a function returns a pointer, for example, and there is a chance that it is uninitialized. }
Here is the code for that little game that was mentioned through this post:
#include <iostream> using std::cout; using std::endl; class Humanoid { public: int health; bool alive; int strength; const char * name; Humanoid(int, int, const char *); }; Humanoid::Humanoid(int ahealth, int astrength, const char * aname) { health = ahealth; strength = astrength; name = aname; alive = true; } Humanoid * battle(Humanoid * h1, Humanoid * h2) { Humanoid * winner; while (h1->alive && h2->alive) { if (h1->health < 0) { h1->alive = false; winner = h2; } else if (h2->health < 0) { h2->alive = false; winner = h1; } else { h2->health -= h1->strength; h1->health -= h2->strength; } } return winner; } int main() { Humanoid player(100, 10, "Billy"); Humanoid enemy(50, 5, "Gnawk"); Humanoid * winner = battle(&player, &enemy); cout << "The winner is " << winner->name << "." << endl; cout << "The player is " << (player.alive ? "alive" : "dead") << "." << endl; cout << "The enemy is " << (enemy.alive ? "alive" : "dead") << "." << endl; return 0; }
In the example above you will notice that I used yet another operator: ->. It is also a dereferencing operator, and is used to simplify the syntax of OOP. Its use is equivalent to (*h1).health, but instead of writing that you can just write h1->health to access the health member.
Hopefully, you now understand what pointers are, how to use them and what to use them for. This would conclude my tutorial on pointers!
Topics I'd like to cover next:
- The heap and stack
- Arrays and pointer arithmetic
- Function pointers
Editor Note: Check out our other tutorial on Pointer!
Edited by Roger, 19 February 2013 - 03:31 PM.
added links