Skip to main content
GameDev.net gamedev.net
🔒 Locked

use of pointers

Started by RaoulJWZ Jul 31, 2014 at 4:13 PM 19 replies 4.9k views
Original Post
RaoulJWZ
RaoulJWZ

Hello everyone,

right now i'm reading a book about c++.

It explaines very well how to use pointers, but it's not very clear why

to use them and why they are so handy. Could you tell me that

thanks in advance.

KulSeran
KulSeran

Probably because nearly everything ends up being a pointer at some point. Memory can be thought of as a bunch of numbered boxes arranged in order. You might call 4 of those boxes "x" when you define "int x = 5;" but they're still numbered and the compiler still keeps track of where "x" is in terms of the numbered boxes. Because of that, you can type "int *pointerX = &x" to get that information for yourself. So, why would you?

To modify something in another scope.


void bar(int &n) {
  n = 7;
}

void baz(int *n) {
  *n = 8;
}

void foo() {
  int x = 5;
  std::cout << x << std::endl; // 5
  bar(x);
  std::cout << x << std::endl; // 7
  baz(&x);
  std::cout << x << std::endl; // 8
}

To keep track of things not allocated on the stack. X in the example above is stack allocated. But to hold larger values, you need to get memory from the OS using functions like malloc/free and new/delete. This opens up the world of dynamic allocation. It can be tricky, and you'd generally want to use tools like std::list, std::vector, std::map to handle this for you.


void printArray(int *n, int size) {
  for (int i = 0; i < size; ++I) {
    std::cout << n[i] << ", ";
  }
}

void foo() {
  int count = 0;
  std::cout << "how many numbers to generate" << std::endl;
  std::cin >> count;
  // this won't work.  Since count isn't known at compile time.
  // int x[count];
  // but this will:
  int *x = new int[count];
  for (int i = 0; i < count; ++I) {
    x[i] = rand();
  }
  printArray(x, count);
  // and you have to clean it up afterwords.
  delete[] x;
}

But one of the key comments there is "known at compile time". If you want to have a max of 32 players in a game, that can be hard-coded and the compiler can know about it. But if you want the max player count to be read in from a config file, so that people can increase it later if their machines are powerful enough, then the compiler can't know about it. When the compiler can't know about the count of objects, you're into the realm of needing to dynamically allocate objects which at some point results in pointers to memory being created.

Glass_Knife
Glass_Knife

I've been wondering about this myself. From the perspective of someone teaching C++, it can be difficult to explain the difference between a variable, a reference, and a pointer.

I have not found a good example. Using the analogy of little boxes is good, but it still fails to capture the concept.

I suspect that using a pointer or a reference to an object is understandable when the student understands that all items passed to/from functions are copied. It is much better to copy a 32bit pointer or reference to an object that to copy the object.

And as KulSeran pointed out, if you need to allocate memory at run time, you'll get a pointer, so you need to understand how they work. But for me, I still feel like something is missing capturing the difference between a reference and a pointer.

C++ references have two properties:

  • They always point to the same object.
  • They can not be 0.

Pointers are the opposite:

  • They can point to different objects.
  • They can be 0.

But I have been unable to explain this so that someone new to the language can remember or understand the significance.

Any thoughts which might help me and the OP?

JonathanCCC
JonathanCCC

A reference must be initialized to point to something straight away and cannot be re-assigned to point to something else, unlike a pointer.

So it's good practice to use references over pointers where possible to avoid bugs and errors.

Agony
Agony

I think that for a person just learning to code, the focus should not be about the technical aspects of pointers, and how they differ from C++ references technically. Instead, the focus should be on the high-level concept of references generally, abstracted away from C++ specifics. Pointers just happen to be a somewhat low-level way to implement and express references. So the question for a new programmer really becomes, "What's the use of references of any form?"

The general use of references are to provide a way for one piece of data to refer another piece of data by identity, not by value. So that if the data that is referenced changes its value, the data that refers to it will be able to access the new value, rather than simply being left with a copy of the old value.

References in general (and as a consequence pointers also) make a whole lot more sense once you get into the computer science topic of data structures and algorithms. Which I would strongly suggest any beginning programmer do as soon as they have a working grasp of basic programming concepts (variables, conditions, loops, simple input/output). Data structures and algorithms sort of form the foundation of all computer science and good software engineering, in my opinion. That, and I just find it to be a fascinating topic to explore, even now after two decades of programming.

