pointers

Trying to get a grip on pointers. Not exactly going to well. I'm not sure how to use them properly. I've read & is address of, and * is to point to a value, or assign a value, or something like that. How does one go about using pointers in the most basic way?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<iostream>
#include<string>
using namespace std;
int main()
{
  int a = 12;
 

  c = *a;

  cout << c;

  return 0;
}


this obviously isn't right, lol.
well I compiled a working program, but it's not doing what I want, which is giving a a value.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<iostream>
#include<string>
using namespace std;
int main()
{
  int a;
  int * c;
  *c = 10;
  c = &a;
  cout << a;



  return 0;
}
OK,

pointer need to be declared also:
int* c;
is missing here. This tells the compiler that c is variable that holds pointer to int.
You can now assign it values (remember it takes pointer to int as value) so you assign it by this:
c = &a;
which means that c now contains the address of the variable a which hold int or in other words point to int (point to a memory block where int are stored).

Using this address you can access the value of a (since we have stored it into c). Like this:
*c = 10;
which means that now the pointed memory block (i.e. a) contains 10 instead of its previous value (i.e. 12).

If you use now
cout << a;.
this prints out 10

Maybe you have also encountered references also. They are C++ only and their purpose is to make our life a bit pointer free (though they are related to pointers)
They are declared like this:
int& r = a;
which means r is a reference to a. Reference just means another name for a in this case. This has to do with the fact that a and r are interchangeable in every aspect. One can go to the place of the other with no consequences. This has also the subtle effect that each time one is changed the other is also affected. This is the trick for calling by reference.

Anyway I mentioned references only because they use the same symbol with pointers (&) and that's confusing sometimes.

And one final difference between them is that reference MUST be initialized when declared (cannot reference to nothing) while pointers can (they can point to NULL).

Hope it helped a little.
1) Pointers are variables which store a memory address.
2) You declare a pointer like this: type *varname; In that context the asterisk * says that varname is a pointer variable which holds a memory address to a value of type type.
3) You take the memory address of a variable by using ampersand & (the reference operator).
4) In contexts other than declaring a pointer, the asterisk * becomes the dereference operator. It has the meaning of "use what's stored at address contained in pointer".

Example:
1
2
3
4
5
6
7
int a = 12;
int *p2a; // declare a pointer to a

p2a = &a; // give pointer memory address of a
*p2a = 133; // change the value at address of a to 13

cout << a << endl; // surprise, a is no longer 12 

Pointers have to point at something! c is just pointing at random memory, as you haven't initialized it. (line 8 and 9 are the wrong way round?)

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<iostream>
#include<string>
using namespace std;

int main()
{
  int a = 0; // set a to a known value, rather leaving it uninitialized (i.e. a random value)
  int * c = &a; // set pointer address before using it!
  *c = 10;
  cout << a;

  return 0;
}

Last edited on
You are familiar with an int? An int is an object that takes up an amount of memory (commonly 4 bytes, sometimes 8) and stores a single numerical value.

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

With me so far?

Now, you know how to create an int? Like this:

int a;

You create a pointer exactly the same way;

1
2
int* a; // This create a pointer to an int
double* b; // This creates a pointer to a double 


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 int without assigning it a value, it'll be just some garbage value? Just whatebver happens to be in the memory at the time.

int b; // This int could have any value at all

Same thing with pointers

int* b; // This int pointer could have any value at all


Of course, the same rules for naming things apply, so this:

1
2
int a;
int* a;


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 I 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).

1
2
int a; // an integer
&a; // the address of that integer 


So here's how we could use that:

1
2
int* p; // a new pointer, with some garbage value - could be pointing anywhere
p = &a; // now we've stored the address of a in the pointer, so now the pointer is pointing at the object a 


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 int* 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 int*, 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 MrPointer, and I wanted to get what it was pointing at, I would do this:

*MrPointer;

Let's say it points at an int. I could do this:

int someInt = *MrPointer;

or I can just use it in an expression

int anotherInt = 4 + *MrPointer;

Is all this clear? If so, we can do "pointer arithmetic" which is a lot easier than it sounds.
Last edited on
It was clear except the last little bit on dereferencing.

int someInt = *MrPointer;

this sets someInt to the value of *MrPointer.

I am interested in hearing that bit on pointer arithmetic though.
No worries.

I'll expand.

1
2
3
4
5
6
7
8
9
10
11
12
13
int a = 7; // Make an int, give it the value 7

int* MrPointer; // Make a pointer. Don't give it a value - it's got some garbage value
            //      if I try to use it, I deserve everything I get!

