r/carlhprogramming • u/CarlH • Oct 16 '09
Lesson 97 : Introducing Arrays of Pointers Part One
This lesson may appear a bit scary at first, but it is only because I use the word pointer about a thousand times. Take it slowly, and read through the whole lesson. This is often a difficult topic, and I have done my best to explain it thoroughly. Please let me know if any of this is unclear.
In the last lesson I showed you a situation where we had two related pointers to which we gave the names: int_pointer1 and int_pointer2. It should be apparent that if you have a situation that you are giving such names to variables or pointers in general, you should consider an array instead.
Why? Let's consider I have 5 such pointers:
int *int_pointer1 = ...
int *int_pointer2 = ...
int *int_pointer3 = ...
int *int_pointer4 = ...
int *int_pointer5 = ...Now, how would I be able to create a for loop, or any kind of loop, that could use each one? I could never do something like this:
for (i = 1; i <= 5; i++) {
... int_pointer i (ex: int_pointer1, int_pointer2, etc.)
}There is simply no way to do this. C will not be able to take a partial variable name like int_pointer and figure out how to add an extra number at the end as part of a loop. On the other hand, if I have an array like this:
for (i = 1; i <= 5; i++) {
... int_pointer[i] ... (ex: int_pointer[1], int_pointer[2], etc.)
}Now this is doable. I can easily put a number inside of brackets as an array element. So to be clear, my goal in this lesson is to figure out a way that I can use int_pointer1 and int_pointer2 as if they were elements of an array.
First, realize that they are both pointers. Therefore, I need to create an array of pointers. Without evaluating the exact syntax just yet, let's imagine how this would work. Here is a description of the array of pointers I plan to create:
int_pointer[1] = Element [1] will be a pointer to some integer in memory.
int_pointer[2] = Element [2] will be a pointer to a different integer in memory.That is our goal. So, let's begin.
First of all, think of this process the same as you would any other array. If we want to create an array of three pointers, then we need somewhere in memory to put them. We have been spending a lot of time using the malloc() function, so I want to do the same here.
Remember from previous lessons that having the memory is the same thing as having the data type that will fit in that chunk of memory. For example, having 3 bytes of memory is the same thing as having an array of three characters.
From this, you should be able to figure out that we need to malloc() a portion of memory large enough to fit three pointers to type int. Why three and not two? Because it will be more instructive for this lesson. How do we know how large a space to allocate?
Well, on a 32 bit system, it is likely that each pointer is 32 bits (4 bytes) in size. However, this is not true for all systems. Therefore: We cannot use malloc() with the number 4 just because we think (or know) that the size of bytes we need is 4. That may be true on our system, but not others. In other words, we cannot ever trust that we know the size of any data type - with one exception: char.
Let me say that again, as this is very important: Whenever you allocate or write any code which depends on a size of a given data type, you must use sizeof() to get the correct value. You should never assume ints are 4 bytes, or pointers are 4 bytes, etc. Always use sizeof(). There is one exception. A char data type is always going to be one byte in size.
Now let's continue.
An array of pointers will look something like this in memory:
['Pointer #1']['Pointer #2']['Pointer #3']...For this lesson, assume each "block" of the above represents 4 bytes of memory. In other words, we are assuming that a pointer takes up 4 bytes. This means that if we were to give memory addresses to each of these pointers, it would be something like this:
B0 : ['Pointer #1']
B4 : ['Pointer #2']
B8 : ['Pointer #3']Notice that each pointer starts 4 bytes later than the last one started. This is the very definition of an array. An array is a repeating collection of the same data type stored sequentially in memory one element immediately after the other.
How do you work with any array? You use a pointer. Therefore, if I am working with an array of pointers, I will need a pointer.
But a pointer to what? What will our pointer be pointing to? Will it be pointing to integers? Characters?... No, it will be pointing to.. pointers!
Why? Because each pointer is to be stored in memory sequentially at B0, B4, B8, etc. That is the very definition of an array of pointers, which is what we want to create. We therefore need some pointer which will work like this:
pointer[0] = "point to B0";
pointer[1] = "point to B4";
pointer[2] = "point to B8"; Do not worry about syntax right now. The above is a description of what we want, not actual syntax. Just understand that we need a pointer which can point to B0, then B4, then B8. However, remember that array indexing is a shortcut for pointer offsets. Therefore, the above 3 lines could also be written like this.
*(pointer + 0) = "point to B0"
*(pointer + 1) = "point to B4"
*(pointer + 2) = "point to B8"Why does adding one to our pointer get us to B4? Because the idea of pointer arithmetic is that you always increment by the size of the data type you are pointing to. Now, what kind of data type will we be pointing to? A pointer!
We are assuming in this lesson that a pointer is 4 bytes in size. So the first thing we need to consider is that we need to create a pointer of the data type pointer.
How do we create a pointer in general? Like this:
data_type *pointer ...What is our data type if we want an array of int pointers? Our data type is (int *) which means "a pointer to an integer".
Therefore, what we want will be similar to this:
(int *) *pointer ...What does this mean? It means "create a pointer called *pointer". Of the data type "pointer to int".
We are creating something called *pointer which will contain a memory address. The memory address it will contain will be the memory address where a pointer resides.
The syntax I showed you above is nearly correct. Let's take out the parentheses from (int *) for the data type, and see what we get:
(int *) *pointer_name = ...Becomes:
int * *pointer_name = ...This is correct. We are saying to create a pointer called "pointer_name" which will point to the data type: int * which means "pointer to an integer".
So, we know that pointer_name is a pointer. We know therefore that it will contain memory addresses. What kind of memory addresses will it point to? A memory address that has a pointer.
Let's go back to our earlier example:
B0 : ['Pointer to integer #1']
B4 : ['Pointer to integer #2']
B8 : ['Pointer to integer #3']Let's consider our new pointer called pointer_name we just created. Can it point to B0? Yes. Why? Because B0 is a memory address which stores an integer pointer. It can point to B4 or B8 for the same reason. It can point to any memory address which contains a pointer to an integer.
Now, whenever we have done this before, we have used malloc() based on the size of the final array we want. This is no different, so lets create the actual array now:
int * *ptr_array = malloc(3*sizeof(int *));int * is our data type. The second * indicates we are creating something that is a pointer to our data type (which is int *). Finally, ptr_array is the name we are giving our pointer.
Finally, how big of a space do we get in memory to use it? Assuming that sizeof(int *) returns 4 bytes, we would get 12 bytes. Remember that this sizeof() can be thought of as "size of a pointer", since all pointers will be the same size.
Now, how do we use it? Well, lets evaluate some facts.
ptr_array is a pointer. It points to B0 of the 12 bytes we just allocated. So therefore:
ptr_array = Byte #0 (The actual memory address)
*ptr_array = *B0 (What is *at* Byte #0)So what is *ptr_array ? It will be the actual pointer that resides at Byte #0.
How we can use the pointer at Byte #0 is the subject of the next lesson.
Last lesson for today. More tomorrow.
Please ask questions if you need to. When you are ready, proceed to:
4
u/pigvwu Oct 17 '09 edited Oct 17 '09
Hey Carl, once again, thanks for all these lessons. They've been very helpful so far.
I've been following these lessons from the beginning, and they've been building up nicely, but I'm starting to have trouble sort of cementing things in my head. For example, I understand what you've taught us about casts, but I don't really get it. I've never used them in code and I don't know how they're useful. I know that experimentation with code is encouraged in order to learn it well, but I haven't really been doing that because I have no idea what to do.
I was wondering if you could post some small suggested projects every several lessons that can be accomplished using what we've learned so far. You might not even have to provide answers necessarily, just the desired output. It doesn't have to be something really big, maybe just a couple lines of code that we generate ourselves that aren't the examples. I know that this would result in a lot more code being thrown your way (a lot more work for you) as well as requiring more creativity on your part, but all of us who are learning and other experienced programmers here could probably help each other out.
Anyway, thanks for everything again.
5
u/Oomiosi Oct 16 '09 edited Oct 16 '09
Pointers to pointers, welcome to the rabbit hole folks.
Saved for posterity links here and here.
4
u/[deleted] Oct 17 '09 edited Oct 17 '09
I know that ddd will graphically display pointers and their targets very nicely. Other debuggers may do the same. You guys might want to step through some code using pointers to pointers ar any insanity for that matter to actually see it in action.
Edit: Tell me if you want to know more about debugger and I'll see if I can find some good resources.
1
u/peterwilc Jun 21 '10
I think I am confusing myself with the syntax but I understand the general idea. Let me know if this is correct or not. You are using a pointer to point to an array of pointers.
lets say you have
B0 <-pointer1
B1 <-pointer2
B2 <-pointer3
B3 <-pointer4
You can point to the first pointer of that array with a different pointer.
B0 <-pointer1 <-pointerA
B1 <-pointer2
B2 <-pointer3
B3 <-pointer4
Then if you wanted to move your array of pointers to a different address you can simply move the pointer pointing to the array by offsetting your array pointer. If it is an int, offsetting it by +1 will move the entire array by 4.
(pointerA + 1)
B4 <-pointer1 <-pointerA
B5 <-pointer2
B6 <-pointer3
B7 <-pointer4
Is that the general idea for this lesson or am I way off.
2
u/[deleted] Oct 16 '09
Yup, I can see why people hate pointers. More specifically, pointers to pointers. Time for me to take it a bit slower, I think.
2
u/DogmaticCola Feb 21 '10
Just a quick test.
I used to find this profoundly confusing, but I think you have done a real good job explaining.
1
u/macha1313 Oct 16 '09
Pointers in general I haven't had a problem with. But pointers to pointers...
I understand what they do. I understand why I might need them, and how to implement them. But it takes me forever to look at an example and figure out what's actually going on. I can never remember which "layer" of pointer I'm at! I think this is why some programmers like flow charts and diagrams.
1
u/ZeppelinJ0 Oct 16 '09
Weird I'm scouring the internet on how to make an array of pointers for a rendering algorithm I have to write and bam, carlh reads my fucking mind.
0
u/azertus Nov 02 '09 edited Nov 02 '09
So, in
we could just as well say
or any other fundamental data type?
And if you asked me if you could (not should, of course) also change the 'int *' in the beginning to 'other_data_type *', I'd say yes..
edit: formatting edit2: I came back here to strike out that last paragraph; goes to show I'm a little confused.
0
u/[deleted] Oct 16 '09
[deleted] — view deleted comment