Arduino programming guide series

16

Understanding references and pointers

The topic of memory pointers in C and C++ is a known cause of intense headaches for many of us. It is the one topic that will most often scare people away from C/C++ and into "higher level" languages like Python and Ruby. But, with a bit of patience, you can understand it. 

Attribution of the image above: This file was made by User Sven. Original: https://commons.wikimedia.org/w/index.php?curid=8861432

Even though microcontrollers are becoming increasingly powerful and able to support languages like Python, Lua and Javascript, the traditional C/C++ toolbox is still dominant and not going anywhere. With a bit of effort, you can learn how to use C and C++.

And yes, that includes figuring out how memory pointers work!

In this article, I will help you understand how the "*" and "&"  memory operators work with the help of a simple example.

I will use this sketch below (you can also see it on Github):

In the sketch, I manipulate primitive and object variables by reference, pointers, and value.

Every time I make a change to a stored value, pointer or reference, I print out the remaining memory on the Arduino; this allows us to get a glimpse of the effect that a particular operation has on the memory, and in particular the static RAM (SRAM).

A few things are worth noting here:

The reference operator "&" is used to extract the memory location where the data of a variable is stored.

For example, look at line 29:

pointer = &a;

Here, we are getting the memory location where the value for the int variable "a" is stored, and we store this memory location to pointer "pointer".

The pointer operator "*" designates a variable as a pointer. This means that whatever is stored in this variable is a pointer to another memory location. Again in line 29, the variable "pointer" will not contain the value that is stored in "a", but the memory location that we stored in variable "a".

You can re-assign pointers just like any other variable. For example, in line 40, I re-assign the "pointer" variable to point to int "b", instead of the original "a".

When it comes to passing values to functions, it can be done in three ways: by referencepointer, or by value.

When you pass an actual value to a function (instead of a reference or pointer to that value), you are creating a copy of that value that "lives" inside the context of the function. 

When the value is a primitive (like an int or a char), this not too expensive in terms of memory utilization. 

But when it is an object, like a String, then the memory consumption can be significant. 

The lesson to take home: pass object values to functions as a reference or as a pointer.

See the examples in lines 50 and 56, and note how these target functions are declared.

If you want to pass a primitive, you can consider using references or pointers if the size of the primitive is larger than the size of the pointer.

In the Arduino Uno, an int takes up 2 bytes in SRAM. The SRAM address uses 1 byte. Therefore, by using a pointer to reference a value, you can save 1 byte of SRAM.

A double primitive consumes 4 bytes, so by using a pointer you can save 3 bytes.

What's better: references or pointers?

My research does not indicate strongly that one is better than the other. I personally prefer references.

Have a look at line 84, and compare it with line 89. I think that the calling code in line 84 looks cleaner. There seems to be no difference in terms of memory or compiler efficiency.

Feel free to play around with the sketch. Experiment with it by making changes to the memory operators so you can get a first-hand impression of the memory footprint of the examples. 

Experiment output: 

freeMemory()=1774
freeMemory()=1774
1 ----- a -----
a=5
a=6
freeMemory()=1774

2 ----- b -----
b=10
b=11
freeMemory()=1774

3 ----- Passing by reference -----
6
freeMemory()=1770

3 ----- Passing by reference -----
11
freeMemory()=1770

4 ----- Passing by pointer -----
11
freeMemory()=1770

4 ----- Passing by pointer -----
6
freeMemory()=1770

5 ----- Passing by value-----
6
freeMemory()=1770

5 ----- Passing by value-----
11
freeMemory()=1770

6 ------- String a -----
string a=Hello World!
freeMemory()=1744

7 ------- String b -----
string b=Hello again!
freeMemory()=1744

8 ----- Passing String by reference -----
Hello World!
freeMemory()=1740

8 ----- Passing String by reference -----
Hello again!
freeMemory()=1740

9 ----- Passing String by pointer -----
Hello World!
freeMemory()=1740

9 ----- Passing String by pointer -----
Hello again!
freeMemory()=1740

10 ----- Passing String by value-----
Hello World!
freeMemory()=1725

10 ----- Passing String by value-----
Hello again!
freeMemory()=1725

"Arduino programming" series

New to the Arduino?

Arduino Step by Step Getting Started is our most popular course for beginners.

This course is packed with high-quality video, mini-projects, and everything you need to learn Arduino from the ground up. We'll help you get started and at every step with top-notch instruction and our super-helpful course discussion space.

Tech Explorations Arduino intermediate level

Done with the basics? Looking for more advanced topics?

Arduino Step by Step Getting Serious is our comprehensive Arduino course for people ready to go to the next level.

Learn about Wifi, BLE and radio, motors (servo, DC and stepper motors with various controllers), LCD, OLED and TFT screens with buttons and touch interfaces, control large loads like relays and lights, and much much MUCH more.


>