What's the point of pointers?



 

Download Code Example Here

One of the things that most confuses people about C++, are pointers. They add a lot of flexibility to your application, but with flexibility comes complexity. With any luck, I can shed some light on what they are, and how to use them in your code. 

What is a pointer?

A pointer is just a number that represents an address in memory. That probably seems too simple since pointers are one of the most commonly feared things about using a C based language, but that's really all they are. 

How do you use a pointer?

If you declare a variable like this

int MySpecialNumber = 5;

then you get the address of MySpecialPointer with the & operator.
So &MySpecialNumber tells the compiler that you want the address of the MySpecialNumber variable. 

You can try it out with this simple sketch:
int MySpecialNumber = 5;
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.print("address of MySpecialNumber: ");
  Serial.println((long)&MySpecialNumber); // you have to cast the address to a long, since the address might be larger than an int.
  Serial.print("value stored in MySpecialNumber:");
  Serial.println(MySpecialNumber);
  MySpecialNumber=6;
  Serial.print("address of MySpecialNumber: ");
  Serial.println((long)&MySpecialNumber);
  Serial.print("value stored in MySpecialNumber:");
  Serial.println(MySpecialNumber);

}


This will print something like this, although you might find that your address is different than mine:



There's also a way to give the compiler an address, then tell it you want the value that's actually there. This is called a 'dereference', and it's the * character. When you put a * in front of a pointer, the compiler knows to return the value stored in memory at the pointer address. 

So in our example you can change the line 

Serial.println(MySpecialNumber);

to  

Serial.println(* &MySpecialNumber);
and the output will be exactly the same. What you essentially wrote here is 'give me the value stored at this address'.

Now, here's where things start to get confusing. In order to declare a variable that holds a pointer, you need to tell the compiler what type of variable will be held at the address (such as int, long, string, double, etc,), then you put that * after the type. For instance we can store the address of our MySpecialNumber like this:

int *MySpecialNumberPointer = &MySpecialNumber;

This tells the compiler that MySpecialNumberPointer is a "pointer to an int", so it knows that it's an address and it also knows how much data is stored in memory starting at that address. 

Now, instead of writing this:
Serial.println((long)&MySpecialNumber);
We can write this instead:
Serial.println((long)MySpecialNumberPointer);

One of the things that gets people into trouble with pointers, is that you can literally tell them where to point - which means you can do something like this:

int *IAmAHacker = (int *)6;

Now, if you write:
Serial.println(* IAmAHacker);
You'll get back whatever is stored at memory location 6 - which could be a super secret value, but it's probably just garbage. 

So, what's the point (pun intended) and why would I use pointers?

The most common use of pointers is to have a single variable that holds something, and you want use it in a bunch of places but don't want to keep passing the whole variable around. You probably also want to alter the value stored in the variable and have that value reflected in all of the other places you might use it. 
It's also often the case that the object your using the pointer for is very large - like an array or something. The arduino doesn't have a lot of memory, so if you can declare your array only once, then just use a pointer to it whenever you want to use it, that will save your memory for other things. 
In fact, in C++ when you pass an array to a function the compiler automatically changes it to a pointer, so if you declare an array like this:
int mySuperLongArray[] = {1,2,3,4,5,6,7,8,9,10,11,12,13};
and you want to pass it to a function, then the function signature must be looking for a pointer to an int, like this:

void printArray(int* arrayToPrint, int size){
  for(int i=0; i < size; i++){
    Serial.print(arrayToPrint[i]);
    Serial.print(",");
  }
  Serial.println();
}

That means if you change your array inside of your function like this:
void changeArray(int* arrayToPrint, int size){
  for(int i=0; i < size; i++){
    arrayToPrint[i]++; // add 1 to every value in the array.
  }
}



Those values will be changed in the actual array, and not just inside the function. 
Here's a complete sketch that will show you this:
int mySuperLongArray[] = {1,2,3,4,5,6,7,8,9,10,11,12,13};

void printArray(int* arrayToPrint, int size){
  for(int i=0; i < size; i++){
    Serial.print(arrayToPrint[i]);
    Serial.print(",");
  }
  Serial.println();
}

void changeArray(int* arrayToPrint, int size){
  for(int i=0; i < size; i++){
    arrayToPrint[i]++; // add 1 to every value in the array.
  }
}


void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  printArray(mySuperLongArray,13);
  changeArray(mySuperLongArray,13);
  printArray(mySuperLongArray,13);
}

What you'll see in the serial monitor is this:










 Happy Coding!
~Tom

Comments

Popular posts from this blog

Using GIT with Arduino

Programming Arduino with Regular Expressions

Organize your Arduino code with header and class files