Instructions: Print your name in the space provided below. This examination is closed book and closed notes, aside from the permitted one-page formula sheet. No calculators or other electronic devices may be used. The use of any such device will be interpreted as an indication that you are finished with the test and your test form will be collected immediately. Answer each question in the space provided. If you need to continue an answer onto the back of a page, clearly indicate that and label the continuation with the question number. If you want partial credit, justify your answers, even when justification is not explicitly required. There are 5 questions, some with multiple parts, priced as marked. The maximum score is 100. When you have completed the test, sign the pledge at the bottom of this page and turn in the test. If you brought a fact sheet to the test, write your name on it and turn it in with the test. Note that either failing to return this test, or discussing its content with a student who has not taken it is a violation of the Honor Code. Do not start the test until instructed to do so! Name printed VT ID (90XXXXXXX) printed Pledge: On my honor, I have neither given nor received unauthorized aid on this examination. signed B 1
xkcd.com B 2
1. [20 points] In the following, you will see a series of pointer type questions. Infer the type of ptr based on the variable p and its type. Please write down the type of variable ptr right next to each code segment. a) [4 points] int **p = ; *p = ptr; int * b) [4 points] int p[3] = ; *ptr = &p[1]; int ** c) [4 points] char **p = ; p[1] = *ptr; char ** d) [4 points] char p[3] =..; **ptr = p[1]; char ** e) [4 points] char *p =..; ptr = &p[1]; char * B 3
2. [20 points] Perform the indicated conversions. You must show your work in order to receive credit! a) Convert from 8-bit 2's complement (i.e., signed) form to base-10: 1101 0101 Make it positive and then expand the positional notation: 0010 1010 + 1 0010 1011 2**5 + 2**3 + 2**1 + 2**0 = 43 Negate to get -43. b) Convert from 8-bit unsigned form to base-10: 1011 1011 2**7 + 2**5 + 2**4 + 2**3 + 2**1 + 2**0 = 187 c) Convert 0x42 hexadecimal to base-10: 4*16**1 + 2*16**0 = 66 d) Convert 42 base-10 to hexadecimal: Repeatedly divide by 16, keeping the remainder. The most significant digit is the result of the last division/modulus. 42 / 16 = 2 R 10 (or A) 2 / 16 = 0 R 2 So 42 10 2A 16 B 4
3. [12 points] Write a solution for the function below and explain the logic that makes it work. The explanation will be worth more than the solution. You may not use any loops (while, for, do-while), selection (if, switch), or casts, and you may not write any constants that are more than one byte wide. Don t worry about the number of operations. Is x a multiple of d? Or is x % d == 0? Returns 1 when x is a multiple of d, 0 otherwise. You may assume d is a power of 2, from 1 (2^0) to 2147483648 (2^31). is_multiple(0, 4) = 1 is_multiple(4, 4) = 1 is_multiple(5, 4) = 0 is_multiple(8, 4) = 1 Legal ops:! ~ & ^ + - << >> uint8_t is_multiple(uint32_t x, uint32_t d) { x is a multiple of d if it has log 2 (d) 0s in the least significant bits. So x is a multiple of 4 if the bottom 2 bits are 0, and x is multiple of 8 if the bottom 3 bits are 0, etc. So we need a mask that isolates the least significant bits based on d. To do that: d = 4, should yield a mask of 0x3 d = 8, should yield a mask of 0x7 d = 16, should yield a mask of 0x15 Subtracting d by 1 will give us that mask: uint32_t mask = d + ~0; Then check if the bottom bits are all 0 using &. The! will give us 0 for any non-zero value (non-multiple), and 1 for a result with all zeros (multiple): } return!(x & mask); B 5
4. [18 points] Consider the following C function. Creates a string with the length prepended at the beginning. Intended behavior: Create a string like so: char * s = make_str_with_length("hello World!", 12); s still works with the standard library and printf(), and is '\0' terminated. For example, to print "Hello World!" (whose length is 12): printf("%s\n", s); But to get the length you don't need to call strlen(), rather it's saved before the characters: size_t len = *((size_t *) (s - sizeof(size_t))); char * make_str_with_length(char *str, size_t len) { Allocate space for: * A size_t that represents the length, 8 bytes for x86-64. * The characters in the string, or len bytes. * Plus 1 extra byte for the '\0' char data[sizeof(size_t) + len + 1]; Point tmp at data, so we can easily save the string length. size_t * tmp = (size_t*) data; Save the string length, which excludes the null terminator. *tmp = len; Move the pointer forward to the location of the first character. char * new = (char *) (tmp + 1); Copy the characters over using strncpy. strncpy(new, str, len + 1); } Return the string. return new; a. [6 points] There is at least one bug in this function. Analyze the code and determine the location of the error(s). It takes a few steps, but this function returns pointer to the local array (variable) data. Once the function ends the space for the array is deallocated. It's also kind of a questionable use of a variable length array, since stack space is limited and a string could be quite long. B 6
b. [6 points] What is the effect of executing the function before correcting any bugs? Returning a pointer to a local variable is undefined behavior, so the behavior of the program can be unpredictable. It's possible the program will crash, but more commonly, the memory that used to be associated with the array is used for other things. For example, let's say we have another function in our program: void mangle_values() { char data[1000]; }... for (size_t x = 0; x < 1000; x++) { if (isupper(data[x])) data[x] = tolower(data[x]); else if (islower(data[x])) data[x] = toupper(data[x]); } In main() char * s = make_str_with_length("hello World!", 12); printf("%s\n", s); mangle_values(); printf("%s\n", s); This *may* print: Hello World! hello world! So the string data can be changed without directly modifying s. The length component of the string is also likely messed up. c. [6 points] How could you fix the bug(s) while keeping the intended functionality? If we use malloc() instead, we no longer have a pointer to a local variable: char * data = malloc(sizeof(size_t) + len + 1); Check if data is NULL, etc... B 7
5. [30 points] Consider the following C function: void mystery(char * s, size_t len) { char *b, *e; uint64_t table[32] = {0}; uint8_t * table_ptr = (uint8_t *) table; *(table_ptr + 9) = 1; *(table_ptr + 10) = 1; *(table_ptr + 11) = 1; *(table_ptr + 12) = 1; *(table_ptr + 13) = 1; *(table_ptr + 32) = 1; for (b = s; *(table_ptr + *b); b++); for (e = s + len - 1; *(table_ptr + *e); e--); a b c } while (b!= e) *s++ = *b++; *s = '\0'; d For the questions below, assume the function has been called with input: mystery("\thello World!\n", 14). a. [6 points] Why is table_ptr being used instead of table? What is the significance of the offsets being used with table_ptr? (1) We want to have byte level access to the table. We only access it byte by byte, and never access uint64_t by uint64_t. (2) The offsets represent the spacing/formatting characters of the ASCII code, 9 is '\t', 10 is '\n', 11 is '\v' (vertical tab), 12 is "\f" (feed) 13 is '\r' (carriage return), while 32 is ' '(space). See: http:www.cplusplus.com/reference/cctype/isspace/ All of the spacing characters get set to 1, while every other byte (character) in the table is set to 0. We use this table in the loops below to test if a character in s is a spacing character. For example, table_ptr['a'] is 0, while table_ptr[' '] is 1, etc. b. [6 points] After the first for loop finishes executing, what is the target of b? Be specific, vague answers will receive no credit. If necessary, e.g. the character pointed to isn't unique, provide the character's index. The loop (and b) will start at the beginning of s and skip any spacing characters, stopping when it finds a non-spacing character. The target of b is the character 'H'. B 8
c. [6 points] After the second for loop finishes executing, what is the target of e? Be specific, vague answers will receive no credit. If necessary, e.g. the character pointed to isn't unique, provide the character's index. The loop (and e) will start at the end of s and skip any spacing characters, stopping when it finds a non-spacing character. The target of e is the character '!'. d. [6 points] Given the targets of b and e from your answers in parts b and c, what effect does executing the while loop have on parameter s? Show the contents of s after executing the while loop below. Be specific, vague answers will receive no credit. The final loop shifts all of the characters in s towards the beginning, so the first non-spacing character is now the first character of s, and so on. s would contain: Hello World! If you tried to print s immediately after the loop, there would be some trailing characters until we 0 terminate s on the next line. e. [6 points] In plain English, explain what the function mystery() accomplishes. This should not be a reiteration of each line, and should be specific, e.g. this function sums the numbers from 1-100. The mystery function strips any spacing characters off both ends of a string. B 9
The ASCII table, 'A' is 0x41 or 65. Found in row 4, column 1 below. 0 1 2 3 4 5 6 7 8 9 A B C D E F ---------------------------------------------------------------- 0 NUL SOH STX ETX EOT ENQ ACK BEL BS TAB LF VT FF CR SO SI 1 DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US 2 SP! " # $ % & ' ( ) * +, -. / 3 0 1 2 3 4 5 6 7 8 9 : ; < = >? 4 @ A B C D E F G H I J K L M N O 5 P Q R S T U V W X Y Z [ \ ] ^ _ 6 ` a b c d e f g h i j k l m n o 7 p q r s t u v w x y z { } ~ DEL Scratch Space Can be used as extra space or scratch space for any question. B 10