Need some explanation with Pointers

I started programming on my own snake game for the arduboy and came across something very interesting I can’t explain to myself (I just discovered it and never thought it would work).

In the Headerfile of a class there is an Arduboy pointer (looks like this: Arduboy *arduboy; )
and a method requiring a pointer (looks like this: void method(Arduboy *ard) )

Now the method looks like this:

void method(Arduboy *ard) {
arduboy = ard;
}

Why and how does this work? Can someone explain this to me please.

Can you point to the actual code you’re asking about? The code you’re showing is just modifying a global variable that points to an Arduboy class. The place where it’s used is likely to be more interesting.

I see what you did there!

By default, when you call a function in C++ the values are copies rather than the original values.

Take this code:

void AddThree(uint8_t x) {
  x = x + 3;
}

uint8_t x = 3;
Serial.print("Before:"); Serial.println(x);
AddThree(x);
Serial.print("After:"); Serial.println(x);

Will produce the result:

Before: 3
After: 3

Why? Because the function is working on a copy of the variable rather than the one passed. Once the arithmetic is complete and the function returns, the copy is discarded.

Change the code to :

void AddThree(uint8_t *x) {
  *x = *x + 3;
}

uint8_t x = 3;
Serial.print("Before:"); Serial.println(x);
AddThree(&x);
Serial.print("After:"); Serial.println(x);

Will produce the result:

Before: 3
After: 6

What’s happening?

The AddThree function now uses a pointer to the original value and is operating on the original value. The pointer is simply a reference to the memory address of the original value. When we call the function, we use the & operator to say pass the memory address of this variable rather than the actual value.

This is a terrible way of doing this though. It would be possible to pass a pointer to anything to the function and the function would try to add 3 to it. Imagine if you called it like this:

AddThree(arduboy);

You would need to add additional code to check that your pointer actually points to a uint8_t before blindly adding the value.

A better approach is to pass by reference. Its easier, cleaner and more readable.

void AddThree(uint8_t &x) {
  x = x + 3;
}

uint8_t x = 3;
Serial.print("Before:"); Serial.println(x);
AddThree(x);
Serial.print("After:"); Serial.println(x);

Will produce the result:

Before: 3
After: 6

In this example, the function receives a reference to the variable by default. Any changes you make to it will be made on the passed variable, not a copy.

2 Likes

Just to add a bit of clarification…

Technically you’d have to do AddThree(reinterpret_cast<uint8_t *>(arduboy)) (or worse, AddThree((uint8_t *)arduboy)) to get that to work.
(Fortunately we don’t live in a world where all pointers are implicitly convertible to uint8_t *.)

If you don’t add a cast the compiler will detect the type mismatch and stop you, which is why a strong type system is a good thing.
(In a dynamically typed language you wouldn’t discover the mistake until you ran the program.)

The bigger issue is null pointers, you could easily write AddThree(nullptr) and the compiler wouldn’t complain.
(I’m not sure if integer literals are implicitly convertible to pointers. I’d hope not.)