You are on page 1of 9

NAME:

Login name:

Computer Science 217


Midterm Exam
Wednesday March 9, 2011
10am-10:50am

This exam is open book and open notes. The test has four (4) questions. You should spend no
more than 12 minutes per question for the 50-minute exam. Put your name on every page, and
write out and sign the Honor Code pledge before turning in the test.

``I pledge my honor that I have not violated the Honor Code during this examination.''

Question Score

1 (25 pts)

2 (25 pts)

3 (25 pts)

4 (25 pts)

Total

1
QUESTION 1: A Little Bit of Binary (25 POINTS)

1a) What does printf(“%d”, (76 ^ 37) ^ 37) print to stdout? (5 points)

The answer is 76. The decimal number 76 in binary is 01001100, and the number 37 is
00100101. The result of the first XOR operation is 01101001, or 105 in decimal. The result of
the second XOR operation is 01001100, or 76 in decimal. As a short-cut, note that XORing the
same value (e.g., 37) twice always produces the same result. This is the basis of simple
encryption and decryption schemes, where 76 is the input message and 37 is the key used for
both encrypting and decrypting the message. A common mistake was to OR (i.e., “|”) rather
than XOR.

1b) What does printf(“%d”,0xB5 – ((0xB5 >> 4) << 4)) print to stdout? (5 points)

The answer is 5. Shifting to the right by 4 removes the last four bits of 0xB5, leaving 0xB.
Shifting 0xB5 to the left by 4 adds four 0 bits at the end, resulting in 0xB0. Subtracting this from
0xB5 leads to a result of 0x05, which is 5 in decimal. Note that thinking about this problem
entirely in hexadecimal is easier than converting everything to binary and back, especially since
hexadecimal is very natural for thinking in groups of four bits.

1c) What does printf(“%d”, 0275 & 63) print to stdout? (5 points)

The answer is 61. The number 63 is represented in binary as 000111111. The octal number
0275 is represented in binary as 010111101. Logical AND produces a result of 111101, or 61 in
decimal. As a short cut, note that 63 in binary is 77 in octal (i.e., all 1s in the last six bits), and
the logical AND extracts the last two octal digits 7 and 5. Converted to decimal, this is 61. A
common mistake was to treat 0275 as a hexadecimal number (0x275) rather than octal.

1d) What is the two’s complement of an 8-bit representation of the decimal number 34? (5
points)

The decimal number 34 is 00100010 (i.e., 32 + 2). The 1’s complement is 11011101. Adding 1
to compute the 2’s complement produces 11011110. A common mistake was to use only six bits
to represent 34, leading to a result of 011110, which is not a negative number.

1e) What is the smallest signed integer that can be represented in eight bits? Please give your
answer as a base-10 number. (5 points)

The smallest number is negative. All 1’s is 11111111. Converting to 2’s complement, this is
00000001, meaning that 11111111 is -1 in decimal, so that’s not the right answer. The number
10000000, converted to 2’s complement, is 01111111 + 1, or 10000000, or 128 in decimal. So
the answer is -128. Common mistakes were answers of -127 or -256.

2
QUESTION 2: Good Bug Hunting (25 POINTS)

For each sample code fragment, identify (i) the bug in the code, (ii) how the bug will manifest
itself (e.g., incorrect output, infinite loop, memory leak, compiler error, etc.), and (iii) a
correction that would fix the bug. Assume that all necessary header files (e.g., stdio.h, stdlib.h,
string.h, and assert.h) and a main() function have been included.

2a) Find and fix the bug in this function for printing a string backwards, and identify how the
bug would manifest itself. (6 points)

void printBackwards(char *s) {


int i;

assert(s != NULL);
for (i = strlen(s)-1; i >= 0; i++)
putchar(s[i]);
putchar('\n');
}

i) incrementing rather than decrementing in the loop


ii) will have incorrect output (printing out until i overflows to a negative) or -- more likely -- will
crash during output
iii) decrement instead of incrementing in the loop. replace i++ with i--

2b) Find and fix the bug in this function for concatenating a string with itself, and identify when
the bug would manifest itself. (6 points)

