Pointers In C David Ljung Madison Definitions ----------- &x The address of x *x (in an expr) What is pointed at by x int *x; (Variable declaration) x is a pointer to an integer *x.f1 What is pointed at by field f1 of x (the field is a pointer) (*x).f1 The value in field f1 of the structure that is pointed at by x x->f1 The same as (*x).f1, x is a pointer to a structure with field f1 Memory Diagram -------------- Example of memory with i=7, *x=103, y=&x (choices of the locations of the variables are arbitrary: i is at 108, x is at 105, y is at 101) ______ 100 |______| y-> |_105__| ------. |______| | |______| <-. | |______| | | x-> |_103__| -' <-' |______| |______| 108 i-> |__7___| Call by value ------------- ~~ foobar(int x) { x+=2; /* same as x=x+2; */ printf("foobar x: %d\n",x); } main() { int i; i=7; foobar(i); printf("main i: %d\n",i); } ~~ This code will output: foobar x: 9 main i: 7 This is because although x is changed in foobar, we only passed the *value* of x to foobar (the value 7). When we come back to main, i hasn't changed, foobar doesn't even know where i is kept. Call by reference ----------------- ~~ foobar(int *x) { /* x is a pointer to an integer */ *x=*x+2; /* or, like above: *x+=2; */ printf("foobar x: %d\n",x); } main() { int i; i=7; foobar(&i); /* we need to send it an address since the param is a pointer */ printf("main i: %d\n",i); } ~~ This code will output: foobar x: 9 main i: 9 Now the i has changed! This is because we sent it the address of i, where we are actually storing the number 7. We add 2 to that, and we have changed the value that i points to. Pointer Arithmetic ------------------ int i; int *p; Just as you can add to the integer i, you can add to p, but it has very different meanings. *p+2 /* The value (integer) at p plus two */ p+2 /* The address p plus two */ Pointer arithmetic is almost always used for arrays. In fact, arrays are just a macro for pointer arithmetic. int p[50]; /* an array of 50 elements */ p[2] /* The third element of the p array (we start at 0! :) */ p+2 /* The third element of the p array */ These are the same, because the declaration of p merely has the compiler point p at the first address (p[0]) and then sets aside space for the next 49 spaces. p[2] is then just two addresses after p. The compiler is smart enough to handle different size types in pointer arithmetic. For example: google p[10]; /* a google is 10 bytes in size */ p+2 /* This will be the address of p plus 10 bytes * 2 */ So if you print out the address, don't be surprised when it is +20, not +2 :) Allocation ---------- int x; /* allocates space for a value which it refers to as x */ int *p; /* allocates space for the address to point to an integer */ *p=2; /* This is a bug! p doesn't point to any allocated space yet! */ A pointer needs to be either set to some space or must be given some allocated space before it can be used. In the above problem, our p doesn't have an address of space to store the number 2 at. In fact, most C compilers will initialize the address p to 0, and we probably don't want to store the number 2 at 0, since anything could be there. One way to solve this is: p=&x; /* Now p points to the space allocated for x */ *p=2; /* both x and *p are 2 (they are the same spot) */ x=4; /* both x and *p are 4 now. */ Another way to handle this is using malloc and free. Here is an example, but the reader should learn more about malloc and free from references outside of this paper. p= (int *) malloc(sizeof(int)); This will try to get some space from available memory for an integer, and if successful, it will point p at that space. (For example, it gets the space for an integer at address 151. malloc takes this space and gives us the address 151, which we set to p) Realize that malloc can fail, it will return NULL (just 0) if it fails, always check for this, it means you are out of memory! The (int *) is a casting. malloc normally turns the void type, but p is a pointer to an int. So you cast the return value of malloc as a pointer to an int, and then you can set p equal to it. Now we can do more things with pointers: int *p1,*p2,x; x=7; p1=&x; /* p1 points to x (the value that is in x) */ *p1=3; p2=(int *) malloc(sizeof(int)); /* we should check this for failure... :) */ *p2=200; /* put 200 at the allocated space */ p1=p2; /* p1 now has the same address as p2, they both point to the 200 */ p2=&x; /* now p2 points to what is at x */ At this point in the code, x=3, *p1=200, *p2=3 (figure out why :) The only reason we could change what p2 points to after allocating memory for it, is because we started pointing p1 at the allocated space first. Otherwise we would have lost the ability to point to the allocated space for good, but we wouldn't be able to use that space anymore. If you want to get space back to open memory, you free it: free(p1); This frees up the allocated memory pointed to by p1. If we hadn't set p1=p2; before changing p2, we would never have been able to do this. It is very important to make sure you free up memory before you lose all references to it. Structs ------- Pointers to Structs ------------------- The common example of structs with pointers is the linked list. Here is a simple example of a struct with a pointer to its own type: typedef struct lst { int num; /* the 'num' field. Keep a number here! ;) */ struct lst *next; /* a pointer to this type of structure */ } lst; /* call this struct 'lst' as well as 'struct lst' */ We needed to use 'struct lst' to declare the next field instead of just 'lst' because the compiler doesn't know we can call the structure with just 'lst' until the last line of the structure. Code Example: lst *p1,*p2; p1=(lst *) malloc(sizeof(lst)); /* allocate a struct lst for p1 */ if(!p1) { /* ack! malloc failed, we got back NULL! */ printf("Out of memory!\n"); exit(-1); /* kill the program! */ } p1->num=7; /* set the num field of the just allocated structure to 7 */ (*p1).num=7; /* same thing */ p2=(lst *) malloc(sizeof(lst)); /* we should check this for NULL as well */ p2->num=23; p1->next=p2; /* the next field of p1 is the address of p2 */ We now have: p1 p2 | | v v ___________ ___________ | num: 7 | | num: 23 | | next: --------->| next: ? | |_________| |_________| We could change the num field in p2 a few ways now, such as: p2->num=6; p1->next->num=6; /* because p1->next is the same as p2 */ Now lets add another element. We can use p2, because we are still pointing at the second struct through p1->next. This code should look familiar :) p2=(lst *) malloc(sizeof(lst)); /* we should check this for NULL as well */ p2->num=5; p1->next->next=p2; p1 p2 | | v v ___________ ___________ ___________ | num: 7 | | num: 6 | | num: 5 | | next: --------->| next: --------->| next: ? | |_________| |_________| |_________| Now, to avoid the next->next, and a next->next->next, and so on, requires careful linked list programming. You should be able to figure out how to do this without too many variables, or else you teacher probably has some good examples. A common example to add to the front of a list is: lst *head; /* assume a list is already pointed at by head */ lst *tmp; tmp=(lst *) malloc(sizeof(lst)); tmp=head->next; head=tmp; Figure out why this works :) Enjoy! Dave Ljung