Hurray, ROBOTC has support for pointers! “That’s nice,” I hear you say, “but what are they?” That’s a good question. I’ll try to explain in this tutorial. This tutorial is the first of a couple that will show you the kinds of things you’ll be able to do with pointers.
Standard varables
Up until now, ROBOTC only supported normal variables; in other words, a variable, be it an int, float or anything else, only had a value, like 712, 0.383 or “howdy”. For example:
long foo = 76278; float baz = 2.9121; string greet= “hello”;
In effect, when you declare a variable, the compiler puts aside an appropriately sized amount of memory and gives it a symbolic name, like i or f or baz. An int is 4 bytes large, a float is also 4 bytes but a string (in the case of ROBOTC) is usually 20 bytes large. Take a look at the drawing below. You see the memory blocks, the labels assigned to them, their contents and the addresses (the hex numbers under the blocks).
Dissecting a variable
A variable has two parts, an rvalue and an lvalue. As you can guess, the rvalue is the part of the right side of the assignment and lvalue is the part on the left side. Now consider this bit of code:
// ptr tutorial example 1 task main() { int i, j; i = 10; j = 51; // This should print out // i: 10, j: 51 writeDebugStreamLine("i: %d, j: %d", i, j); // Assign i's rvalue to j j = i; // This should print out // i: 10, j: 10 writeDebugStreamLine("i: %d, j: %d", i, j); i++; // This should print out // i: 11, j: 10 writeDebugStreamLine("i: %d, j: %d", i, j); }
The output of this program should look like this:
i: 10, j: 51 i: 10, j: 10 i: 11, j: 10
When looking at the memory locations and their contents after each operation, it would look something like this:
On the first assignment, i’s rvalue is given the value 10, on the second assignment, j’s rvalue is given the value 51. However, what happens on the line “j = i”? Simple, the rvalue of i is copied and assigned to j’s rvalue. Now both j and i’s rvalues are 10. What happens when we increment i? Does it change j’s rvalue? In short, no, the two rvalues are completely separate entities.
Getting to the point(er)
As I mentioned earlier, when a variable is declared, the compiler assigns an appropriately sized chunk of memory to it. This chunk has an address, a number, much like a house. Should you want to access this address in ROBOTC, you can do so with the reference (&) operator. This isn’t very useful in itself, though. Fear not, they didn’t add this ability to get the address without being able to doing something practical with it as well. Enter the pointer (*) operator.
Previously, we saw that the rvalue of a variable contains the actual value that we assigned. With a pointer, the rvalue is in fact the address of the variable we’re pointing at. But what if we want to see the value of this variable we’re pointing at? We can do that with the dereference operator, also a “*”. Combining all this new-found knowledge, we get the following:
// ptr tutorial example 2 task main() { int i = 10; int *iPtr; iPtr = &i; // iPtr now points at the address of i // This should print out // i: 10, iPtr: 656F6968, *iPtr: 10 // (value for iPtr may vary) writeDebugStreamLine("i: %d, iPtr: %p, *iPtr: %d", i, iPtr, *iPtr); i++; // This should print out // i: 11, iPtr: 656F6968, *iPtr: 11 // (value for iPtr may vary) writeDebugStreamLine("i: %d, iPtr: %p, *iPtr: %d", i, iPtr, *iPtr); }
The output should look like this:
i: 10, iPtr: 656F6968, *iPtr: 10 i: 11, iPtr: 656F6968, *iPtr: 11
If you look at it from a memory perspective, it looks like this:
Pointer arithmetic
Now that you know how what pointers are, let’s have some fun with them. Consider the code below:
// ptr tutorial example 3 task main() { ubyte arr[3] = {1, 2, 3}; ubyte *arrPtr; arrPtr = &arr[0]; // arrPtr now points to the address of arr[0] // This should print out // arr[0]: 1, arrPtr: 656F6968, *arrPtr: 1 // (value for arrPtr may vary) writeDebugStreamLine("arr[0]: %d, arrPtr: %p, *arrPtr: %d", arr[0], arrPtr, *arrPtr); arrPtr++; // we're now pointing at arr[1] // This should print out // arr[1]: 2, arrPtr: 656F6969, *arrPtr: 2 // (value for arrPtr may vary) writeDebugStreamLine("arr[1]: %d, arrPtr: %p, *arrPtr: %d", arr[1], arrPtr, *arrPtr); }
That should print out something like this:
arr[0]: 1, arrPtr: 656F6968, *arrPtr: 1 arr[1]: 2, arrPtr: 656F6969, *arrPtr: 2
Please note that the address arrPtr may be different in your case. It is important that on the second line, the address arrPtr points to is one higher than in the first one.
When you look at what it looks like in memory, you can get a good idea of what’s going on:
Interesting fact about pointers
When you use sizeof() on a normal variable, like a ubyte, int or long, you get the number of bytes this type takes up. In the case of a ubyte, that’s one byte, int takes up two and a long uses four. Doing a “sizeof” of a pointer is, well, pointless. You won’t be getting the size of the item you’re pointing at, it’s always 4 in the case of ROBOTC. That’s because the address stored in the pointer is a 4 byte number, a long. Keep that in mind when you want to use sizeof() to get the number of bytes you want to wipe with a memset(), for example.
About time! I know a lot of people who will be happy to hear this!
Wait until you see the next couple of instalments 🙂
Pointer are the source for many programming errors. That they added pointers is really a burden rather than a blessing. Especially for beginners.
As far as I can see, RobotC supported call-by-reference already. That should be sufficient. Honestly, anything related to pointers is really just satisfying bad habit of programmers.
You don’t have to use them if you don’t want to. I find them tremendously useful, as you will see in the next instalment of the pointer tutorial.
[…] Note that the first argument of the function is a pointer to a tStack structure. Remember in the previous tutorial how we used pointers to allow us to modify a variable through another? This is the same thing, […]
[…] like queues, binary trees and other fun stuff. You can read about pointers in this tutorial: [LINK]. If you want to know more about stacks, you should check out part 2: […]