Pointers
Unfortunately, many programmers find pointers to be a confusing concept, the most challenging part of learning C. The best advice I can offer to those who are confused by pointers is to learn assembly language. The details of addressing and dereferencing are not subtle points in assembly language, and once a programmer is accustomed to dealing with such issues, their experience in C will be much easier.
Memory
Before we get into pointers, let's take a step back and talk about how microprocessors and memory work. All traditional computers have the same basic design. At the heart of the computer is a microprocessor, also called a Central Processing Unit (CPU). The job of the CPU is to execute simple instructions one at a time. These instructions, and the data they operate on, are located in Random Access Memory (RAM).
RAM is a chip or a bank of chips that store on/off values in bits. A bit can store a 1 or a 0. Bits are organized into bytes. By combining all the possible values of the 8 bits, a byte can store a value between 0 and 255. A computer has many bytes of RAM (kilobytes, megabytes, or even gigabytes of RAM). In order to refer to a specific byte in RAM, they are numbered, starting with zero. This number reference is called an address.
The address of a particular byte of memory is used in the CPU's instructions to indicate which byte in memory should be acted on. Machine instructions do things like "read a byte from memory address 56", or "begin reading instructions starting at memory address 893." Often, the address is not encoded directly in the instructions, but is found elsewhere. For example, "write a byte to the memory address contained in register X." A program uses addresses to instruct the CPU to operate on specific addresses.
In C, most memory addresses are calculated for you, and you don't have to think about them. For example, most variables are stored in memory (register variables are not), and the location in memory where that variable is stored has an address. As the programmer, you just use the variable's name, and the C compiler automatically determine's the variable's address, which is then inserted into the machine code that it generates. You don't have to think about the fact that the variable named age is stored in memory location 4562 because the C compiler does this for you. This is one of the many conveniences of a high-level programming language like C.
Sometimes, however, you want to do something that is not as simple. You often need to do something that is more complicated than the C compiler can do for you. For example, what if you want to use a specific memory address, such as the computer's video framebuffer? What if you must allocate memory while the program is running (perhaps because you don't know how much memory you will need until then)? In these cases, the compiler can't do everything automatically, and you must give specific instructions about where to get a memory address.
So what is a pointer?
A pointer is really just a memory address. A pointer variable is a variable that holds an address, which can then be used to reference another variable or memory location.
Consider the following example:
main()
{
short x;
short *p;
p = &x; /* Make p point to x */
x = 1; /* Assign 1 to x */
printf("x is %d\n", *p);
x++; /* Increment x */
printf("now x is %d\n", *p);
}
Here I have defined two variables. x is a normal variable of type short. You have seen this kind of variable many times before. However, p is a special variable known as a pointer -- you can tell because of the *, which is the pointer operator. p is not an short, but rather it is a variable that holds an address, and the address is known to point to a variable of type short.
Declaring a Pointer Variable
Already, we have encountered one issue that many people find confusing: a pointer variable actually has two datatypes associated with it. The pointer variable itself stores a memory address. How big is a pointer variable? Well, that depends on the CPU, the operating system, and sometimes even the compiler. On a 32 bit operating system, a pointer variable is normally -- you guessed it -- 32 bits, or 4 bytes. However, since it is used to reference other variables, the C compiler needs to know what type of data it is pointing to.
In the example above, we have declared a variable called x, with a datatype of short (for me, a 2 byte, 16 bit variable). We have also declared a pointer variable called p. The C compiler knows it is a pointer because of the asterisk (*) preceding the variable's name.
The pointer variable p stores a 32 bit address, so it occupies 32 bits. But it is used to point to 16 bit variables of type short.
The "Address of" Operator
p isn't initialized, so before we can use it, we have to store something in it. What are we storing in it? An address. Since it is a pointer variable, the value stored in it is actually the address of something else.
So where do we get the address of something? Well, not so coincidentally, C has an operator for that: the unary & "address of" operator. We can use it to get the address of another variable. In our example above, we use to get the address of the x variable. The expression:
&x
evaluates to the address used to store the value contained in the variable named x. (The C compiler knows the difference between this and the bitwise AND operator because bitwise AND is a binary operator, used between two values.) So, if we can get the address of something, then we can store that address in a pointer value. Thus, the expression:
p = &x;
assigns the address of x into the pointer variable p. p can now be said to "point" to x.
By now, you have made use of the scanf() functions. At the time, you were told to use an & in front of arguments, but it was probably not explained to you. Well, now you know. This is a use of the address of operator. What you were actually supplying to scanf() was a pointer to the variable that you wanted scanf() to store values in.
Using a Pointer
Here is another issue that confuses new C programmers: in your program, sometimes you want to use the address stored in the pointer variable, other times, you want to use the value stored in the variable pointed at by the pointer variable. There are two different notations for this, and you must understand which to use at the appropriate time.
Most of the time, I will want to use the value stored in the memory location that the pointer variable points to:
*p = 3;
printf("x is %d\n", *p);
In this first statement, I am assigning a value to the variable pointed to by p (ie, I am assigning a value to x). In the second statement, I am passing the value stored in x (which p currently points to) to the printf() function. Contrast that to the following statements:
p = &x;
printf("The address of x is %d\n", p);
In the first statement, we are storing an address into the pointer variable. In the second statement, we are printing out the address stored in p, which happens to be the address of x. (The act of printing the address in a pointer variable is not exactly a good idea. I tend to debug programs that way occasionally, but otherwise memory locations aren't very useful. Not all platforms have 32 addresses that are the same size as integers (implied by %d). Some implementations of printf() have a special %P for printing out memory addresses, which tend to show 16 bit segment addresses and 16 bit memory addresses separated by a colon. The example above is only given to illustrate a point [no pun intended] about pointer variables.)
Pointers and Strings
You may not have realized it before now, but you have been using pointers when dealing with strings and other arrays. When you use the name of an array variable, you are actually referencing a pointer to the beginning of the array.
There are two ways a string (or any array) can be declared:
char yourstring[] = "This is also a string"; char *mystring = "This is a string";
In the first declaration, 22 bytes are set aside and the initializer value copied into it. In the second declaration, a pointer variable is created (typically 4 bytes), and an address is stored in it which points to the initializer string where it appears in the executable progam.
Using yourstring and mystring in a program both supply an address. mystring is a pointer variable, and can be made to point to something else later. yourstring has a fixed address (a constant, if you will), and only refers to that 22 character array in memory.
This is why you do not use & in front of strings when supplying them as arguments to the scanf() function. String (character array) variables already supply an address.
Interestingly enough, referencing the first element of an array (such as the first character in a string) can be done with the pointer operator:
*mystring = 'A';