Pointers

Published: 2021-05-08 13:55:57

Pointers


A pointer looks exactly the same as that. It takes up the same amount of memory as an  and stores a single numerical value. All pointers look the same, whether they are pointers to  or pointers to double or whatever. They take up the same space and store a single numerical value.

With me so far?

 	


Like any object, you have to create a pointer before you can use it.

You can create a pointer to any kind of object you can think of using this syntax, but don't be fooled - the pointers all take up 4 (sometimes 8) bytes and store a single numerical value.

You know that if you create an  without assigning it a value, it'll be just some garbage value? Just whatever happens to be in the memory at the time.
 	


would refuse to compile, as I have two objects with the same name.

So far, so simple, yes?

There is one special thing a pointer can do. You recall that the pointer stores a single numerical value? I can carry out an operation called "dereferencing" on the pointer, and that simply means I get back whatever object is in the memory location of that single numerical value. So, if my pointer stores the value 0x22334455 (I've used hex here, but it's just a number), when I dereference the pointer, I get back whatever is at memory location 0x22334455. We sometimes say that the pointer is pointing at the object in memory location 0x22334455.

How do we know what numerical value to put into a pointer (or, put another way, how do we know what the memory address of an object is, so that we can store than memory address in the pointer)? Fortunately, there exists an operator "&" which will return the address of any object (i.e. the numerical value of the place in memory where that object is).
	


There are circumstances where you might need to manually enter the address location yourself, and you can do that. Say you knew for sure that a value of interest was at address location 0x2323FFFF. You can manually set it, and we'll cover that a bit later.

If all pointers are the same size and store a single numerical value, why do we have  and double* and string* and all the rest? Because we need to know what kind of object we will get back when we dereference them. That's all. If we were being awkward, we could make an , and then force it to point to a double, because the pointer just stores a number - it does not store anything about what kind of thing it's pointing to. That's a bit more advanced, though.

We do this dereferencing operation with the "*". So, if I had a pointer called  and I wanted to get what it was pointing at, I would do this:
 		




Pretty simple. What about pointers? First, take a step back and recall what pointers are all about. The idea is that they hold a memory address, and that memory address is the address of some object somewhere in memory.
 	  	


So b will have some value, lets say it's 0x22334455.

Pointer arithmetic is the arithmetic of adding (and subtracting) values from a pointer. Recall that a pointer holds a memory address, so pointer arithmetic is the arithmetic of adding (and subtracting) values from a memory address. If we add the value 1 to a pointer, we don't mean "add 1 to that memory address you're holding". We mean "add enough to your memory to point at the next object along".

An  is commonly 4 bytes. If the address of b is 0x22334455, then the  will be occupying the bytes 0x22334455 and 0x22334456 an 0x22334457 an 0x22334458 (on some systems, it will be 0x22334455 and 0x22334454 and 0x22334453 and 0x22334452 - doesn't matter which, as your compiler will keep track of all that sort of thing for you and will adjust the pointer correctly). So if we have a pointer to an  like b above, when we add 1 to that printer the memory address will change by 4 so that it now holds the memory address of the next  along in memory. It is up to us to make sure that there actually is an  there! The compiler will trust us to do it right.

So, imagine we had two values next to each other in memory. We could have done this by making an array
  	


  	


Now, let's add 1 to the pointer.
 	p = p+1; // Now, the pointer holds the value of the next .  	


Pretty clever, eh? When you add 1 to a pointer, the value it holds does not change by one; it changes by exactly the right amount so that it will point to the next object along. It knows how far that is because when you made it, you specified what kind of object it will point to. If it is an  pointer, the value it holds will change by 4 (sometimes 8) bytes. If it points to some crazy object that is 40 bytes long, the value will change by 40 bytes. You don't need to worry about how much the memory address actually changes by; you can trust the compiler to handle it for you.

If you added 2 to a pointer, the value of the pointer would change exactly the right amount to point at the object 2 along, and so on. If you subtract 1 from a pointer, it will point to the previous object along in memory.




This code outputs the (garbage) values of an array of doubles, and demonstrates pointer arithmetic.

As you'll  this opens itself to all sorts of neat stuff. How about if you had an  and just for the fun of it, you wanted to look at each byte individually? A char is by definition one byte long, so if you had a char pointer, and made it point at that  and then you added one to the char pointer, you'd be looking at the very next byte -




You can get an insight into exactly what numbers are in the memory and you should be able to work out how the number 256 is being represented using the 4 bytes.

As you could guess, we could do lots of crazy stuff with this kind of power to directly interfere with individual bytes. Welcome to the low-level power of C and C++!


The difference uses of the "*" when dealing with pointers
The * has two meanings.


  	


When we create objects, the "*" is just part of the description of what kind of object we want to make. Once we've made the object, we just use the name of the object.

Here's how we pass the object b that we just made:
 	some Function(b);	


We don't need to specify that b is an  when we actually use it.

So here's how we pass the pointer we just made:
 	some Other Function(a);	


We don't need to specify that a is an -pointer when we actually use it.

The other use of "*" is as the dereference operator. We use it on a pointer when we don't want the actual pointer; we want what it is pointing at.
	


They are two different uses, with two different meanings. If you see * or double* or string* or any other such <object-type>* it is describing a kind of object. If you see *pointer, it means dereference.

You will see the "describing a kind of object" use in making pointers, and in specifying what kind of objects a function takes (and returns), as in this example function prototype:
 	void Deal Damage