midterm on thursday. there's a practice midterm and solutions on my website. i think it would be a good idea try doing lab 5 before the midterm, since the material in the lab will be on the midterm. if you find a way to do the lab without using pointers, make sure you get a tutor or ta to show to you how to do it with pointers.
pointers are a difficult concept. honestly, it probably took me about a year to fully understand them myself, so don't feel bad if you're having a hard time. i will try to explain pointers using a different approach from the one used in lecture... it's not better than the approach in lecture, it's just another way to think about pointers. it doesn't matter how you think about pointers, just make sure you have a basic understanding of pointers.
here we go. we can think of variables as labeled boxes that hold information. so, if i say:
int x; int y;we can think of this as creating two boxes, labeled x and y, like this:
the boxes contain question marks because i didn't specify an initial value for the variables, so we don't know what the values of the variables are.
with this model, i can do normal stuff like:
x=2; y=x+3;
this will put a 2 into the box labeled x, and then it will grab the 2 from the box labeled x and add it to 3, which makes 5, then it will put the 5 into the box labeled y:
pretty straightforward, i hope. now let's introduce pointers into this picture. suppose i say:
int* p1; int* p2;
the * right after the word int indicates
that these variables are pointers to integers.
a pointer is just like any other variable [we represent it as a box with a label], but instead of containing a number or a character, we think of them as containing arrows that point to other boxes.
since i didn't specify initial values for the two pointers, the computer will pick something random for them to point to. the picture looks like this:
the two arrows are pointing in weird directions to show that we don't know what they're pointing at. let's make the pointers point at something. suppose we say:
p1 = &x; p2 = p1;
the first line says that i want p1 to point at x, and the second line says that i want p2 to point to the same thing that p1 points to. so, the picture looks like this:
note that the arrows point to the boxes, not to the values in the boxes. pointers point to other variables, not the values in the variables. let's try using our pointers. suppose i say:
*p1=3;
the * operator means "follow the arrow" [it's the "magic
wand of dereferencing", if you prefer]. so in our case, we need to
follow the arrow that starts in p1, and put a 3 in whatever the arrow
points to. so, we put a 3 in the box labeled x, because that's what
the arrow in p1 points to. so the picture now looks like this:
don't forget that variables can only have one value at a time. so when we put the 3 into the box labeled x, it replaces the 2 that was there before. now suppose i say:
*p2=4;
this time, we follow the arrow that starts in p2, and put a 4 in the box that p2 points to, like this:
let's try something more complicated:
*p2 = *p2 + *p1;
to process this assignment statement, we need to first figure out what
*p2 + *p1 is. *p2 says to follow the arrow
that starts in p2, and look at what's in the box it points to. p2
points to the box labeled x, which contains 4, so *p2 is
4. next, we need to figure out what *p1 is. again, we
look at the arrow that starts in p1, and look at the contents of the
box it points to. p1 also points to the box labeled x, so we get 4
again. next, we need to add these two 4's together, and we get
8. finally, the left side of the assignment statement says that we
need to put this 8 into the box pointed to by p2. so the picture looks
like this:
we can make our pointers point to something else, if we want. we can say:
p1 = &y;this says that we want p1 to point at y. so the picture looks like this:
now, if we say:
*p1 = *p2 + y
this says, take whatever's in the box pointed to by p2, and add that number to what's in the box labeled y. finally, take the result of the addition, and put it into the box pointed to by p1. p2 points to the box labeled x, which contains 8, and y contains 5, so adding these two together, we get 13. we need to put this 13 into the box pointed by p1, which is the box labeled y, so the picture looks like this:
now suppose i want to print out a bunch of stuff, like this:
printf("%d %d %d %d\n", x, y, *p1, *p2);
getting the values of x and y are pretty easy, they're 8 and 13, according to our diagram. getting the values of *p1 and *p2 is a little trickier, because we need to follow the arrows. by following the arrows, we see that p1 points to the box labeled y, which contains 13, and that p2 points to the box labeled x, which contains 8. so, *p1 is 13, and *p2 is 8.
so, the printf will display 8 13 13 8.
when you call a function in c, the function receives copies of the arguments. this means that if your function modifies its arguments, the changes will not be visible to the caller of the function. for example:
#include <stdio.h>
void increment(int x);
void main()
{
int x=2;
increment(x);
printf("%d\n", x);
}
void increment(int x)
{
x++;
}
when we run this, the computer will print 2, because the increment function receives a copy of the value of x, and modifies the copy. when we return to main, the printf displays the original value.
pointers give us a way to get around this. for example:
#include <stdio.h>
void increment(int* x);
void main()
{
int x=2;
increment(&x);
printf("%d\n", x);
}
void increment(int* x)
{
(*x)++;
}
let's see how this works. when we process int x=2, we get
a picture that looks like this:
we draw a big box around everything, and label the box "main", to
indicate that this variable can only be used directly by main. don't
forget that functions can't directly access each other's
variables. so, all the x's in main refer to the int x=2
in main, and the x's in increment refer to the int* x in
increment. .
when we call increment(&x), another big box is created,
but we label this one "increment", to indicate that the variables in
the box can only be used directly by increment. there is one variable
in the increment box: the int* x that it accepts as an
argument. when we say increment(&x) in main, we are saying that
the actual value of increment's x is the address of main's x. in other
words, increment's x points at main's x. so the picture looks like
this:
now we need to run the code in increment. it says (*x)++,
which means follow the arrow that starts in x, and increase the value
of the variable it points to by 1. since we are running code in
increment, we need to look inside our big box labeled "increment" for
a pointer called x. so, after we're done, it looks like this:
we're now done running the code in increment, so we return to main, and we do the printf, which prints the value of x. since we're running code in main, we look inside our big box labeled "main" for an integer variable called x, and we print the value in that variable, which is 3.
so as you can see, pointers let us get around call by value. when you use pointers to enable a function to change variables outside of its big box [or "scope", if you prefer], it's called "call by reference".
this explains why we need to put the & in front of
variables we pass to scanf, if you think about it [scanf is just
another c function...].
let's take a look at the pointer question from last quarter's midterm. what is the output of this program?
#include <stdio.h>
void cool(int *);
void main()
{
int a = 7;
cool( &a );
printf("%d", a);
}
void cool(int *x)
{
int a = 0, b = 0;
a = *x + 5;
b = *x;
*x = a * b;
}
each of the following two bits of code have a significant pointer-related problem. identify the problems.
/* part 1 */ int* p; *p = 10; /* part 2 */ int x=1; int y=2; int* p; p=x; *p = 10;
fill in the body of the function get_integer below, so that the program works properly:
#include <stdio.h>
void get_integer(int* x);
void main()
{
int x;
printf("please enter an integer:\n");
get_integer(&x);
printf("you entered %d\n", x);
}
void get_integer(int* x)
{
/* fill this in */
}