char *Str_repeat(const char *pcSrc) {


char *pcReturn;

assert(pcSrc != NULL);
pcReturn = (char *) malloc(2 * strlen(pcSrc));
assert(pcReturn != NULL);

strcpy(pcReturn, pcSrc);
strcpy(pcReturn + strlen(pcSrc), pcSrc);
return pcReturn;
}

i) not enough memory is allocated because strlen doesn't count trailing nullbyte.
ii) will corrupt memory, writing one beyond the allocated space
iii) in malloc call consider that you need a space for the nullbyte: malloc((2*strlen(pcSrc))+1)

3
The most common error was misunderstanding that strcpy can't know how large its destination
is. The incorrect answers were that the returned string pcReturn would not be null-terminated,
with corresponding correction of strcpy to strcat or explicitly appending a '\0'. The second
common error was misunderstanding the destination "pcReturn + strlen(pcSrc)". The incorrect
answers were either that you can't use + like that, or that the returned string pcReturn would
have only one copy of the string, because it makes a second copy after the nullbyte, with
corresponding correction to be inserting a "-1" into the second call to strcpy ().

2c) Find and fix the bug in this function for identifying the minimum value in an array of
integers, and identify how the bug would manifest itself. (6 points)

int getMinimum(int *A, int n) {


int currmin, i;

assert(A != NULL);
for (i = 0; i < n; i++)
if (A[i] < currmin)
currmin = A[i];
return currmin;
}

i) currmin isn't initialized


ii) unpredictable return value
iii) after assert insert "currmin = A[0]". Ideally, change loop to go from 1 to n to avoid
repeating 0th element

The most common error was an incorrect solution to correctly identified bug. The incorrect
suggested fix involved initializing currmin to 0, which would exhibit the same buggy behavior as
an initial junk value. Another common error was suggesting that n might not be large enough.
The function's programmer has no control over this, and no way to verify whether it is the case
or not (because the array is passed in, so sizeof() is of no use).

2d) Find and fix the bug in this function for initializing a string, and identify how the bug would
manifest itself. (7 points)

char *initialize() {
char string[80];

strcpy(string, "good bug hunting");


return string;
}

int main(void) {
char *string = initialize();
printf("%s\n", string);
}

4
i) returning stack-resident array
ii) possibly wrong output in main because address points to memory that is no longer allocated
and thus may have been changed
iii) lots of possibilities.
Easiest, based on use in program, in initialize(): char* string = "good bug hunting", which
would return an address in RODATA.
Most robust for general use beyond printing and exiting: replace char string[80]; with char*
string = malloc(80*sizeof(char));

The most common error was misunderstanding string literals (for instance, indicating that "good
bug hunting" isn't null-terminated, or that you can't call strcpy() with a string literal). Another
common error was changing program to do allocation in main. These solutions, while
successfully avoiding the bug, prevent the initialize function from knowing the size of the array,
severely limiting it. Another common (minor) error: saying this function corrupts memory -- it
doesn't, because nothing is written to the dangling pointer.

5
QUESTION 3: That Thing That You Do (25 POINTS)

Briefly explain (i) what operation these C functions perform or (ii) what inputs these DFA
accept. Answer in one short phrase --- long answers will have points deducted.

3a) State, in one short phrase, what operation this C function performs. (6 pts)

int q3b(int *a, int n, int m) {


int i, c;
assert(a != NULL);

for (i = 0, c = 0; i < n; i++)


c += (a[i] == m);

return c;
}

Returns the number of items in the array that equal m.

3b) State, in one short phrase, what operation this C function performs. (7 pts)

int q3d(char *s) {


int i, n;
assert(s != NULL);

n = strlen(s);
if (n % 3 != 0)
return 0;

for (i = 0; i < n/3; i++, s++)


if ((*s != *(s+n/3)) || (*s != *(s+2*n/3)))
return 0;

return 1;
}

Checks if the string is a repetition of the same string three times.

6
3c) What binary inputs does this four-state DFA accept? Assume all input characters are either
‘0’ or ‘1’, and that the upper left state is both the initial state and the only accept state. (6 pts)

