Skip to main content

Module Quiz

Complete this quiz after finishing all concept and practice pages. Treat it closed-book; check answers after finishing.

Current Module Questions

Question 1: Hex Fluency

How many bits fit in two hex digits? How many decimal values does a single byte span as unsigned?

Answer: Two hex digits = 8 bits = 1 byte. Unsigned range is 0 to 255 inclusive (256 values).

Question 2: Two's Complement

Give the 8-bit two's-complement representation of -1 and of -128, and explain why the second is "the negative without a positive."

Answer: -1 is 0xFF (1111 1111). -128 is 0x80 (1000 0000). +128 would need 9 bits to represent (0 1000 0000), so an int8_t can hold -128 but not +128. This asymmetry is why INT_MIN == -INT_MIN can be true as a bit pattern and why -INT_MIN is undefined behavior as an expression.

Question 3: IEEE 754 Decoding

Decode the 32-bit pattern 0x40490FDB as an IEEE 754 float.

Answer: Sign = 0. Exponent field = 10000000 = 128, so actual exponent = 128 - 127 = 1. Mantissa = 1.10010010000111111011011_2. Value = 1.5707963... * 2^1 = 3.1415927..., approximately pi.

Question 4: Undefined Behavior on Signed Overflow

Predict the output. What does the C standard say?

int x = INT_MAX;
if (x + 1 < x) puts("overflowed");
else puts("did not");

Answer: Signed overflow is undefined behavior. A conforming compiler may print either line. Modern optimizers often replace x + 1 < x with false, so "did not" is common output, but the program cannot be relied on. Fix by using unsigned or by checking x > INT_MAX - 1 before adding.

Question 5: Pointer Arithmetic Prediction

Predict the output on a typical x86-64 system:

int a[5] = {10, 20, 30, 40, 50};
int *p = a + 2;
printf("%d %d %d\n", *p, p[-1], *(p + 2));

Answer: 30 20 50. p points to a[2]. p[-1] == *(p - 1) == a[1] == 20. *(p + 2) == a[4] == 50.

Question 6: Type-Scaled Arithmetic

Given double *d; with d == 0x1000, what is the address value of d + 3? What about (char *)d + 3?

Answer: d + 3 has address 0x1000 + 3 * sizeof(double) = 0x1000 + 24 = 0x1018. (char *)d + 3 is 0x1003 because char has sizeof == 1. The pointer type determines the byte step.

Question 7: Dangerous Cast

Predict the bytes printed on a little-endian machine:

uint32_t x = 0xAABBCCDD;
unsigned char *p = (unsigned char *)&x;
printf("%02x %02x %02x %02x\n", p[0], p[1], p[2], p[3]);

Answer: dd cc bb aa. Little-endian stores the least-significant byte at the lowest address.

Question 8: Segment Placement

Where do each of these live (stack, heap, .data, .bss, .rodata)?

const char *msg = "hello";
int g_init = 5;
static int g_zero;
int main(void) {
int local;
int *p = malloc(4);
return 0;
}

Answer: msg is in .data (initialized global). "hello" is in .rodata. g_init is in .data. g_zero is in .bss (zero-initialized static). local is on the stack. *p points into the heap; p itself is on the stack.

Question 9: Stack-Frame Tracing

Briefly describe what the x86-64 function prologue push rbp; mov rbp, rsp; sub rsp, 32 does in words.

Answer: It saves the caller's frame-pointer register on the stack, sets the current frame pointer to the current stack pointer, and then reserves 32 bytes of local-variable space by decreasing the stack pointer.

Question 10: Heap Discipline

Is there a bug in this code, and if so, what kind?

char *p = malloc(32);
if (p != NULL) strcpy(p, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJ");
free(p);

Answer: Yes: a heap-buffer overflow. strcpy writes 37 bytes (including \0) into a 32-byte allocation. ASan catches this immediately.

Question 11: Use-After-Free

Which line is the use-after-free?

char *s = malloc(16);
strcpy(s, "hello");
free(s);
printf("%s\n", s);

Answer: The printf on line 4 dereferences s after s has been freed. Fix by not reading s after free, or by setting s = NULL; immediately after free and checking before use.

Question 12: Struct Layout Prediction

On x86-64 with default alignment, what is sizeof(struct S)?

struct S { char c; int i; char d; };

Answer: 12. c at offset 0 (1 byte), 3 bytes padding, i at offset 4 (4 bytes), d at offset 8 (1 byte), 3 bytes trailing padding so the struct aligns to 4 on array next element. Total = 12.

Question 13: Endianness on the Wire

On a little-endian host, uint32_t x = 0x01020304; write(fd, &x, 4);. Another machine reads the 4 bytes into its own uint32_t y with read. If that machine is big-endian, what does it see in y?

Answer: It sees 0x04030201. The wire bytes were 04 03 02 01 (low address first, little-endian order). The big-endian reader treats the first byte as the most significant, giving 0x04030201. The fix is to use htonl / ntohl or explicit byte order on both ends.

Question 14: Alignment vs Packing

What is the risk of __attribute__((packed)) on a struct with an int field followed by a double field?

Answer: The double loses its natural 8-byte alignment. On architectures that trap on misaligned accesses, reading or writing that field can crash (SIGBUS) or be served by a very slow software-emulated path. Even on x86-64, sanitizers and some compiler passes flag unaligned access as undefined behavior.

Question 15: Tricky Cast and Alignment Prediction

Predict whether this compiles clean and what it prints on a typical Linux x86-64 system:

char buf[5] = { 0x78, 0x56, 0x34, 0x12, 0x00 };
uint32_t v;
memcpy(&v, buf, 4);
printf("0x%08x\n", v);

Answer: Prints 0x12345678. memcpy is the safe way to reinterpret bytes regardless of alignment. Note that uint32_t *p = (uint32_t *)buf; would be undefined behavior because buf may not satisfy uint32_t alignment and because of strict-aliasing rules. memcpy is the portable fix.

Interleaved Review Questions

Prior Module Question 1 (S1 Module 3: Probability)

What does linearity of expectation say, and why does it not require independence?

Answer: E[X + Y] = E[X] + E[Y] for any random variables X and Y. Expectation is a linear functional, so the identity follows from the definition of expectation regardless of whether X and Y are independent.

Prior Module Question 2 (S2 Module 1: Algorithm Analysis)

Solve T(n) = 2 T(n/2) + n using the Master Theorem.

Answer: a = 2, b = 2, n^(log_b a) = n, and f(n) = Theta(n). Case 2 applies, giving T(n) = Theta(n log n).

Prior Module Question 3 (S3 Module 2: Software Design)

What is the core idea of the Single Responsibility Principle in one sentence?

Answer: A module should have exactly one reason to change, which usually means it should be responsible for one stakeholder-facing concern.

Prior Module Question 4 (S4 Module 1: C Programming Fundamentals)

What does static mean for a file-scope variable versus a function-scope variable in C?

Answer: At file scope, static limits the identifier's linkage to this translation unit (other files cannot see it). At function scope, static gives the local variable static storage duration (lifetime equals the program, not the call) while keeping its identifier scoped to the function.

Prior Module Question 5 (S1 Module 1: Proof Techniques)

Prove by induction that sum_{i=1}^n i = n(n+1)/2.

Answer: Base n=1: 1 = 1*2/2. Inductive step: assume sum_{i=1}^k i = k(k+1)/2. Then sum_{i=1}^{k+1} i = k(k+1)/2 + (k+1) = (k+1)(k+2)/2, which is the claim for k+1. QED.

Self-Assessment and Remediation

Mastery Level (90-100% correct):

  • Ready to advance to Module 3 (Computer Organization & Architecture) with confidence.

Proficient Level (75-89% correct):

  • Review the missed concept pages and redo two problems of each missed type. Pay particular attention to pointer-arithmetic prediction and struct layout if those were the gaps.

Developing Level (60-74% correct):

  • Rework Practice 2 (pointers) and Practice 3 (memory errors / tools). Run at least three programs under ASan and at least one under Valgrind. The weakness is almost certainly under-using the tools.

Insufficient Level (<60% correct):

  • Return to Cluster 1 and Cluster 2 concept pages. Most likely issue: reading pointer code without drawing memory diagrams. Restart the concept sequence with a blank paper next to you and draw every pointer before asking "what is this code doing?"