84. Tables and pointers
I’d like to talk about something that puzzles many programmers who don’t understand what “pointers” are. But they aren’t so mysterious when you get to know them a little better.
Let’s start with something you already know. If I say
a=3 b=a b=b+7 print(a,b)
You would expect to print 3,10. This is because b is set equal to a, which is 3, so b becomes 3, and then we add 7 to get 10. But this doesn’t affect a, because b is a separate copy of a.
And we do see 3,10 on the printout, so that’s good.
Now how about this?
a={} a[1]=5 b=a b[1]=9 print(a[1],b[1])
We have a little table in a, and we set b equal to this table, and change the first value.
You might expect the printout to show 5,9, because b is again a copy of a.
But you’d be wrong. The printout shows 9,9. And if you experiment, you’ll find every change you make to b is also made to a. What’s happening? Is this some kind of quantum entanglement?
Let me use a couple of real world examples to try to explain.
You go into your local library and ask the librarian a question. She is about 102 and doesn’t want to leave her warm chair. So if you ask her a simple question, like how many books you can take, or how to spell Shakespeare, she can write those down for you on a piece of paper. If someone else comes along and asks the same questions, they’ll get their own separate copies of those answers.
But suppose you ask “Where is the thriller section?”. She’s just going to point to it, and away you go. Someone else comes and asks the same question, and she points again, and now there are two of you in the same thriller section. So the librarian told you where to find the answer, but didn’t make a copy of it for each of you. So if you take one of the thrillers, it’s not going to be there for the other person to take. You are sharing the same books.
Now the difference in the two cases is the information you were given. In the simple case, you were given the actual information, while in the second case, you were pointed to where it was.
Now let’s look at the programming situation. Things like tables are complex structures, and it is cumbersome to be passing them back and forwards in memory.
So what Lua (and most other languages) do when you set b=a, is this:
- if a is a single item, eg number or string, make a copy and put it in b
- if it is more complex, put a “pointer” to (ie address of) a in b, but don’t make a copy
.
Another example. I may have previously used the analogy that Lua stores values in lockers with labels (the variable names) on the front. But there is only room for one thing in the lockers. They can fit a number or a string like “ABC”. But a table is too big. So what Lua does is put a piece of paper in the locker that tells it where the table is kept.
So here is the code from above again, with comments to explain what is happening.
a={} --set up table somewhere, put pointer (address) in a a[1]=5 --use pointer to find the table, add first item b=a --put the same pointer in b b[1]=9 --use pointer to find the table, change first item print(a[1],b[1]) --use pointers to find the [same] table, print first item
So a pointer is literally an address that tells you where to find something, and that’s all. And it’s used for anything more complex than a number or a string, to tell you where something is kept. So if two variables are set equal to the same table, they will each be given the same pointer telling Lua where to find the table itself. And that’s why changing table b above makes exactly the same changes to table a.
Copying tables
But this leaves us with a problem. In the code above, suppose I want b to be a copy of a, not just the same table. How do I do it? Surely I don’t have to laboriously copy every item from a to b?
Yes, unfortunately, that’s exactly what you have to do. In the simple case where a is a list of items in sequence, you can write this
a={1,2,3,4,5,6,7} --put some items in a b={} --set up b, then copy items from a for i=1,#a do b[i]=a[i] end b[1]=9 print(a[1],b[1]) --prints 1,9
So now it does what we wanted in the first place.
Passing parameters to functions
Have a look at this code:
function setup() a1={1,2,3,4,5,6,7} --put some items in a a2=3 b1,b2=func(a1,a2) print(a1[1],b1[1],a2,b2) --prints 9,9,3,6 end function func(c1,c2) c1[1]=9 c2=6 return c1,c2 end
We set up a1 as a little array and a2 as a number, and pass them to a function “func” that makes a change to both of them. They are returned as b1 and b2 and printed out. The question is whether what was passed to func was a copy or the original.
Well, if you understood the explanation about pointers above, then this will make sense too. Lua treats functions the same as variables, ie if the parameter is a simple number or string, it will pass a copy, so any change doesn’t affect the original (a2 in our example). But if you pass something complex like a table (a1 in our example), it only passes a pointer (address) to the table, so any change you make is to the original table. The result, in our example, is that a1 is changed by the function (because there is only one table, shared by both), but a2 is not changed, because func was given a separate copy.
So when you use functions, and pass parameters to them, bear in mind that simple parameters will be copies, but tables will be the originals, not copies!
I hope I explained this so you understand it!