Pointers 49
Frame Symbol Address Value
swap
s 105 83
m 104 101
k 103 100
Return Location 102 line 13
f
c 101 74
a 100 74
The fifth line assigns 83 to the value at address 101.
Frame Symbol Address Value
swap
s 105 83
m 104 101
k 103 100
Return Location 102 line 13
f
c 101 83
a 100 74
After the swap function finishes, the top frame is popped.
Frame Symbol Address Value
f
c 101 83
a 100 74
Note that the values of a and c have been changed. Pointers are a central feature of C
programming, and they must be handled carefully. The swap function should be understood
thoroughly, since it is a simple example of using pointers. You should understand how to
call swap, how it is implemented, and why it is implemented in the way that it is.
Section 2.4 says a function can see only its own frame. However, the swap function
modifies the values of a and c even though a and c are in a different frame. Does this mean
the rule in Section 2.4 is violated? The answer is no. The swap function still cannot access
a or c directly. The swap function can access a or c indirectly because k and m store the
addresses of a and c. Through pointers, a function can access (i.e., read or write)
the values of variables in another frame.
By using pointers, the swap function can read or write the values in f’s frame. Is it
possible for f to use pointers to read or write variables in swap’s frame? We can illustrate
this question with a simple example. Will the following code change m’s value from 0 to 7?
int * f1 ( void )1
{2
int m = 0;3
return & m ;4
}5
6
void f2 ( void)7
{8
int * iptr = f1 () ;9
/* RL */10
* iptr = 7;11
}12
The answer is no: m exists only inside of f1’s frame.
Before calling f1, m does not exist.
When running the code in f1, the program executes the statements in f1, not in f2.
50 Intermediate C Programming
After f1 finishes, the program continues from the return location (line 10). The top
frame has been popped and m no longer exists.
Hence, it is impossible for f2 to modify m. In fact, most compilers will warn you that the
fourth line is likely a mistake. Using pointers to read or write only works in one direction.
If f2 calls f1, f1 can read or write values in f2’s frame but f2 cannot read or write values
in f1’s frame. This rule can be generalized: Through pointers, a function can read or write
values stored in the function’s frame or the stack frames below it. It is impossible to read
or write values in a frame that is above the function’s frame.
4.5 Type Errors
Every time I teach pointers, I get questions like this: What happens if the types are
mixed up? For example,
int a = 5;1
char * cptr ;2
cptr = & a;3
The type of a is int and cptr is a pointer to char. What will happen? The simple answer
is don’t do it. Mixing types is asking for trouble. In this case, the program will assume
that there is a char variable at the address of a, when clearly there is an int variable there.
It makes no sense. The precise value of * cptr will depend on the type of hardware that
is running the program. Fortunately, gcc will tell you there is a problem by adding -Wall
after calling gcc.
How about this?
int a = 5;1
int * iptr ;2
iptr = a;3
* iptr = -12;4
This is also problematic. If we manage to convince the compiler to actually compile it, we
will end up assigning 5 to iptr. Although this makes no sense, it will not cause problems per
se. The real problem comes when we attempt to use the pointer. When creating pointers,
we almost always want to dereference them at some stage. That is, we add * in front of the
pointer to read from or to write to the value stored at address stored in the pointer. The
third line assigns 5 (a’s value, not a’s address) to iptr’s value. Now adding * in front of
iptr will cause the program to try to read from or write to the value at address 5. However,
programmers have no control over the specific addresses a program uses. The fourth line
intends to write 12 to the value at address 5. This is not a’s address—a’s address is
& a—and a problem will ensue. In fact, modern operating systems will stop the program
from doing it. Address 5 is almost certainly inaccessible to your program, and attempting
to read from or write to the value at this address will cause the operating system to stop
the program.
Pointers 51
4.6 Arrays and Pointers
What is an array? Consider the following example:
int arr [5];1
This line of code creates an array of 5 elements; each element is an integer. The elements
have not been initialized so the values are garbage. The following example shows how to
write to the array elements.
arr [0] = 6; // assign 6 to the first element1
arr [1] = -11; // assign -11 to the second element2
arr [2] = 9; // assign 9 to the third element3
Please remember that an array’s indexes always start from zero.
Arrays have a special relationship with pointers, and in many cases are indistinguishable
from pointers. We can illustrate this by explaining the specific meaning of the third line
above. This line does three things:
1. Interprets arr’s value as a pointer whose value is the address of the first element of
the array.
2. Finds the address of two elements after the address of the first element to get a new
address. Here, the value 2 is the index inside [ ] and is called address offset.
3. Modifies the value at that address to 9.
How about the following example?
int c; // create an integer variable called c1
c = arr [1]; // read the value of the second element2
// and assign it to c3
The second line does four things:
1. Interprets arr’s value as a pointer whose value is the address of the first element
2. Finds the address of one element after that address to get a new address. Here, the
value 1 is the address offset.
3. Reads the value at that address and it is 11.
4. Writes 11 to c’s value.
Note that arr can always be interpreted as a pointer whose value is the address of the
first element of the array. The first element is arr[0]. Thus, the following equality is always
true:
arr == & arr [0]1
In Section 2.3.5, we noted that the addresses of array elements are contiguous. If arr
stores the address of the first element, a compiler can easily calculate the address of any
element. The address of arr[k] is the address of arr[0] plus k × the size of each element.
For the time being, let’s assume that each element occupies only one unit of memory space.
We will examine the size of data in a later chapter.
Since arr is the address of the first element, calling a function with arr allows the called
function to access the array’s elements (both reading and writing). We can use a pointer
type as the argument for the array. The following example adds the elements in an array:
int sumarr ( i n t * intarr , in t len )1
{2
int ind ;3
int sum2 = 0; // r e member to initia l ize to zero4
52 Intermediate C Programming
for ( ind = 0; ind < len ; ind ++)5
{6
sum2 += intarr [ ind ];7
}8
return sum2 ;9
}10
void f( void )11
{12
int sum = 0;13
int arr [5];14
arr [0] = 4;15
arr [1] = -7;16
arr [2] = 2;17
arr [3] = 3;18
arr [4] = 9;19
sum = sumarr ( arr , 5) ;20
/* RL */21
printf (" sum = %d n" , sum );22
}23
The function f2 creates arr, an array of five elements and sum, an integer. This is the
call stack before calling sumarr.
Frame Symbol Address Value
f
arr[4] 105 9
arr[3] 104 3
arr[2] 103 2
arr[1] 102 7
arr[0] 101 4
sum 100 0
Function f calls function sumarr with two arguments: arr and 5. The former is the
address of arr[0]. This is the call stack after starting function sumarr before the fifth line.
Frame Symbol Address Value
sumarr
sum2 111 0
ind 110 garbage
len 109 5
intarr 108 101
value address 107 100
return location 106 line 21
f
arr[4] 105 9
arr[3] 104 3
arr[2] 103 2
arr[1] 102 7
arr[0] 101 4
sum 100 0
Please pay special attention to the value of intarr at address 108. The value is 101
because the address of the first element, i.e., & arr[0], is 101. In C programs, an array
itself does not provide information about the number of elements. As a result, when calling
sumarr, another argument is needed for the number of elements. Because intarr has the
address of the array’s first element, function sumarr can read the array’s elements even
Pointers 53
though the array is stored in a different frame. The for loop adds the elements’ values and
stores the result in sum2. This is the call stack after finishing the for loop.
Frame Symbol Address Value
sumarr
sum2 111 0 11
ind 110 5
len 109 5
intarr 108 101
value address 107 100
return location 106 line 21
f
arr[4] 105 9
arr[3] 104 3
arr[2] 103 2
arr[1] 102 7
arr[0] 101 4
sum 100 0
The value of sum2 is then written to the value at address 100 (sum’s address). This is
the call stack after function sumarr has finished.
Frame Symbol Address Value
f
arr[4] 105 9
arr[3] 104 3
arr[2] 103 2
arr[1] 102 7
arr[0] 101 4
sum 100 0 11
Because an array is passed as a pointer to the first element, a function can modify the
values of an array in another frame. Consider this example:
void incrarr ( in t * intarr , i n t len )1
{2
int ind ;3
for ( ind = 0; ind < len ; ind ++)4
{5
intarr [ ind ] ++;6
}7
}8
void f( void )9
{10
int arr [5];11
arr [0] = 4;12
arr [1] = -7;13
arr [2] = 2;14
arr [3] = 3;15
arr [4] = 9;16
incrarr ( arr , 5) ;17
/* RL */18
}19
This is the call stack after entering incrarr before executing the for loop.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset