Lecture #22: More Pointers

November 24, 1999

Bob Pitha

bpitha@cs.ucsd.edu

http://www-cse.ucsd.edu/classes/fa99/cse5a

remember…

•      Pointers give us a way of referring directly to a location in memory

•      We can declare a pointer variable:

            int *p;

–   but it doesn’t actually “point” to anything until we set its value to a real address

•      We can find the address of a variable

–   using the & operator

            int i;

            int *p = &i;

remember…

•      We can operate on what a pointer points to

–   using the * operator

            int i;

            int *p = &i;

            *p = 3;

–   this sets the value of i to 3

NULL

•      There is a special value for pointers, named NULL

–   this is the canonical “invalid” value

•   it’s never a valid address

•   it can be used to indicate when a pointer variable is not set to an address

•   it’s useful for initializing pointer variables before you’ve actually set their value

•   it’s used in a lot of other situations too, we’ll encounter some

–   don’t confuse this with the nul character, which is the name for ASCII code 0

•   also ‘\0’

pointers and function arguments

•      When I introduced functions, I said that functions can have any number of inputs (arguments), and a single output (return value)

–   I lied. Well, sort of…

how did I lie?

•      In C, arguments are passed by value

–   this means that when you specify an expression as an argument to a function call, the value of that expression is copied into the parameter in the function

•   remember, a variable name is a simple expression

–   so when we change the value of a function parameter, we’re changing a copy, and the change doesn’t affect the variable used to call the function

example

•      Changes to parameters don’t “pass back”

but…

•      What if the variable we pass into a function is a pointer?

–   we still can’t change the parameter inside the function and expect the change to affect anything outside the function

–   but we can change the thing the pointer points to

•   and then when we return from the function, we can find the change at the same location

example

example – what’s going on here?

example – what’s going on here?

example – what’s going on here?

example – what’s going on here?

example – what’s going on here?

example – what’s going on here?

example – what’s going on here?

example – what’s going on here?

example – what’s going on here?

so what does all that mean?

•      You can “return” more values from a function than just the return value

–   by passing in pointers to other variables

–   the function can then use the pointer to change the value of the variable

scope

•      This circumvents the “scope” rules we talked about earlier

–   remember that a variable’s scope determines where it can be seen from within the program

–   so in our example, i and j were not in the scope of the function swap(), but we changed their values anyway

extent

•      There is another variable concept called extent, that we haven’t really talked about

•      Extent determines how long the variable exists while the program is running

–   scope is a static property: it applies to the code

–   extent is a dynamic property: it applies to the running program

•      Extent and scope are often mixed together, but they’re really separate concepts

extent

•      Let’s look at our swap function again

Text Box: void swap (int *a, int *b)
{
    int tmp;
    tmp = *a;
    *a = *b;
    *b = tmp;
}
 


–   notice that the variable tmp is declared at the beginning of this function

•   thus, its scope is the entire body of the function

•   and the scope rules say that we can only use the name tmp within the function

scope vs. extent

•      But we just saw an example where we manipulated variable values from outside their scope

–   so in that case, the variables still existed somewhere in memory

–   scope says whether we can use the name, not whether the variable will exist or not

variable lifetime

•      But each variable has a well-defined lifetime

–   at some point, the computer sets aside the memory for that variable

•   before that point, the variable doesn’t exist

–   at some later point, the compter frees the memory for that variable, so it can be used for something else

•   after that point, the variable doesn’t exist either

•   and if we still have a pointer to that memory, it’s no longer valid

•   to use that locker analogy, it would be like someone still having a slip of paper with my locker number on it, even though I’ve graduated and my locker’s been given to someone else

variable extent

•      The lifetime of the variable is its extent

–   most of the variables we’ve seen and used so far have automatic extent

•   that means they’re created and destroyed automatically, as needed

•   each type of scope has its own extent rules

•   and we can manually affect the extent of some variables

swap() again

Text Box: void swap (int *a, int *b)
{
    int tmp;
    tmp = *a;
    *a = *b;
    *b = tmp;
}
 


•      In swap(), we declare the variable tmp

–   and it’s a definition: it reserves some memory

–   but up to now, we haven’t looked at when that memory is reserved, and when it’s released.

extent of tmp

•      The extent of tmp in this case pretty much mirrors its scope

–   it’s created when we enter the function

–   it’s destroyed when we leave the function

–   that means that every time we call the function, the expression &tmp may have a different value

extent of i and j

•      Similarly, i and j exist from the beginning of the block in which they’re declared until we leave it

–   which is really from the beginning of the program until it ends

–   so they exist while swap() is being called

so what?

•      Why do we care about extent?

–   What if we did something like this?

Text Box: /*
 * power: calculate m to the nth power
 */
long *power (int m, int n)
{
    int i;
    int product=1;

    for (i = 1; i <= n; ++i)
        product *= m;
    return &product;
}
 


returning a pointer

Text Box: /*
 * power: calculate m to the nth power
 */
long *power (int m, int n)
{
    int i;
    int product=1;

    for (i = 1; i <= n; ++i)
        product *= m;
    return &product;
}
 


•      Now we’re returning a pointer

–   (don’t ask why; this is a contrived example)

–   but product’s extent ends at the return statement

–   so this function always returns an invalid pointer