Professional Documents
Culture Documents
03 CS107 Practice Midterm
03 CS107 Practice Midterm
Open book/notes You may bring textbooks, notes, handouts, code printouts, etc. to refer to during the exam. No computers, phones, PDAs, or other electronic devices may be used. The exam will include a list of relevant stdlib function prototypes. Dont let the "open-book" nature delude you into not preparing. There will not be time to learn or relearn the material during the exam. You must come prepared to answer questions, referring to your notes only for the occasional detail. Material The exam will concentrate on material covered in the labs and assignments. For the midterm, this means questions that delve into strings, pointers, arrays, function pointers, low-level memory manipulation, and data representation (e.g. bits, ints, floats). (IA32 assembly will not be tested on the midterm.) When evaluating your C code, we will not be picky about minor syntax/oversights (missing braces around a block of clearly indented code, forgetting to declare an integer variable, we don't ask for #include's etc.), but there are subtleties that matter immensely, e.g., an int** is just one character different than int*, yet there is a world of difference between the two! The &s, *s, []s, and typecasts are things that really matter. Practice You're unlikely to do well on a test if you don't understand the core concepts, but there is no guarantee about the inverse. Some students who are quite accomplished in practice don't manage to demonstrate that same proficiency in the exam setting. Writing code on paper under time constraints is different than working at the computer, and most students need practice to adapt their skills. We recommend you sit down with the problems and write out solutions in longhand. This is much more valuable than a passive review of the problem alongside its solution where it is too easy to conclude "ah, yes, I would have done that" only to find yourself sad during the real exam when there is no provided solution to agree with! The rest of this handout is based the midterm given last winter in CS107 so you can consider the questions fairly representative in terms of format, difficulty, and content. The one key difference is that those questions were written for a 180-minute exam, and your exam will be written to take about 75 minutes, even though Im giving you two hours to take it. Ill also confess that the second problem was a little longer than it needed to be.
4 Larger words can't be compactly stored this way. For larger words, the eight bytes need to be used differently. Here's the final heuristic: o The first of the eight bytes will be to tell us whether the remaining seven are enough to store the entire suffix. This first byte will store a 0 [equivalently, a '\0'] when the suffix of the word being stored is 7 characters or fewer. The suffix should itself be terminated with a '\0' if its length is less than 7, but suffixes of length 7 should not store the '\0', since there won't be any room for it. o Should the first byte store anything nonzero, the remaining seven bytes are divided up. Bytes 2, 3, and 4, store the first three characters of the suffix, but bytes 5 through 8 store the address of a dynamically allocated character array large enough to store the rest of them. o When analyzing the suffix, it's the implementation's responsibility to check this first byte to see if the suffix is fully stored in the remaining seven bytes, or if the suffix is broken up into two separate arrays. Here are a few examples: The word "abacus" would take up eight bytes in the 2nd [or the "ab"] CVector. The suffix would be stored as follows:
'a'
'c'
'u'
's'
The first byte stores a zero, because the "acus" suffix can be fully stored in the remaining seven bytes. The leading 0 informs the implementation that everything resides in the eight-byte chunk. The word "polyphony" would take up eight bytes in the "po" CVector. The suffix would be stored as follows: 0 'l' 'y' 'p' 'h' 'o' 'n' 'y'
Again, the "lyphony" suffix can be wedged into the seven-byte chunk. The only difference here is that the '\0' can't be stored. This shouldnt limit your implementation, as it should just realize that at most seven characters can be accommodated. The word "onomatopoeia" is a mighty big one. The "on" CVector would contain eight bytes on behalf of "omatopoeia", but those eight bytes would look this:
'o'
'm'
'a'
't'
'o'
'p'
'o'
'e'
'i'
'a'
Note the 1 in the very first byte. Thats the signal that the last four bytes point to dynamically allocated space [allocated by the implementation, of course] to store the suffix that just couldn't fit in the eight primary bytes.
5 The dynamically allocated portion is always null-terminated and is always exactly the size it needs to be to store the rest of the characters. Some relevant ANCI C functions youll want to make use of: int strcmp(const char *s1, const char *s2); int strncmp(const char *s1, const char *s2, int len); void strcpy(char *dst, const char *src); void strncpy(char *dst, const char *src, int len); strcmp compares the two null-terminated C strings and returns a negative number if the first is lexicographically less than the second, a positive number if the first is lexicographically greater than the second, and 0 if they exactly match. strncmp does the same thing, except that it compares at most len characters. strcpy copies the string src to dst [including the terminating '\0' character]. The strncpy function copies no more than len characters from src into dst, appending '\0' characters if src is less than len characters long, and not terminating dst otherwise. a) Implement the CLexiconCreate and CLexiconDispose functions. CLexiconCreate dynamically allocates a CLexiconImplementation struct, initializes all 676 CVector *s within to address properly constructed but otherwise empty CVectors, and then returns the address of the struct. CLexiconDispose brings down all of the resources contributing to the CLexicon being destroyed. CLexicon *CLexiconCreate() { void CLexiconDispose(CLexicon *cl) { b) Implement the CLexiconContains function. You should leverage CVectorSearch to detect whether the word is present, but you shouldnt assume the suffixes are otherwise sorted in any way. Take the time to create helper functions if you foresee them being useful in the context of inserting and mapping over words. [You will need to write a comparison function and pass it to CVectorSearch. You should assume the key CVectorSearch is looking for is always passed as the first of the two parameters to the comparison function.] static int CLexiconHashToBucket(const char *str) { int msb = str[0] - 'a'; // more significant byte int lsb = str[1] - 'a'; // less significant byte return msb * 26 + lsb; // base 26 number in range [0, 676) } bool CLexiconContains(CLexicon *cl, const char *str) { c) Next, implement the CLexiconAdd function, which ensures that the specified word is present in the CLexicon. You should call CLexiconContains directly, and if the word is already present, you should just return without doing anything. If the word is missing, you should append the correct eight-byte figure to the end of the relevant CVector. You should not worry about sorting anything. void CLexiconAdd(CLexicon *cl, const char *word) {
6 d) Finally, implement the CLexiconMap function. You should assume for the sake of convenience that no word in the lexicon is ever longer than 64 characters. You will need to manually reconstruct all of the strings on-thefly so that they can be passed to the mapping routine, but the reconstruction should not make use of any dynamically allocated memory. void CLexiconMap(CLexicon *cl, CLexiconMapFunction mapfn, void *auxData) {
If n is
positive negative zero
Mystery(n) returns.