Accepts binary inputs with an even number of 0 bits and an even number of 1 bits.

3d) State, in one short phrase, what kind of binary numbers this three-state DFA accepts.
Assume all input characters are either ‘0’ or ‘1’, and that the left state is both the initial state and
the only accept state. (6 pts)

Accepts binary numbers that are multiples of 3.

Note, for example, that the DFA accepts 11 (i.e., 3), 110 (i.e., 6), 1100 (i.e., 12), 11000 (i.e., 24),
by visiting the middle state once, followed by zero or more 0’s; this accepts 3 multiplied by any
power of 2. Visiting the middle state more often accepts 1111 (i.e., 15), 111111 (i.e., 63), and so
on; visiting the left state more times multiplies these by powers of 2. Including the third state,
the DFA also accepts 1001 (i.e., 9), 10101 (i.e., 21), 101101 (i.e., 45), and so on. And so on…

7
QUESTION 4: The Work is Stacking Up (25 POINTS)
This question concerns the generic stack module that we discussed in lecture. The stack.h file is
listed below.

typedef struct Stack *Stack_T;

Stack_T Stack_new(void);
void Stack_free(Stack_T s);
int Stack_push(Stack_T s, const void *item);
void *Stack_top(Stack_T s);
void *Stack_pop(Stack_T s);
int Stack_isEmpty(Stack_T s);
int Stack_areEqual(Stack_T s1, Stack_T s2,
int (*cmp)(const void *item1, const void *item2));

4a) Briefly define “data encapsulation” and give two distinct reasons why data encapsulation is
useful. Does this stack module properly encapsulate its data? (5 pts)

Data encapsulation is design in which a client cannot access internals of a data structure except
through the interface functions.

Data encapsulation is useful because it (i) encourages abstraction, (ii) prevents clients from
corrupting the data, and (iii) allows the data structure and module implementation to change
without even recompiling the client code.

Yes, the stack exhibits data encapsulation, because the client does not have access to the struct
definition, only to an opaque pointer.

4b) The Stack_push() function has an item argument of type “const void *”. Why is a
void pointer used rather than (say) an int or char pointer? What does “const void *” mean
and why is the “const” useful? (5 pts)

void* allows us to pass in the memory location of any type of data.

The const keyword means that the thing the pointer points to is a constant, which is useful
because it means the functions can't change it (even if they knew what it was and could
dereference it). Also, using “const void*” allows the client to pass any kind of pointer, whether
a const or not, as an argument.

8
4c) Consider the following client program. Assume the program includes stack.h and stdio.h,
and is compiled along with stack.c. Does this program compile successfully? If so, does the
program crash when it runs? Why or why not? (5 pts)

int main(void) {
char *str1 = "good bug hunting";
int *i;

Stack_T s1 = Stack_new();
Stack_push(s1, str1);
i = (int *) Stack_pop(s1);
printf("%d\n", *i);
}

The code compiles and builds successfully. The program will run but the output will be
nonsensical as it tries to interpret the string's first four bytes (or however many bytes the system
allocates for integers) as an integer.

4d) The Stack_areEqual() function in stack.c relies on the client to provide the comparison
function *cmp(). The client calls Stack_areEqual(s1, s2, intCompare) to
compare a stack of integers. Write the client function intCompare(). The function should
return an integer greater than, equal to, or less than 0, if the first argument is greater than, equal
to, or less than the second argument. Your function should be written so the client program
compiles without errors or warnings, but does not need to perform error checking. (8 pts)

int intCompare(const void* arg1, const void* arg2) {


const int *i = (const int*) arg1;
const int *j = (const int*) arg2;
return *i - *j;
}

The input arguments are const void*, as expected from the (*cmp) in stack.h. These are cast to
integers to enable the function to operate on variables of type integer. Subtracting one number
from another produces a positive result if the first number is bigger, zero if the two numbers are
equal, and a negative result if the second number is bigger; this is a simpler implementation than
code that does several if/else statements to compare the values. A common mistake was to have
“int*” or “const int*” arguments, which does not agree with how stack.h declares the types.

You might also like