This is a quick review of pointers in C. If you are not an experting in pointers, I would strongly
urge you to read everything on this page carefully. All the words here are carefully chosen to be
as correct as possible. If there is anything you do not understand or wondering about the choice
of words, please feel free to send an e-mail to the instructor. (If you see a bug on this page, please
also let the instructor know.)
Just like integers and characters, a pointer is a primitive data type in C/C++.
An integer takes up 4 bytes, a character takes up 1 byte,
and a pointer takes up 4 bytes on a 32-bit machine (and 8 bytes on a 64-bit machine, but we will assume that
we are running on a 32-bit machine for the rest of this discussion).
A pointer is simply a 4-byte quantity that contains a memory address.
Here's an example of how you would declare an integer variable, a character variable, and a pointer variable in C:
In an assignment statement, the left-hand-side refers to a memory location that can store a value.
The right-hand-side is evaluated to obtain a value and this value is put into the memory location
referred by the left-hand-side.
In the above example, n refers to a memory location that can hold a 4-byte quantity.
(Please note that the word "refer" has nothing to do with C++'s "reference variable".)
By declaration, the compiler will interpret this memory location as containing an integer.
Similarly, c refers to a memory location that can hold one byte of data.
By declaration, the compiler will interpret this memory location as containing an character.
Finally, p refers to a memory location that can hold a 4-byte quantity.
Since it's declared (void*), the compiler just knows that it's a pointer, it can
point to anything, i.e., it can contain a memory address of any object. (I'm using the
word "object" to refer to anything in C. It can be an integer, a character, a data structure,
a pointer, or even a pointer to a pointer of a pointer of a pointer.)
n = 5 means to put an integer value of 5 into the memory location referred to by n.
c = 'z' means to put an encoded character 'z' into the memory location referred to by c.
p = NULL means to put a value of 0 into the memory location referred to by p
since NULL is simply defined as 0. We would call p a "NULL pointer" because it contains
memory address zero. It is known that there is nothing at memory address zero. It's perfectly okay to
point to it. But if you try to "see" what's in memory address zero (i.e., by dereferencing p),
you will get a segmentation fault and your program will be killed by the operating system.
Please note that we often depict pointers by drawing arrows from a pointer type to another object.
There are of course no arrows in memory. It would be more accurate to simply put a value of 0
into p in the above picture.
Continuing from the example above, let's see some assignments operations.
m = n means to evaluate the right-hand-side to get a numeric value and put the value into
the memory location that's referred to by m. If you see a variable on the
right-hand-side, you need to use the value at the memory location that's referred to by the variable.
Similarly, p2 = p means to evaluate the right-hand-side to get a numeric value and put the value into
the memory location that's referred to by p2. Since p is a variable on the right-hand-side,
if you use the value at the memory location that's referred to by p, you would get 0.
Therefore, you put 0 into p2. As a result, p2 will contain the same value as p
(i.e., p2 will end up pointing to the same object as p).
On a 32-bit machine, the size of the address space (supposedly addressable memory locations) of a program is 232 bytes.
An address is a number that can be used to refer to a memory location in the address space of a program.
Therefore, an address is 4 bytes long on a 32-bit machine.
The data type of a variable's address is a pointer data type. All pointer data types are compatible.
Since a variable refers to a memory location, you can actually get the memory location to which it refers:
&n denotes the address of n.
Therefore, p3 = &n means to put the address of n into
the memory location that's referred to by p3.
The same address can also be put into the memory location that's referred to by p4 (pointer of a different type)
since all pointer data types are compatible.
Please note that it's perfectly valid to do:
int *p5 = (int*)0x12345678;
because it is possible to use 0x12345678 to refer to a memory location in the address space of a program.
But if you try to dereference p5 and use what's in memory location 0x12345678,
you need to make sure there's really something at location 0x12345678
or you will get a segmentation fault and your program will be killed by the operating system.
To dereference a pointer means to follow the pointer to where it points to and fetch what's there.
First, n set to contain 23.
m = *p4 means to evaluate the right-hand-side and put the value in m.
If we have just p4 on the right-hand-side, the right-hand-side would evaluate to what's in p4.
But we have *p4 instead (where * is the dereference operator). So, we need to follow the pointer to where it points to and fetch what's there.
p4 points to n; therefore, *p4 evaluates to what was in n and you end up putting
23 into m.
Next, *p4 = 876 means to put a numeric value of 876 into the memory location referred to by *p4.
Since p4 is a pointer, to get the memory location that's referred to by *p4,
you dereference the p4 pointer by following the address stored at p4. As it turns
out, you get the memory location referred to by n. So you end up putting 876 into n.
An array variable has the same type as a pointer. For example, if you have:
then a and p5 refers to the same memory location,
a and p5 refers to the same memory location,
and a and p5 refers to the same memory location.
You need to be careful with pointer math. Although p5 evaluates to the address
of variable a, the expression p5+1 will be calculated as p5+sizeof(int)
(which is p5+4, which is the same as &a) because p5 is declared as a pointer to int.
Similarly, since p6 is declared as a pointer to short,
the expression p6+1 will be calculated as p6+sizeof(short),
which is p6+2, which would not correspond to any element of the a array.
Here's one weird thing about C. If a is an array, then a refers to the address of that array.
Then what is &a?
As it turns out, in C, if a is an array, &a is treated the same as a.
Although an array has the same type as a pointer, this is how an array is different from a pointer.
In C++, there are "call by value", "call by pointer", and "call by reference" and it's all very confusing.
In C, there is only one way to pass parameters in making a function call and that's using "call by value".
This means that all function parameters are copied. For example, if you have:
void foo(a, b, c)
and you call foo() by doing:
foo(x, y, z)
effectively, it's like performing assignment operations
(although in reality, it's a lot more complicated as you will learn in Ch 3 of the textbook):
a = x;
b = y;
c = z;
no matter what the types of a, b, c, x, y, and z are.