MrPointer = &a; // Now, MrPointer contains the memory address of the int a

*MrPointer; // This means "get me the int at the memory address you're pointing to" - we know the value
          //    of that int will be 7. I'm not doing anything with it, so this is a bit of a waste of code!

int b = *MrPointer; // Now I've made a new int, and set it to the value of whatever MrPointer points at. 
                   //    We know MrPointer holds the memory address of a, so the value returned is 7,
                   //     so the value of b will be 7. 


How's that?

Last edited on
oh, that's genius, completely get it now, lol.
thanks. :) ahha, quite happy about learning that.

Soon I'll be able to update the library I'm working on so that my function actually takes in a pointer, and return an updated value of health. This was something I was unable to do without pointers. I believe so anyway.
Last edited on
So, pointer arithmetic.

You are happy with regular arithmetic? Let's recap with an int.

1
2
3
int a = 7;
a++; // Now, a holds the value 8
a = a+4; // Now, a holds the value 12 


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.

int* b = &a; // the pointer b holds the memory address of our int a, from above

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 int is commonly 4 bytes. If the address of b is 0x22334455, then the int 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 int, like b above, when we add 1 to that pinter, the memory address will change by 4 so that it now holds the memory address of the next int along in memory. It is up to us to make sure that there actually is an int there! The compiler will trust us to do it right.

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

1
2
int c[2]; // This will make two int values in memory, right next to each other
int* p = &(c[0]); // This makes a pointer p, and gives it the value of the address of c[0], which is the first int 


Note that this would also work;

1
2
int*p = c; // This makes a pointer p, and gives it the value of the address of c, which is itself a pointer,
            // pointing at the start of the array. 


Now, let's add 1 to the pointer.

p = p+1; // Now, the pointer holds the value of the next int along, c[1].

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 int 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.

So, check this:

1
2
3
4
5
6
7
double x[10]; // an array of 10 doubles
double* p = x; // p is a pointer, now pointing at the first double
for (int i=0;i<10;i++)
{
  std::cout << *p << std::endl;  // output the double value
  p++; // make the pointer point to the next one
}


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

As you'll realise, this opens itself to all sorts of neat stuff. How about if you had an int, 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 int, and then you added one to the char pointer, you'd be looking at the very next byte - inside the int!

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>

int main()
{

  int a = 34;
  char* p = (char*)&a; // Make a char*, and force the compiler to treat the address of a as a char pointer.
  for (int i =0;i<4;i++)
    {std::cout << (int)(*p) << std::endl; p++;} // Cycle through the 4 bytes of the int, outputting each byte as if it were an int
  return 0;
}


This one is a tiny bit more interesting

1
2
3
4
5
6
7
8
9
#include <iostream>
int main()
{
  int a = 256;
  char* p = (char*)&a; // Make a char*, and force the compiler to treat the address of a as a char pointer.
  for (int i =0;i<4;i++)
    {std::cout << (int)(*p) << std::endl; p++;} // Cycle through the 4 bytes of the int, outputting each byte as if it were an int
  return 0;
}


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++!

Last edited on
lol, it's all seeming really interesting, but I must say some of the stuff is still well over my head. Although I am learning. I really appreciate the help. I think if I keep at it, and mess around with the language it'll all fall into its place for me.

I had probably completely figure out this pointer business, and stuff of that nature before I even try and comprehend the complexities of something like

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>

int main()
{

  int a = 34;
  char* p = (char*)&a; // Make a char*, and force the compiler to treat the address of a as a char pointer.
  for (int i =0;i<4;i++)
    {std::cout << (int)(*p) << std::endl; p++;} // Cycle through the 4 bytes of the int, outputting each byte as if it were an int
  return 0;
}


Thanks for the crash course though. I really appreciate it. Maybe you could help me with my next thread. On effectively passing a pointer into a function, so that the variable in main inherits the change to the pointer after it's been processed in the function.
No worries. It's worth playing around with that code above, though. So long as you have the idea solidly in your head that a pointer is a complete, real object that holds a value you can use to find a different object somewhere else in memory, you're there. Many people never understand that and it causes them no end of grief.

Since a pointer is a complete, real object, can you have a pointer to a pointer? Of course you can. How do we make one?

1
2
3
int a; // make an int
int* b; // make a pointer to an int
int** c; // make a pointer to an object, and that object is an int* 


and so on. As you can see, there's really no trick to it so long as you know what a pointer is.
Last edited on
Topic archived. No new replies allowed.