Exam 2 Practice Problems
This document contains some practice problems designed to prepare students for the second exam. Some questions have answers are partial answers available while others do not. Students may visit office hours to discuss full answers to all problems.
Table of Contents
1 Pointers on the Stack
1: #include <stdio.h> 2: void mult1(int p1, int p2){ 3: p1 = p1 * p2; 4: } 5: void mult2(int *p1, int *p2){ 6: *p1 = (*p1) * (*p2); 7: } 8: int main(){ 9: int i=4, j=2, k=7; 10: mult1(i, j); /* Start here */ 11: mult2(&j, &k); 12: printf("%d %d %d\n",i,j,k); 13: /* Show state of stack */ 14: 15: int *p = &i; 16: mult2(p,p); 17: printf("%d %d %d %d\n",i,j,k,*p); 18: /* Show state of stack */ 19: 20: mult1(k,*p); 21: *p = 5; 22: printf("%d %d %d %d\n",i,j,k,*p); 23: /* Show state of stack */ 24: 25: p = &k; 26: *p = 2; 27: mult2(p, &i); 28: printf("%d %d %d %d\n",i,j,k,*p); 29: /* Show state of stack */ 30: 31: j = *p - i; 32: mult1(*p,i); 33: mult2(&i,&j); 34: printf("%d %d %d %d\n",i,j,k,*p); 35: /* Show state of stack */ 36: 37: return 0; 38: }
For the program above, show the contents of the stack frame of
main()
at each position indicated. This will require you to
understand pointer dereferencing and the address-of operator as well
as how the functions mult1()
and mult2()
affect the stack.
Use the following as the initial state of the stack with the addresses
of main()
local variables as given and line 9 of main()
has been
executed already.
Method | Line | Var | Value | Addr | Notes |
---|---|---|---|---|---|
main() | 10 | i | 4 | 2048 | |
j | 2 | 2052 | |||
k | 2 | 2056 | |||
p | ? | 2060 |
2 Memory Errors
Examine the following code written by programmer Sy Zeoff
1: #include <stdio.h> 2: #include <string.h> 3: #include <stdlib.h> 4: 5: typedef struct{ 6: char name[128]; 7: double dist; 8: } planet_t; 9: 10: planet_t * copy_planet_array(planet_t *pa){ 11: planet_t *new_pa = malloc(sizeof(pa) * sizeof(planet_t)); 12: int i=0; 13: for(i=0; i<sizeof(pa); i++){ 14: new_pa[i] = pa[i]; 15: } 16: return new_pa; 17: } 18: 19: int main(){ 20: planet_t solarsys[9] = { 21: {.name="Mercury" , .dist=0.4}, 22: {.name="Venus" , .dist=0.7}, 23: {.name="Earth" , .dist=1.0}, 24: {.name="Mars" , .dist=1.5}, 25: {.name="Jupiter" , .dist=5.2}, 26: {.name="Saturn" , .dist=9.5}, 27: {.name="Uranus" , .dist=19.6}, 28: {.name="Neptune" , .dist=30.0}, 29: {.name="PLUTO!!!" , .dist=35.0} 30: }; 31: 32: planet_t * copy = copy_planet_array(solarsys); 33: 34: int i; 35: for(i=0; i<9; i++){ 36: printf("%s %lf\n",copy[i].name,copy[i].dist); 37: } 38: 39: free(copy); 40: 41: return 0; 42: }
Sy intends for the function copy_planet_array()
to create a
heap-allocated copy an array of planet_t
objects. The program
compiles but on running it, Sy sees that something is wrong based on
the output of main()
.
1: > gcc memory_errors.c 2: > a.out 3: Mercury 0.400000 4: Venus 0.700000 5: Earth 1.000000 6: Mars 1.500000 7: Jupiter 5.200000 8: Saturn 9.500000 9: Uranus 19.600000 10: Neptune 30.000000 11: 0.000000
Sy is inclined to "fix" this error by changing one line of
copy_planet_array()
from
for(i=0; i<sizeof(pa); i++){
to
for(i=0; i<sizeof(pa)+1; i++){
Answer the following questions.
- Explain why Sy's proposed "fix" will not work in every case.
- Explain why Sy has a fatal flaw in his understanding of how to solve this problem
- Create a working version of
copy_planet_array()
. This may involve deviating from Sy's current version of the program somewhat such as altering its prototype or re-arranging his code.
3 Heap Memory
The following program involves memory allocation on the heap. Focuse
your attention on the main()
function. The remaining functions are
utilities to print program information as it runs.
1: #include <stdio.h> 2: #include <stdlib.h> 3: 4: typedef struct{ 5: int channel; double frequency, phase; 6: } channel_params; 7: 8: void print_state(channel_params chan, channel_params *cpp, 9: int *ip, channel_params *cpa, int cpa_len); 10: 11: int main(){ 12: channel_params chan = {.channel=2, .frequency=2.417e9, .phase=0.5}; 13: channel_params *cpp = &chan; 14: int *ip = &(chan.channel); 15: channel_params *cpa = NULL; 16: 17: /* Draw the stack and heap here */ 18: print_state(chan, cpp, ip, cpa, 0); 19: 20: cpa = malloc(sizeof(channel_params)*3); 21: cpa[0] = chan; 22: cpa[1] = *cpp; 23: cpa[1].phase = 1.0; 24: cpa[2].channel=3; cpa[2].frequency=2.422e9; cpa[2].phase=0.75; 25: 26: cpp = &cpa[1]; 27: 28: /* Draw the stack and heap here */ 29: print_state(chan, cpp, ip, cpa, 3); 30: 31: ip = &cpa[1].channel; 32: *ip = 4; 33: cpp->frequency = 2.427e9; 34: 35: /* Draw the stack and heap here */ 36: print_state(chan, cpp, ip, cpa, 3); 37: 38: free(cpa); 39: 40: return 0; 41: } 42: 43: /* Utility to print a channel */ 44: void print_channel(channel_params cp){ 45: printf("{.channel=%2d, .frequency=%8.3e, .phase=%6.2lf}", 46: cp.channel, cp.frequency, cp.phase); 47: } 48: 49: /* Utilitity to print an array of channel_params */ 50: void print_channels(channel_params *cps, int n_cps){ 51: int i; 52: for(i=0; i<n_cps; i++){ 53: printf("[%2d]: ",i); 54: print_channel(cps[i]); 55: printf("\n"); 56: } 57: } 58: 59: /* Print state of program */ 60: void print_state(channel_params chan, channel_params *cpp, 61: int *ip, channel_params *cpa, int cpa_len){ 62: printf("chan : "); print_channel(chan); printf("\n"); 63: printf("*cpp : "); print_channel(*cpp); printf("\n"); 64: printf("*ip : "); printf("%d\n",*ip); 65: if(cpa == NULL){ 66: printf("*cpa : NULL\n"); 67: } 68: else{ 69: printf("cpa[]:\n");print_channels(cpa,cpa_len); 70: } 71: printf("\n"); 72: }
Draw the state of the stack and the heap at the positions indicated. Make the following assumptions.
ints
are 32 bits,doubles
are 64 bits, pointers are 32 bits- The stack frame for
main()
is laid as indicated below. - The heap starts at the position indicated below.
Method | Line | Var | Value | Addr | Notes |
---|---|---|---|---|---|
main() | 10 | chan.channel | ? | 1024 | |
chan.frequency | ? | 1028 | |||
chan.phase | ? | 1036 | |||
cpp | ? | 1044 | |||
ip | ? | 1048 | |||
cpa | ? | 1052 | |||
… | |||||
4048 | Heap starts here |
4 Input File Processing: Count Word Occurrences
Write a main()
function which counts the number times a word appears
in a file. Both the file to search and the word to search for are
specified on the command line as per the examples below.
1: > gcc count_word.c 2: > a.out 3: usage: a.out word filename 4: > a.out apple not-there.txt 5: Could not open file 'not-there.txt'; exiting 6: 7: > cat fruitss.txt 8: banana grape apple 9: apple orange 10: grape apple 11: banana 12: kiwi apple tomato 13: > a.out banana fruitss.txt 14: banana appears 2 times in file fruitss.txt 15: > a.out apple fruitss.txt 16: apple appears 4 times in file fruitss.txt 17: > cat veggies.txt 18: carrot beet carrot 19: asparagus beet 20: tomato beet carrot 21: beet tomato 22: > a.out carrot veggies.txt 23: carrot appears 3 times in file veggies.txt 24: > a.out apple veggies.txt 25: apple appears 0 times in file veggies.txt 26: > a.out beet veggies.txt 27: beet appears 4 times in file veggies.txt
Notes and hints
- You may assume that all words in files are shorter than 128 characters long. This is a mildly dangerous assumption but simplifies the code.
- While it is possible to solve this problem by scanning character by
character through a file, it is much easier to read whole words from
a file using an appropriate call to
fscanf()
which should also ignore whitespace. - Make sure to check that both command line arguments are specified (search word and file to search) and that the file is opened successfully.
- You will likely want to do string comparison to check whether a word read from the file matches the search word. Review the header file which allows one to access string functions and how the string comparison function works.
5 Ordered Pair Allocation
The struct int_pair
is defined below.
typedef struct { int i,j; } int_pair;
It represents a pair of integers with its i
and j
fields.
Write a function
int_pair * allocate_pairs(int max_i, int max_j, int *len);
which allocates memory for an array of int_pairs
on the heap and
fills it with pairs in a specific pattern. All integers
0,1,2,... (max_i-1)
are paired with all integers
0,1,2,... (max_j-1)
and appear as pairs in the array. The order of
pairs should follow this pattern.
(0,0), (0,1), (0,2),... (0,max_j-1), (1,0), (1,1), ... (1,max_j-1), (2,0), ... (max_i-1, max_j-1)
Note that the j
field of the pairs changes faster than the i
field.
The argument len
to the function allocate_pairs()
is a pointer to
single integer that should be set to the length of the array.
The following main()
function demonstrates the prototype and use of
allocate_pairs()
.
1: #include <stdio.h> 2: #include <string.h> 3: #include <stdlib.h> 4: 5: typedef struct { 6: int i,j; 7: } int_pair; 8: 9: int_pair * allocate_pairs(int max_i, int max_j, int *len); 10: 11: int main(int argc, char **argv){ 12: int len = -1; 13: int_pair *arr = NULL; 14: int k; 15: 16: arr = allocate_pairs(1, 9, &len); 17: for(k=0; k<len; k++){ 18: printf("(%d,%d), ",arr[k].i, arr[k].j); 19: } 20: printf("\n"); 21: // (0,0), (0,1), (0,2), (0,3), (0,4), (0,5), (0,6), (0,7), (0,8), 22: free(arr); 23: 24: arr = allocate_pairs(7, 2, &len); 25: for(k=0; k<len; k++){ 26: printf("(%d,%d), ",arr[k].i, arr[k].j); 27: } 28: printf("\n"); 29: // (0,0), (0,1), (1,0), (1,1), (2,0), (2,1), (3,0), (3,1), (4,0), (4,1), (5,0), (5,1), (6,0), (6,1), 30: free(arr); 31: 32: arr = allocate_pairs(4, 5, &len); 33: for(k=0; k<len; k++){ 34: printf("(%d,%d), ",arr[k].i, arr[k].j); 35: } 36: printf("\n"); 37: // (0,0), (0,1), (0,2), (0,3), (0,4), (1,0), (1,1), (1,2), (1,3), (1,4), (2,0), (2,1), (2,2), (2,3), (2,4), (3,0), (3,1), (3,2), (3,3), (3,4), 38: free(arr); 39: 40: return 0; 41: }