When you begin studying the classic data structures like linked lists, trees, and hash tables, as well as the algorithms associated with them, such as searching for an item, sorting all items, or inserting and removing items, you'll really begin to appreciate how essential references are to programming.

For someone coming from another language that doesn't support pointers, the technical details do become more important, since they are likely to already know about data structures and the general uses of references. In that case, I like to simply think of pointers as integers, and the computer's memory as one gigantic array. The pointers just index individual elements of the array, and now I have a whole bunch more power and responsibility to manage these element references however I want.

"We should have a great fewer disputes in the world if words were taken for what they are, the signs of our ideas nly, and not for things themselves." - John Locke o
bjgamer
bjgamer

Try to think of pointers as street addresses. Its a lot faster and more efficient to say I live at "101 binary street" than it would be to grab your actual house and say I live here.

rip-off
rip-off

Here are common reasons why people use pointers:

0) A particular object or array is large, and they don't want to incur a copy of it when passing to a function. Passing a pointer is small (typically four or eight bytes), versus copying potentially thousands or millions of elements in an array.

Note: C++ provides references for this.

1) The programmer wishes to add an optional parameter to a function. If the parameter is not important, the programmer will pass a nullptr. Otherwise, they pass the address of an interesting object. The function can test if the pointer is null before using it.

2) The programmer wishes an object to live beyond the scope it is created in. Typical C++ objects live in a given scope, and can only be copied outside that scope. By dynamically allocating the object and storing a pointer to it, the original object can live longer than its scope and can be deleted later. An example in a game is a bullet, there might be a shoot() function, but the bullet should live until it hits something, not be destroyed at the end of the function.

Note: modern C++ recommends using smart pointers for allocating and managing the ownership of such objects.

3) The programmer has a rich ownership convention, where raw pointers convey the concept of "this object does not own the other object". Care must be taken with this approach to avoid dangling pointers. A possible alternative is a "weak pointer". An example in a game might be an AI object's current target.

4) Interacting with C style APIs. Some APIs pre-date C++, or otherwise are written in a rather simple fashion (e.g. for crossing DLL boundaries). In this case you might be forced to deal with these pointers. An example in a game might be the SDL library, which returns the "screen" surface as a raw pointer from SDL_SetVideoMode().

Note: Modern C++ style recommends "wrapping" the code that deals with such objects to some degree, at the very least using smart pointers to manage their lifecycle.

RaoulJWZ
RaoulJWZ

okay thanks everyone, i understand it way better now!

RaoulJWZ
RaoulJWZ

Well, all of the explainations pushed me more and more in the right direction (even staticPoof's :D),

There are two reactions who helped the most: The one from Andy Gainey and rip-off.

For me these two reactions are clear and easy to understand, while english isn't my first language.

I hoped i answered the question with this, otherwise you just have to say it.

thanks again!

Lactose
Lactose

I also remember pointers clicking more in my mind when I thought them as addresses or directions for an object.

I thought about data types as e.g. boxes, and pointers as a paper with the directions to the box on it.


int myInt;

--> an int sized box with myInt written on it. int sized because it needs to be big enough to store whatever int I want to put in it.


int *myIntPointer = &myInt

--> a piece of paper saying "the box in the kitchen on the top shelf".

Somewhat unrelated, hovering the "pointers" tag of this thread gives some fairly entertaining suggestions.

Hello to all my stalkers.
frob
frob




There are two reactions who helped the most: The one from Andy Gainey and rip-off.
For me these two reactions are clear and easy to understand, while english isn't my first language.

That is my normally preferred explanation as well:

Try to think of pointers as street addresses. Its a lot faster and more efficient to say I live at "101 binary street" than it would be to grab your actual house and say I live here.

You can either refer to the address, or you can chose that every time you refer to it you build a clone of the entire house, clone every item in the house, exactly duplicate the house in every aspect, then use the clone.

A pointer is just the address. It is a scrap of paper with an address. It can have a nonsense address, or even a blank address on the paper.

A reference is somebody saying "This is an object's home". It cannot be blank since that would make the object homeless. It cannot be modified, since the home will remain at that location.

HScottH
HScottH

To not understand the utility of pointers is to not understand the language. A much older and wiser engineer than myself once said something that I immediately understood and knew to be true: to be a real black-belt with any technological level, you must know the one beneath it.

To understand how to really use C or C++, you need some Assembly experience. Not a lot, you don't need to be a pro, but this will show you what the machine is doing and how, and you will suddenly understand everything in C and C++ intuitively--and you'll be damn good at it too!

The good thing in that all modern C/C++ compilers support inline assembly, so you don't need new tools, just google "x86 assembly tutorial" and spend an evening or two doing some ultra-low level coding :-)

tonemgub
tonemgub

A pointer is just 4 bytes (or 8 bytes in 64-bit), whereas the data it points to might be in the order of megabytes... Passing the pointer to the data around where it's needed is obviously faster than passing around the whole data.

Funny analogy: if you want to tell someone where you live, you don't just get a crane to lift your house and bring it to them to show them you live in that house... you just give them your address.

RaoulJWZ
RaoulJWZ

Thanks everyone for all those many reactions.

The only question i still have is: Is the whole explaination about the house

and the adress only of use with pointers to functions and arrays, or with normal int variables to?

(because a pointer to an int is 4 bytes and an int variable to)

thanks!

Agony
Agony

Thanks everyone for all those many reactions.

The only question i still have is: Is the whole explaination about the house

and the adress only of use with pointers to functions and arrays, or with normal int variables to?

(because a pointer to an int is 4 bytes and an int variable to)

thanks!

If you use that int as an address of some sort, then yes, the analogy of a house and address can be just as relevant to an int as it can to a pointer.

For example, if you have an array of 100 items, and an int that refers to the index in the array of the item most recently selected, then it is behaving like a reference to some other piece of data. It's as if you already know which street the house will be on (this is the array of 100 items), and now you simply need to know which of those 100 houses is the one you're interested in at the moment (this is the int index). There may be many other streets, but they serve other purposes. If you don't know which street contains the item you're looking for, then just having an array index won't help you. ("I'm looking for the fifth item, but the fifth item in which array?")

Pointers are sort of like global addresses, however, and don't care about which street a house is on. ("I'm looking for the seven hundred and sixteenth house in the world.") A basic array in C++ can in fact behave very much like a pointer. If an array is like a single street with multiple houses, using it like a pointer is like getting the global address of the very first house on the street. ("The first house on this street is the three hundredth house in the world.") If you want to access the very first house, you just use that address directly. If you want to access the fifth house on that street (which would be four houses down from the first house), you just take the address of the first house, and add four to it. That will give you the global address of the fifth house on that street: It is the three hundred and fourth house in the world. Something like this is in fact what your code is actually doing whenever you access an array using an int index.

There are other ways that an int could serve as a reference. If you research hash tables, you'll discover that an int can be used as a way to quickly find an item. It's typically referred to as a "key", but in this case it works very similarly to an address. It is a small piece of information that allows you to quickly find the full data to which it refers.

Hash tables pretty much only use some kind of int for their keys, but other data structures can use a wide variety of types as their keys. Strings can be used, pretty much in exactly the same way as you might look a word up in a dictionary. Two-dimensional vectors might be used as keys to look up which items are stored in a particular cell of a 2D grid, given an (x, y) location.

So the house/address analogy works for a lot of different situations. The curious aspect about pointers specifically is that the there is only one street in the entire world, it is very very long, and all the houses are only on one side of the street. The addresses theoretically start at 0, and increase by 1 until you reach the end of the street (the maximum amount of memory available)*. Other systems of houses and addresses might not be so simply organized. A dictionary can still be ordered alphabetically, but there may be large gaps between one word and the next. A hash table doesn't even have a well-defined concept of order; as you walk along its street, the addresses might go 3083, 95, 40382, 8593... There is a system that organizes it all, but it's not obvious to a casual human observer.

* Side note about pointers and the very long street: Modern operating systems and CPUs do a bunch of work that provide us with this rather simple view of memory. But it's not really a technically accurate view about what's actually going on in memory and in the hardware. In general, you shouldn't need to worry about this at all. But you might encounter patterns with pointer values that seem odd, or at some point you might need to delve into some more serious systems programming. In such cases, being aware that there is a deeper layer of complexity regarding pointers and addresses will help you avoid temporary confusion, and provide you a direction for research. "Virtual memory" would be the primary term to search for.

"We should have a great fewer disputes in the world if words were taken for what they are, the signs of our ideas nly, and not for things themselves." - John Locke o
SeraphLance
SeraphLance

Thanks everyone for all those many reactions.

The only question i still have is: Is the whole explaination about the house

and the adress only of use with pointers to functions and arrays, or with normal int variables to?

(because a pointer to an int is 4 bytes and an int variable to)

thanks!

Ints, arrays, and functions are all just data. They're meant to be interpreted differently, but they're all just data. This is particularly important when you're dealing with pointers. float* foo = bar; means "get the value of bar. I want to store that value in a variable named foo. I want you to pretend that value is an address to a block of memory, and I want you to pretend that block of memory is a float."

That block of memory could have been an int. bar itself could have been an int. C doesn't care, and it will try to do what you ask regardless.

To not understand the utility of pointers is to not understand the language. A much older and wiser engineer than myself once said something that I immediately understood and knew to be true: to be a real black-belt with any technological level, you must know the one beneath it.

To understand how to really use C or C++, you need some Assembly experience. Not a lot, you don't need to be a pro, but this will show you what the machine is doing and how, and you will suddenly understand everything in C and C++ intuitively--and you'll be damn good at it too!

The good thing in that all modern C/C++ compilers support inline assembly, so you don't need new tools, just google "x86 assembly tutorial" and spend an evening or two doing some ultra-low level coding :-)

Turtles all the way down. Does this mean you need to know micro-instructions in order to be good enough at assembly in order to be good enough at C? Do you then need to pick up Verilog in order to be able to synthesize the hardware?

There's other issues with this idea too. What's "below" python? Certainly, the reference implementation is written in C, but there's nothing fundamentally C-like about it. They could just as easily have written it in FORTRAN.

Eck
Eck

I didn't see where anyone mentioned Polymorphism yet.

You can use pointers to common base classes instead of instances of specific classes. For example: instead of your physics manager having a list of starships, missiles, asteroids, etc. You can create a PhysicsObject base class and then derive your Starships, Missiles, and Asteroids from them.

Now your physics manager class can have a list of PhysicsObject pointers and not care about which derived type each item is. Each of these derived items can be treated like a PhysicsObject so you have a list of them, and all your physics functions can take a PhysicsObject pointer.


// For this function, we could pass it a pointer to a Starship, a pointer to a missle, 
// or a pointer to anything that dervies from the base class PhysicsObject
void ApplyForce(PhysicsObject* physicsObject, float direction, float magnitude, float elapsedSeconds);

Do a search for C++ polymorphism for some more reading. This looked like a decent article.

http://www.codingunit.com/cplusplus-tutorial-polymorphism-and-abstract-base-class

- Eck

Glass_Knife
Glass_Knife




I didn't see where anyone mentioned Polymorphism yet.

Yes. That is another reason to understand them. If you are coming from Java or C#, you can just set references equal to base classes. But you need a reference or pointer in C++. No wonder beginners have trouble learning the language.

HScottH
HScottH
Turtles all the way down. Does this mean you need to know micro-instructions in order to be good enough at assembly in order to be good enough at C? Do you then need to pick up Verilog in order to be able to synthesize the hardware?

There's other issues with this idea too. What's "below" python? Certainly, the reference implementation is written in C, but there's nothing fundamentally C-like about it. They could just as easily have written it in FORTRAN.

Each "level" of language provides an abstraction from the lower (and generally previous) one; new languages have most often been added to improve developer productivity (with many more recent exceptions existing merely for preference, to enforce some specific programming paradigm, or to provide platform agnosticism--such as Java).

C, for example, while it is a high level language, was intended to abstract away the ridiculous verbosity of ASM, because for the vast majority of programs you don't need to know how, for e.g., a loop works on the hardware (in actual instructions): you just need to know it will work. This has the side benefit that it makes better programmers, because heuristics learned by seasoned low-level geeks can be baked into the compiler and then the rest of us get them for free.

It is a bit silly to say "the more you know, the better you'll be..." even thought it's true. But I would argue that the incremental improvement for a Javascript developer to learn low-level CPU architecture details is very near zero. But this is not so for closer languages. If you want to really excel at C or C++, learning a bit of what the developers of those language were trying to abstract will really pay off.

In the context of your question, this means learning a bit of assembly. The term "pointer" is a somewhat language-specific one, whereas more generally (and when using ASM) the term "address" would be used. You can't miss how the CPU handles memory and what role addresses play in processing--both all the time, and also in the context of creating highly performant code. And you can't walk away from even a few simple "Hello world" type ASM apps without feeling quite at home using them.

And no, learning below that will not pay much. But, if you decide to become a driver optimization guru and do much of your work in ASM, you would be well served by learning all about instruction piplining, microcode and more.

That would be rather architecture-specific knowledge. Fortunately, the basic stuff you would learn from ASM and be able to apply to C/C++ would not.

I hope I explained myself a bit more clearly :-)

Oh, and I know very nearly nothing about Python, so I can't really comment. Find out who created it and what they used before and you'll have your answer.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.