Pointers

You can think of memory inside the computer as arranged in a huge array of bytes, each with its own unique location or "address." Characters are stored in individual bytes; arrays of characters are stored beginning at a certain address and continuing in consecutive addresses. Integers (on e.g. runner's SPARC processors) are stored using four consecutive bytes starting at one address; this address is the address of the integer. Similarly, all variables of all types in C have their own address.

A pointer is the C representation of a machine address. Just as we can have variables and expressions that are values of floats, ints, chars, etc., we can have variables and expressions that are pointers to different data types. The "&" operator, which you can read as "address of," returns a pointer to the variable it precedes. When declaring a variable, parameter, or function, preceding it with a "*" makes it a pointer to the type you declare it as. In an expression, the "*" operator does the opposite of "&"; it precedes a pointer and returns what the pointer points to. This operator "dereferences" the pointer (pointers are also sometimes called "references"). Here is an example of all these concepts:

int main () {
	int	a, *p;		/* a is an int; p is a pointer to an int */

	/* set a to 20 and print it out */

	a = 20;
	printf ("%d\n", a);

	/* initially, p points nowhere; we can initialize it to point to a.
	 * this line means "put the address of a into p"
	 */

	p = &a;

	/* this line means "put a 10 in the int p points to" */

	*p = 10;

	/* since p points to a, *p is the same thing as a, 
	 * so 10 will be printed
	 */

	printf ("%d\n", a);
	exit (0);
}
A limited sort of arithmetic can be done on pointers (pointer arithmetic). If p is a pointer to some int in memory, then p + 1 is a pointer to the next int in memory, p + 2 to the next one after that, and so forth. Since arrays elements are laid out consecutively in memory, this means we can address elements of an array using pointer arithmetic. This function, for instance, prints out a string on standard output:
void print_string (char s[]) {
	char	*p;

	p = &s[0];
	while (*p != 0) {
		putchar (*p);
		p++;
	}
}
Remember how call-by-value works for everything except for arrays? It turns out this is because an array name as a parameter to a function (and in most expressions) is treated as a pointer to the first element of the array. So the char s[] parameter above is really just a pointer to the first element of the array. Thus we can rewrite the function:
void print_string (char *s) {
	char	*p;

	for (p=s; *p; p++) putchar (*p);
}
However, I encourage you to use array notation whenever possible; it greatly increases readability, especially by novices or by those used to notation from other programming languages. Also, in C, you can use array notation with pointers, so the function would also work like this:
void print_string (char *p) {
	int	i;

	for (i=0; p[i]; i++) putchar (p[i]);
}
or even
void print_string (char *p) {
	int	i;

	for (i=0; p[i]; i++) putchar (*(p+i));
}

Here are some important rules to remember when using pointers:

Call by Reference

Pointers can be used to simulate "call-by-reference" behavior, where variables modified in a function remain modified after the function exits. Note that the following function and function call won't work because of call by value:
void square (float f) {
	f = f * f;
}

int main () {
	float	d;

	d = 2;
	square (d);
	printf ("%f\n", d);
	exit (0);
}
The program will just print "2" since the change done in square is not kept in main. But we can change the functions to use pointers, sending the address of d instead of the value:
void square (float *f) {
	*f = *f * *f;
}

int main () {
	float	d;

	d = 2;
	square (&d);
	printf ("%f\n", d);
	exit (0);
}
Now the program prints "4" since d itself has been squared. This is why scanf requires & to be placed in front of variables it reads in, so it can change the variables through pointers.