
































Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
Introduction to computer science. CS107 Practice Midterm Exam with solution of Programming Paradigms. Prof. Cain - Stanford University
Typology: Exams
1 / 40
This page cannot be seen from the preview
Don't miss anything!

































CS107 Handout 21
Spring 2008 April 30, 2008
Exam Facts
Normal Time: Wednesday, May 7th^ at 7:00 p.m. in Hewlett 200.
The exam is open-notes, open-book, closed-computer. You should bring your own lecture notes, the course
handouts, and any printouts of assignment code you’ll want to consult during test time.
This practice exam is constructed from actual midterms I’ve given in previous quarters. You will effectively
have as much time as you want, although we will need to pull the exams at 10:00 p.m., since that’s when the
room needs to be locked back up. Those on campus who need to take the exam earlier in the day on
Wednesday may do so, provided you start some time after 9:00 a.m. (but not during the hour that I teach. ☺)
Material
The exam will focus on material like that covered on the first five assignments. Be prepared for C/C++ coding
questions requiring strong understanding of pointers, references, arrays, function pointers, and low-level
memory manipulation, as well as questions on code generation, function call and return, variable layout, stack
and heap implementation, and the compilation process as covered in Assignment 5.
In general, a good way to study for the coding questions is to take a problem for which you have a solution
(lecture or section example, homework problem, sample exam problem) and write out your solution under test-
like conditions. This is much more valuable than a passive review of the problem and its solution where it is too
easy to conclude "ah yes, I would have done that" only to find yourself adrift during the real exam when there is
no provided solution to guide you!
This handout is intended to give you practice solving problems that are comparable in format and difficulty to
those which will appear on your midterm. Understand that I am under no obligation to mimic this exam when
writing yours. That being said, any material covered in lecture or on an assignment is fair game.
Problem 1: San Francisco Fine Dining
Consider the following struct definitions:
typedef struct { int **garydanko; int aqua[3]; char *quince; } appetizer;
dessert *dinnerisserved(short *boulevard, appetizer *jardiniere); int *bonappetit(dessert azie, char **indigo) { appetizer oola; dessert *catch;
azie.bacar[azie.ame[2]] += catch->farallon.aqua[4]; ((appetizer *)(((dessert )(&oola.quince))->farallon.garydanko))->quince = 0; return (dinnerisserved((short *) &indigo, &oola)).farallon.aqua; }
Generate code for the entire bonappetit function. Be clear about what assembly code corresponds to what line. You have this and the next page for your code.
typedef struct { short ame[2]; appetizer farallon; char bacar[8]; } dessert;
line 1 line 2 line 3
Problem 3: The C multiset
A multiset operates much like the hashset from Assignment 3, except that the multiset maintains a count on
how many times each element has been inserted. The similarities between hashset and multiset are otherwise
so great that it makes sense to layer the multiset right on top of the hashset.
Our multiset is fully generic, so the client should be able to use one to store any type he or she wants. But each
item needs to be accompanied by its multiplicity. So each element is packed up against a sizeof(int)-byte
integer and stored in the encapsulated hashset.
The difficult part here is the manual construction of these variably-sized figures so that they can be inserted into
the encapsulated hashset.
Here’s the reduced interface file for the multiset:
typedef int (MultiSetHashFunction)(const void elem, int numBuckets); typedef int (MultiSetCompareFunction)(const void elem1, const void elem2); typedef void (MultiSetMapFunction)(void *elem, int count, void auxData); typedef void (MultiSetFreeFunction)(void *elem);
typedef struct { hashset elements; int elemSize; MultiSetFreeFunction free; } multiset;
void MultiSetNew(multiset *ms, int elemSize, int numBuckets, MultiSetHashFunction hash, MultiSetCompareFunction compare, MultiSetFreeFunction free); void MultiSetDispose(multiset *ms); void MultiSetEnter(multiset *ms, const void *key); void MultiSetMap(multiset *ms, MultiSetMapFunction map, void *auxData);
Your job is to implement all four functions, leveraging as much as possible off of the hashset routines
implemented in Assignment 3.
Some additional information:
elemSize bytes sizeof(int) bytes
a. Present implementations of MultiSetNew and MultiSetDispose. These should be short and
straightforward.
typedef int (MultiSetHashFunction)(const void elem, int numBuckets); typedef int (MultiSetCompareFunction)(const void elem1, const void elem2); typedef void (MultiSetFreeFunction)(void *elem);
/**
void MultiSetNew(multiset *ms, int elemSize, int numBuckets, MultiSetHashFunction hash, MultiSetCompareFunction compare, MultiSetFreeFunction free) {
void MultiSetDispose(multiset *ms) {
Problem 4: The Queen Of Parking Infractions
Parking on campus has become so laughably difficult that some have resigned themselves to parking illegally and
just paying whatever parking tickets they get. In the interest of crowning the Queen of Parking Infractions, you’ve
been handed a fully constructed and populated multiset of license plate numbers, where the multiplicity is
understood to be the number of parking tickets recorded for a particular license plate. Because all license plate
numbers are strings of at most 7 characters, the multiset stores eight-byte figures, which are really static
character arrays storing null-terminated C strings of length seven or less.
Your job is to complete the implementation of FindQueenOfParkingInfractions, which takes the address of a
fully constructed multiset of license plates, and populates the specified character buffer with the license plate for
which the maximum number of parking tickets has been recorded. You should use the convenience struct I’ve
provided.
typedef struct { const char *licensePlate; int numTickets; } maxTicketsStruct;
void FindQueenOfParkingInfractions(multiset *ms, char licensePlateOfQueen[]) {
Problem 5: Short Answers
For each of the following questions, we expect short, insightful answers, writing code or functions only when
necessary. Clarity and accuracy of explanations are key.
a. Assuming all instructions and pointers are 4 bytes, explain why instructions of the form M[x] = M[y] +
M[z], where x, y, and z are legitimate but otherwise arbitrary memory addresses, aren't included in the instruction set.
b. Our activation record model wedges the return address information below the function arguments and above
the local parameters. Why not pack all variables together, and place this return address at the top or the bottom of the activation record?
c. Write a short function called IsLittleEndian, which returns true if and only if the computer architecture is
little endian—that is, if the least significant byte of a multi-byte figure is stored at the lowest address instead of the highest one.
CS107 Handout 21S Spring 2008 April 30, 2008
Problem 1: San Francisco Fine Dining
Consider the following struct definitions:
typedef struct { int **garydanko; int aqua[3]; char *quince; } appetizer;
dessert *dinnerisserved(short *boulevard, appetizer *jardiniere); int *bonappetit(dessert azie, char **indigo) { appetizer oola; dessert *catch;
azie.bacar[azie.ame[2]] += catch->farallon.aqua[4]; ((appetizer *)(((dessert )(&oola.quince))->farallon.garydanko))->quince = 0; return (dinnerisserved((short *) &indigo, &oola)).farallon.aqua; }
Generate code for the entire bonappetit function. Be clear about what assembly code corresponds to what line. You have this and the next page for your code.
The full activation record layout is large as far as CS107 activation records go. The picture is here, and the code is on the next page.
typedef struct { short ame[2]; appetizer farallon; char bacar[8]; } dessert;
line 1 line 2 line 3
saved PC
SP before decrement
SP after decrement catch
oola.garydanko
oola.aqua[0]
oola.aqua[1]
oola.aqua[2]
oola.quince
azie.ame[0…1]
azie.farallon.garydanko
azie.farallon.aqua[0]
azie.farallon.aqua[1]
azie.farallon.aqua[2]
azie.farallon.quince
azie.bacar[0…3]
azie.bacar[4…7]
indigo
Problem 3: The C multiset
a. void MultiSetNew(multiset *ms, int elemSize, int numBuckets, MultiSetHashFunction hash, MultiSetCompareFunction compare, MultiSetFreeFunction free) { HashSetNew(&ms->elements, elemSize + sizeof(int), numBuckets, hash, compare, free); ms->elemSize = elemSize; ms->free = free; }
/**
void MultiSetDispose(multiset *ms) { HashSetDispose(&ms->elements); }
b. void MultiSetEnter(multiset *ms, const void *elem) { void *found = HashSetLookup(&ms->mappings, elem); if (found == NULL) { char pair[ms->elemSize + sizeof(int)]; memcpy(pair, elem, ms->elemSize); *(int )(pair + ms->elemSize) = 1; HashSetEnter(&ms->mappings, pair); } else { if (ms->free != NULL) ms->free(found); memcpy(found, elem, ms->elemSize); ((int *)((char *) found + ms->elemSize))++; } }
c. typedef struct { MultiSetMapFunction originalMap; void *originalAuxData; int elemSize; } mapHelper;
static void ApplyMultiMapFunctionWithAuxData(void *elem, void *auxData) { mapHelper *helper = auxData; int multiplicity = *(int *)((char *) elem + helper->elemSize); helper->originalMap(elem, multiplicity, helper->originalAuxData); }
void MultiSetMap(multiset *ms, MultiSetMapFunction map, void *auxData) { mapHelper helper = { map, auxData, ms->elemSize }; HashSetMap(&ms->elements, ApplyMultiMapFunctionWithAuxData, &helper); }
Problem 4: The Queen Of Parking Infractions (5 points)
typedef struct { const char *licensePlate; int numTickets; } maxTicketsStruct;
static void IdentifyContender(void *elem, int count, void *auxData) { maxTicketsStruct *ticketData = auxData; if (count > ticketData->numTickets) { ticketData->numTickets = count; ticketData->licensePlate = elem; // cast not needed, but fine to have it } }
void FindQueenOfParkingInfractions(multiset *ms, char licensePlateOfQueen[]) { maxTicketsStruct ticketData = { NULL, 0 }; MultiSetMap(ms, IdentifyContender, &ticketData); strcpy(licensePlateOfQueen, ticketData.licensePlate); }
Problem 5: Short Answers
For each of the following questions, we expect short, insightful answers, writing code or functions only when
necessary. Clarity and accuracy of explanations are key.
a. Assuming all instructions and pointers are 4 bytes, explain why instructions of the form M[x] = M[y] +
M[z], where x, y, and z are legitimate but otherwise arbitrary memory addresses, aren't included in the instruction set.
If x, y, and z are legitimate but otherwise arbitrary memory address, then the instruction would need 32 bits to encode any one of them. Since the entire instruction is confined by a 32-bit limit, there’s no way to pack information about three address and the opcode for addition into the instruction.
b. Our activation record model wedges the return address information below the function arguments and
above the local parameters. Why not pack all variables together, and place this return address at the top or the bottom of the activation record?
The return address can't be placed on the bottom because the calling function doesn't know where the bottom of the full layout is. The caller knows nothing of what locals are allocated by the callee's instruction list.
The return address could, in most cases, be placed at the top since the callee function generally knows what all of the parameters are and how large everything is. The key exception—the reason that this as a convention could not be adopted—is that some functions, such as printf and scanf, take a variable number of arguments. In those situations, no information about the number and size of the parameters is around, so it can't reliably understand where the stored PC would be.
CS107 Handout 25S Spring 2008 May 14 th, 2008
Problem 1: Your Friendly Green Grocer (15 points: 5, 4, and 6)
Consider the following struct definitions:
typedef struct { int apple; char *banana; char cherry[16]; } fruit;
typedef struct { short pea[6]; fruit potato; fruit *parsnip[3]; } veggie;
fruit *tort(fruit **fig, int grape); fruit *casserole(veggie carrot, veggie *spinach) { fruit date; date.cherry[4] = spinach->pea[carrot.potato.apple]; ((veggie *)(((veggie *)carrot.parsnip[0])->parsnip))->potato.banana = *(char **) &date; return tort(&(spinach->parsnip[2]), date.banana[4]) + 10; }
Generate code for the entire casserole function. Be clear about what assembly code corresponds to what line. You have this and the next page for your work.
line 1 line 2
line 3
carrot.potato.apple
carrot.potato.banana
carrot.peas[0…1]
carrot.peas[2…3]
carrot.peas[4…5]
carrot.potato.cherry[0…3]
carrot.potato.cherry[4… ]
carrot.potato.cherry[8… 1]
carrot.potato.cherry[12…15]
carrot.parsnip[0]
carrot.parsnip[1]
carrot.parsnip[2]
spinach
saved pc
date.apple
date.banana
date.cherry[0…3]
date.cherry[4…7]
date.cherry[8…11]
date.cherry[12…15]
// allocation of date SP = SP - 24;
// line 1 R1 = M[SP + 40]; // load carrot.potato.apple R2 = R1 * 2; // scale by sizeof(short) R3 = M[SP + 76]; // load spinach, which is also &spinach->peas[0] R4 = R3 + R2; // compute address of short identified on rhs R5 =.2 M[R4]; // load that short M[SP + 12] =.1 R5 // populate date.cherry[4] with one-byte version
Criteria for Line 1 (5 points)
// line 2 R1 = M[SP]; // load the pretend char * overlaying date.apple R2 = M[SP + 64]; // load carrot.parsnip[0] M[R2 + 52] = R1; // drop R1 in make-believe banana of make-believe veggie
Criteria for Line 2 (4 points)
because this is super important)
// line 3 R1 = M[SP + 76]; // load spinach again R2 = R1 + 44; // load address of parsnip[2] within record addressed by R R3 = M[SP + 4]; // load date.banana R4 =.1 M[R3 + 4]; // load date.banana[4] SP = SP – 8; // make space for function call M[SP] = R2; // write down param 0 value M[SP + 4] = R4; // write down param 1 value CALL
Criteri for Line 3 (6 points)
// deallocation of date SP = SP + 24; RET;
b. (7 points) Now implement HashSetEnter , which manages to copy the element address by elem into the
hashset addressed by hs. It uses the quadratic internal probing technique, as described above, to find a home for the new element. HashSetEnter returns true if the new element gets inserted into a previously unoccupied bucket, and false if the new element replaces a previously inserted one. (Don’t worry about rehashing the hashset if more then three quarters of the buckets are occupied. You’ll worry about that in part c.) Use this and the next page for your work. Don’t worry about alignment restrictions.
bool HashSetEnter(hashset *hs, void *elem) { if (hs->count > (3 * hs->alloclength / 4)) HashSetRehash(hs); // you’ll implement this function for part c
int hashcode = hs->hashfn(elem, hs->alloclength); int delta = 0;
while (true) { hashcode += delta; int bucket = hashcode % hs->alloclength; bool *occupied = (bool *) NthEntry(hs->elems, bucket, hs->elemsize);
void targetAddr = occupied + 1; if (!occupied) { // it goes here, and we return true *occupied = true; memcpy(targetAddr, elem, hs->elemsize); hs->count++; return true; } else if (hs->cmpfn(elem, targetAddr) == 0) { if (hs->freefn != NULL) hs->freefn(targetAddr); memcpy(targetAddr, elem, hs->elemsize); return false; } delta++; } }
Criteria for Problem 2b (7 points)
c. (5 points) Finally, implement the HashSetRehash function, which updates the hashset addressed by
hs so that it has twice as many buckets and all of the elements are rehashed to take up residence in a bucket where they could have resided had the new number of buckets been the number of buckets all along. (You’ll benefit by figuring out how to call HashSetEnter to help with the redistribution.)
static void HashSetRehash(hashset *hs) { hashset clone; memcpy(&clone, hs, sizeof(hashset));
hs->count = 0; hs->alloclength *= 2; hs->elems = malloc(hs->alloclength * EntrySize(hs->elemsize)); for (int i = 0; i < hs->alloclength; i++) *(bool *)NthEntry(hs->elems, i, hs->elemsize) = false;
for (int i = 0; i < clone.alloclength; i++) { bool *occupied = (bool ) NthEntry(clone.elems, i, hs->elemsize); if (occupied) { void *elem = occupied + 1; HashSetEnter(hs, elem); } }
free(clone.elems); }
Criteria for Problem 2c (5 points)
There are several approaches to this problem that will work just fine. My approach is a little more clever than
I’d expect from anyone coding by hand in a timed situation. But the overall effect of whatever you wrote needs to
be the same.
person *decompress(void *image) { int numPeople = *(int *)image; person *people = malloc(numPeople * sizeof(person));
int offset = sizeof(int); for (int i = 0; i < numPeople; i++) { char *name = (char *) image + offset; people[i].name = strdup(name); offset += strlen(name) + 1; while (offset % sizeof(char *) > 0) offset++; people[i].numfriends = *(int *)((char *) image + offset); people[i].friends = malloc(people[i].numfriends * sizeof(char *)); offset += sizeof(int); char **friends = (char **)((char *) image + offset); for (int j = 0; j < people[i].numfriends; j++) { people[i].friends[j] = strdup(friends[j]); } offset += people[i].numfriends * sizeof(char *); }
return people; }
Criteria for Problem 3 (10 points)
Portions of this handout by Eric Roberts and Patrick Young