Jump to content


Check out our Community Blogs

Register and join over 40,000 other developers!


Recent Status Updates

View All Updates

Photo
- - - - -

Pointers!

pointers objects variables memory arrays arithmetic c++ c address

  • Please log in to reply
30 replies to this topic

#1 Yannbane

Yannbane

    CC Addict

  • Advanced Member
  • PipPipPipPipPip
  • 238 posts
  • Programming Language:C, Java, C++, PHP, Python, JavaScript, PL/SQL, Lisp, Assembly, Bash, Others
  • Learning:Lisp, Scheme

Posted 01 January 2013 - 04:26 PM

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

  • 3

My blog: yannbane.com. I post about programming, game development, and artificial intelligence.


#2 Ritwik I the programmer

Ritwik I the programmer

    CC Addict

  • Advanced Member
  • PipPipPipPipPip
  • 244 posts
  • Programming Language:Java
  • Learning:Java, C++, Python

Posted 05 January 2013 - 06:32 PM

Thanks for your brief but simple tutorial. Could you explain how one could check if a pointer actually does point to something, or has just been declared? I want to use this as an if condition. I tried null(Java habits die hard), but apparenty it isn't even a keyword in C++. Also, what does int **x mean?

Edited by Ritwik I the programmer, 05 January 2013 - 06:33 PM.

  • 0

I can believe, but why should I?


#3 Yannbane

Yannbane

    CC Addict

  • Advanced Member
  • PipPipPipPipPip
  • 238 posts
  • Programming Language:C, Java, C++, PHP, Python, JavaScript, PL/SQL, Lisp, Assembly, Bash, Others
  • Learning:Lisp, Scheme

Posted 06 January 2013 - 04:40 AM

Thanks for the feedback, Ritwik. I've added the method of checking whether a pointer is initialized or not. It's very simple: if (pointer) {/*executes if the pointer is not null*/}.

And about int ** x...

Technically it could be an array of arrays. For example, char * argv[] is the same as char ** argv, but I will explain that in my tutorial on arrays.
  • 0

My blog: yannbane.com. I post about programming, game development, and artificial intelligence.


#4 0xDEADBEEF

0xDEADBEEF

    CC Devotee

  • Senior Member
  • PipPipPipPipPipPip
  • 790 posts
  • Programming Language:C, Java, C++, C#, (Visual) Basic, Perl, Transact-SQL, Bash, Prolog, Others
  • Learning:Others

Posted 06 January 2013 - 05:00 AM

probably best to keep it simple.

int **x

is a pointer to a data item which is a pointer to an int.

Also there can be differences between char** and char*[], so be careful.
  • 0

Creating SEGFAULTs since 1995.


#5 Yannbane

Yannbane

    CC Addict

  • Advanced Member
  • PipPipPipPipPip
  • 238 posts
  • Programming Language:C, Java, C++, PHP, Python, JavaScript, PL/SQL, Lisp, Assembly, Bash, Others
  • Learning:Lisp, Scheme

Posted 06 January 2013 - 05:58 AM

Yeah, it's probably the easiest way of thinking about it, but it can be misleading in some cases (e.g. being confused by char * x[] and char **).

There is a difference, sure. IIRC it's about mutability, char ** being an array of constant strings (i.e. immutable), or something like that.
  • 0

My blog: yannbane.com. I post about programming, game development, and artificial intelligence.


#6 object

object

    CC Regular

  • Member
  • PipPipPip
  • 35 posts
  • Programming Language:C, C#, JavaScript

Posted 06 January 2013 - 06:02 AM

Ritwik: It's NULL in C, and you'll have to #include <stddef.h> to get to it. The easiest way to tell if a pointer points to something is to make it point to something or nothing, ie. initialise it so that it points at an object: &object or malloc(sizeof object), or initialise it so that it points at nothing: NULL... and when the pointer no longer points at something it would be a good idea to assign NULL to the variable, eg. object's storage duration has ended (the function that object was declared in returned), or a malloc()'d object was free()'d.

A pointer is a just another variable, which can contain a memory address of any other variable or object.

int x; Have you ever seen anyone write code like &x = 42;? That's silly, right? But &x is a value that has type "pointer to int", and <sarcasm>pointers are variables, thus values are variables</sarcasm>... It might be a good idea to explicitly remind students about the relationship between objects and values.

In addition, assuming that all types of pointers have the same alignment requirements is a recipe for eventual disaster. Here's what the C11 standard draft says:

7 A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned68) for the referenced type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer. When a pointer to an object is converted to a pointer to a character type,
the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.
68) In general, the concept ‘‘correctly aligned’’ is transitive: if a pointer to type A is correctly aligned for a pointer to type B, which in turn is correctly aligned for a pointer to type C, then a pointer to type A is correctly aligned for a pointer to type C.


A pointer to an integer and a pointer to your Humanoid object do not actually differ in size

Again, this isn't a requirement of the C standard. Stop assuming your architecture is the only one on the planet. Consider the pre-MacOSX Macintosh machines, which might segfault if you attempt to do something like: int *foo = (void *) "bar";... Here's what the standard says:

28 A pointer to void shall have the same representation and alignment requirements as a
pointer to a character type.48) Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements. All pointers to structure types shall have the same representation and alignment requirements as each other. All pointers to union types shall have the same representation and alignment requirements as each other. Pointers to other types need not have the same representation or alignment requirements.
48) The same representation and alignment requirements are meant to imply interchangeability as
arguments to functions, return values from functions, and members of unions.



Essentially, you've just learned about three new operators.

When used in declarations, the asterisk is not an operator; It's part of the type name. So, technically, we've just learnt about two new operators and a heap of new type names... Not three new operators.

And about int ** x...

Technically it could be an array of arrays.

-sigh-... Which book are you reading from, master teacher? I think it has some errors.

Edited by object, 06 January 2013 - 06:41 AM.

  • 0

#7 0xDEADBEEF

0xDEADBEEF

    CC Devotee

  • Senior Member
  • PipPipPipPipPipPip
  • 790 posts
  • Programming Language:C, Java, C++, C#, (Visual) Basic, Perl, Transact-SQL, Bash, Prolog, Others
  • Learning:Others

Posted 06 January 2013 - 06:08 AM

int a;
&a = 5;

doesn't work, &a isn't a lvalue. you need an lvalue to asign to.
  • 1

Creating SEGFAULTs since 1995.


#8 object

object

    CC Regular

  • Member
  • PipPipPip
  • 35 posts
  • Programming Language:C, C#, JavaScript

Posted 06 January 2013 - 06:36 AM

int a;
&a = 5;

doesn't work, &a isn't a lvalue. you need an lvalue to asign to.


That's my point... He said pointers can be assigned to. &a is a pointer value (which is one definition for "pointer"), as opposed to a pointer object (the other definition for "pointer"). Pointer values can't be assigned to.
  • 0

#9 0xDEADBEEF

0xDEADBEEF

    CC Devotee

  • Senior Member
  • PipPipPipPipPipPip
  • 790 posts
  • Programming Language:C, Java, C++, C#, (Visual) Basic, Perl, Transact-SQL, Bash, Prolog, Others
  • Learning:Others

Posted 06 January 2013 - 07:24 AM

I think for the level of this tutorial, its fine. There's nothing wrong with simplifing things - if all we did was say read the standard, nobody would learn C or C++.

There is a difference between types and variables, or values and objects. But that's kind of implicient. I mean 5 is a value of type int, but you hardly see people thinking they can write:

5 = 6;

So I wouldn't assume that, given the correct explaination of &, i.e it gives the address of the variable, not it gives a pointer to the variable. Most people would automatically assume they could do &a = x;

Anyway, maybe you could write a defacto tutorial on the subject. It would be of benift to codecall.
  • 1

Creating SEGFAULTs since 1995.


#10 Yannbane

Yannbane

    CC Addict

  • Advanced Member
  • PipPipPipPipPip
  • 238 posts
  • Programming Language:C, Java, C++, PHP, Python, JavaScript, PL/SQL, Lisp, Assembly, Bash, Others
  • Learning:Lisp, Scheme

Posted 06 January 2013 - 09:46 AM

int x; Have you ever seen anyone write code like &x = 42;? That's silly, right? But &x is a value that has type "pointer to int", and <sarcasm>pointers are variables, thus values are variables</sarcasm>... It might be a good idea to explicitly remind students about the relationship between objects and values.


int * x;

Would you agree that x is a pointer?

&y;

Would you agree that this expression evaluates to the address of the variable y?

If so, then I don't see what you were complaining about. Of course it is not possible to assign values to other values, I don't know where I suggested that &y = something; was legal...

You seem to be saying that pointers are values. Addresses are values, and pointers are types, or, colloquially, variables associated with those types (int x;, x is an integer).

In addition, assuming that all types of pointers have the same alignment requirements is a recipe for eventual disaster. Here's what the C11 standard draft says:

Again, this isn't a requirement of the C standard. Stop assuming your architecture is the only one on the planet. Consider the pre-MacOSX Macintosh machines, which might segfault if you attempt to do something like: int *foo = (void *) "bar";...


OK, my mistake. There are cases where pointers can differ in size, I just read about 8051 being an example of this. It is a Harvard architecture miC though and I totally didn't picture those while writing this tutorial. However I never implied that the conversion you proposed was a valid one. I only said that pointers have the same size in memory independently of the type of the variable they are pointing to, which is the case most of the time, and gets my point across pretty well.

If it is good enough to be assumed most of the time, it is good enough for an beginner-level tutorial.

When used in declarations, the asterisk is not an operator; It's part of the type name. So, technically, we've just learnt about two new operators and a heap of new type names... Not three new operators.


Very true, my bad. I wanted to emphasize how the asterisks in int * x; and *x are different so I misused the term "operator". I'll correct this part...

-sigh-... Which book are you reading from, master teacher? I think it has some errors.


Initialize it correctly and it could be a matrix. But simply saying "a pointer to a pointer" would probably be better.
  • 0

My blog: yannbane.com. I post about programming, game development, and artificial intelligence.


#11 Ritwik I the programmer

Ritwik I the programmer

    CC Addict

  • Advanced Member
  • PipPipPipPipPip
  • 244 posts
  • Programming Language:Java
  • Learning:Java, C++, Python

Posted 06 January 2013 - 10:35 AM

This means that uninitialized pointer has a value 0, right?

Edited by Ritwik I the programmer, 06 January 2013 - 10:35 AM.

  • 0

I can believe, but why should I?


#12 0xDEADBEEF

0xDEADBEEF

    CC Devotee

  • Senior Member
  • PipPipPipPipPipPip
  • 790 posts
  • Programming Language:C, Java, C++, C#, (Visual) Basic, Perl, Transact-SQL, Bash, Prolog, Others
  • Learning:Others

Posted 06 January 2013 - 10:42 AM

This means that uninitialized pointer has a value 0, right?


No, an uninitialized pointer, might have any value; just like any uninitialized variable.

When creating a pointer, it should either be initialised to the address of a variable or to NULL (or 0).

i.e.

int *p = 0;
int *p = NULL

  • 1

Creating SEGFAULTs since 1995.






Also tagged with one or more of these keywords: pointers, objects, variables, memory, arrays, arithmetic, c++, c, address

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download