C Final Exam Cheat Sheet

Copy-paste templates from every lab · All structures ready to go

Boilerplate — always start here
Standard main with argc/argv + file open
// Always include these — add math.h if you use sqrt/pow/etc.
#include <stdio.h>   // printf, scanf, fopen, fclose, fprintf, fscanf, FILE
#include <stdlib.h>  // malloc, free, EXIT_SUCCESS, EXIT_FAILURE
#include <string.h>  // strlen, strcpy, strcmp, strcat
#include <math.h>    // sqrt, pow  (link with -lm if needed)
#include "myheader.h" // your own header — use quotes, not angle brackets

// argc = number of arguments including program name
// argv[0] = program name, argv[1] = first user argument, etc.
int main(int argc, char *argv[]) {

    // argc != 2 means user gave 0 or 2+ args — we expect exactly 1 (the filename)
    if (argc != 2) {
        printf("Usage: %s filename\n", argv[0]); // argv[0] is the program name
        return 1;  // non-zero = error exit
    }

    // FILE* is the type for a file handle.
    // fopen returns NULL if the file can't be opened.
    // "r" = read text  "w" = write text  "rb" = read binary  "wb" = write binary
    FILE *fp;
    if ((fp = fopen(argv[1], "r")) == NULL) {
        printf("%s: Unable to open \"%s\"\n", argv[0], argv[1]);
        return 1;
    }

    // ... do work with fp ...

    fclose(fp);  // always close the file when done
    return 0;    // 0 = success (same as EXIT_SUCCESS)
}
Header file template (.h)
// Include guard — prevents double-inclusion if multiple .c files include this header
#ifndef MYHEADER_H   // if not defined yet...
#define MYHEADER_H   // ...define it (so next include is skipped)

#include <stdio.h>   // needed here if you use FILE* in declarations

#define MAX_SIZE 100  // constant — use this instead of magic numbers

// typedef lets you rename a type so you only change it in one place
typedef int ItemType;
#define ITEM_FORMAT "%d"   // matching format string — change both together

// Function declarations (prototypes) — no body, just the signature + semicolon
void myFunc(int x);

#endif /* MYHEADER_H */  // closes the #ifndef block
Implementation file template (.c)
#include <stdio.h>
#include <stdlib.h>
#include "myheader.h"   // include the matching .h for this .c file

// Function DEFINITION (has a body) — must match the prototype in the .h exactly
void myFunc(int x) {
    printf("%d\n", x);
}
Rule: declarations in .h — definitions in .c — #include the .h in both
Strings & String Arrays — Labs 1, 2, 4 patterns (high exam probability)
scanf number + unit string — Lab 2 / Midterm Q2 exact pattern
// Read a double AND a short string in ONE scanf call
// e.g. user types "24.5F" or "100^" — scanf splits at the number boundary
// %10s reads up to 10 non-whitespace chars into line[]
double inputVal;
char   line[11];   // 10 chars + null terminator
char   type;

scanf("%lf%10s", &inputVal, line);  // no & before line — array decays to pointer
type = line[0];   // first character is the unit/operator

// Then dispatch with if-else on the character:
if      (type == 'F' || type == 'f') { /* Fahrenheit */ }
else if (type == 'C' || type == 'c') { /* Celsius   */ }
else if (type == 'K' || type == 'k') { /* Kelvin    */ }
else                                    { printf("Unknown unit '%c'\n", type); }
This exact pattern appeared in Midterm Q2 (power/remainder). Lab 2 is temperature conversion using the same scanf structure — very likely on the final.
String pointer as result variable — Lab 1 grade lookup pattern
// const char* points to a string literal — no array needed
// Set a default, then overwrite it in if-else chain
// The pointer just changes where it points — no copying
const char *grade = "F";   // default — set before the if-else

if      (percent >= 90.0) grade = "A+";
else if (percent >= 85.0) grade = "A";
else if (percent >= 80.0) grade = "A-";
else if (percent >= 77.0) grade = "B+";
else if (percent >= 73.0) grade = "B";
else if (percent >= 70.0) grade = "B-";
// ... continue down to F ...

printf("Letter grade: %s\n", grade);  // %s prints the string the pointer points to

// Also: input validation — always check scanf and range
if (scanf("%lf", &percent) != 1) { fprintf(stderr, "Invalid input\n"); return 1; }
if (percent < 0.0 || percent > 100.0) { fprintf(stderr, "Out of range\n"); return 1; }
Array of string literals — Lab 4 pattern
// char *names[] = array of pointers, each pointing to a string literal
// Access with names[i] — same as any array
const char *kSingleNames[] = {"Twenty", "Ten", "Five", "Toonie", "Loonie"};
const char *kPluralNames[] = {"Twenties", "Tens", "Fives", "Toonies", "Loonies"};

int count = 3;
const char *label = (count == 1) ? kSingleNames[0] : kPluralNames[0]; // ternary
printf("%d %s\n", count, label);   // "3 Twenties"

// Print all with a loop:
for (int i = 0; i < 5; i++)
    printf("%s\n", kSingleNames[i]);
sizeof array count + formatted printf widths — Lab 4 / Lab 3
// sizeof(arr)/sizeof(arr[0]) = number of elements — works for any type
// Only works on the ACTUAL array, not a pointer to it
const double kValues[] = {20.0, 10.0, 5.0, 2.0, 1.0};
size_t n = sizeof(kValues) / sizeof(kValues[0]);  // n = 5
for (size_t i = 0; i < n; i++)
    printf("%.2f\n", kValues[i]);

// Formatted column widths (Lab 3 table output):
// %8.2f = field width 8, 2 decimal places — right-aligned by default
printf("    Time    Height   Velocity\n");       // header
printf("%8.2f  %8.1f   %8.2f\n", time, height, vel);  // data rows

// snprintf — safe way to write a formatted string into a char array:
// snprintf(dest, sizeof(dest), format, ...)
// NEVER use sprintf — it can overflow; always use snprintf
char buf[50];
snprintf(buf, sizeof(buf), "%s_%d", prefix, num);
File I/O — text and binary
Text file read loop (Lab 5 pattern)
// fscanf reads one formatted value at a time from the file
// returns the NUMBER of items successfully read (1 here)
// returns EOF (-1) at end of file or on error — so check == 1, not != EOF
double x, sum = 0.0, sumsq = 0.0;
int count = 0;

while (fscanf(fp, "%lf", &x) == 1) {  // %lf for double (not %f!)
    sum   += x;         // running total
    sumsq += x * x;     // sum of squares (for standard deviation)
    count++;
}
fclose(fp);  // close after loop exits (EOF reached)
Check fscanf returns 1 — not just non-zero
Binary file read with fread (Lab 6 pattern)
// "rb" = read binary — MUST use this for binary files, not "r"
FILE *fp = fopen(argv[1], "rb");

// Allocate a block of memory: n elements each sizeof(int) bytes
int *arr = (int *) malloc(n * sizeof(int));

// fread(destination, size_of_one_element, how_many, file)
// returns number of elements actually read — compare to expected n
size_t got = fread(arr, sizeof(arr[0]), n, fp);

if (got != (size_t)n) {  // cast n to size_t to avoid signed/unsigned warning
    if (feof(fp))          // hit end of file before reading n items
        printf("Unexpected EOF\n");
    else if (ferror(fp))   // actual read error (disk problem etc.)
        printf("Read error\n");
    fclose(fp);
    free(arr);   // free before returning on error — don't leak memory
    return 1;
}
fclose(fp);
Write to output file (Lab 5 pattern)
// Build a new filename by concatenating PREFIX + input filename
// strlen gives length WITHOUT the null terminator, so +1 for '\0'
int nChar = strlen(PREFIX) + strlen(argv[1]) + 1;

// malloc allocates nChar bytes on the heap — holds the combined string
char *outName = (char *) malloc(nChar);

// snprintf writes at most nChar chars into outName (safe — won't overflow)
snprintf(outName, nChar, "%s%s", PREFIX, argv[1]);

FILE *outFile;
if ((outFile = fopen(outName, "w")) == NULL) {  // "w" creates or overwrites
    printf("Unable to open \"%s\"\n", outName);
    free(outName);
    return 1;
}
// fprintf is like printf but writes to a file instead of stdout
fprintf(outFile, "%d Values, Mean = %g\n", count, mean);
fclose(outFile);
free(outName);  // free the heap string when done
sscanf to parse a command-line argument
// argv[] is always a string — use sscanf to convert it to a number
// sscanf reads FROM a string (not a file or stdin)
// returns number of items successfully parsed — check == 1
int n;
if (sscanf(argv[2], "%d", &n) != 1) {
    printf("Bad number: %s\n", argv[2]);
    return 1;
}

// stderr = error stream — shows even if stdout is redirected to a file
fprintf(stderr, "Error: %s\n", msg);
fprintf(stdout, ...) == printf(...) · fprintf(stderr, ...) for errors
Dynamic Memory — malloc / realloc / free
malloc an array
// malloc(bytes) — allocates memory on the HEAP (survives the function call)
// sizeof(int) = number of bytes in one int (usually 4)
// cast (int*) tells the compiler what type the returned void* is
int *arr = (int *) malloc(n * sizeof(int));

// ALWAYS check if malloc succeeded — returns NULL if out of memory
if (arr == NULL) {
    fprintf(stderr, "malloc failed\n");
    return EXIT_FAILURE;
}

// use arr[i] exactly like a normal array...

free(arr);   // MUST free when done — each malloc needs exactly one free
malloc a single struct / item
// Allocating one struct — sizeof(Node) gives total size of the struct
Node *p = (Node *) malloc(sizeof(Node));
if (p == NULL) { return NULL; }  // propagate failure up to caller

// Access struct fields via arrow -> when you have a POINTER to a struct
p->value = 42;
p->next  = NULL;  // always set next to NULL for the last node in a list

// calloc(count, size) — like malloc but also ZEROES the memory
Node *p2 = (Node *) calloc(1, sizeof(Node));
Every malloc needs exactly one free. Never free stack variables.
Structs — typedef, access, pass by pointer (Lab 9 / Week 10)
Define and use a struct
// typedef struct { ... } Name;  — creates a new type called Name
// Without typedef you'd have to write "struct Name" everywhere
typedef struct {
    int    size;              // how many elements are in use
    double data[MAX_SIZE];    // fixed-size array inside the struct
} Vector;

// Declare on the stack (no malloc needed)
Vector v1 = {3, {1.0, 2.0, 3.0}};  // initializer list

// Dot (.) accesses fields when you have a VARIABLE (not a pointer)
v1.size;
v1.data[0];

// Pass by VALUE — function gets a COPY, changes don't affect original
printVector(v1, stdout);

// Pass by POINTER (&v1) — function can modify the original struct
updateVector(&v1);
Pointer to struct — arrow notation
typedef struct {
    int  age;
    char name[100];
} Player;

Player  p   = {20, "Alice"};
Player *ptr = &p;   // ptr is a pointer TO p (holds p's address)

// Arrow (->) = dereference pointer + access field in one step
// Use -> when you have a POINTER to a struct
ptr->age  = 21;
ptr->name[0];

// This is identical to the arrow above — just harder to read
(*ptr).age = 21;   // (*ptr) dereferences first, then .age accesses field
Use dot (.) when you have a variable · use arrow (→) when you have a pointer
Lab 9 — Vector struct with operations (full pattern)
// ── vectorMath.h ───────────────────────────────────────────────────────
#define MAX_VECTOR_SIZE 20
typedef struct { int size; double data[MAX_VECTOR_SIZE]; } Vector;

// Returns pointer to result on success, NULL on error (size mismatch / div by 0)
// v1 and v2 passed by VALUE (copies) — result passed by POINTER (written to)
Vector *vectorMath(const Vector v1, const Vector v2, Vector *result, char op);
void    printVector(const Vector v, FILE *stream);

// ── vectorMath.c ───────────────────────────────────────────────────────
Vector *vectorMath(const Vector v1, const Vector v2, Vector *result, char op) {
    if (v1.size != v2.size) return NULL;   // can't operate on different sizes
    result->size = v1.size;                  // set output size to match inputs
    switch (op) {                             // dispatch on operator character
        case '+': for (int i=0; i<v1.size; i++) result->data[i] = v1.data[i] + v2.data[i]; break;
        case '-': for (int i=0; i<v1.size; i++) result->data[i] = v1.data[i] - v2.data[i]; break;
        case '*': for (int i=0; i<v1.size; i++) result->data[i] = v1.data[i] * v2.data[i]; break;
        case '/': for (int i=0; i<v1.size; i++) {
            if (v2.data[i] == 0) return NULL;   // avoid division by zero
            result->data[i] = v1.data[i] / v2.data[i];
        } break;
        default: return NULL;    // unknown operator
    }
    return result;  // return pointer to the filled result struct
}
void printVector(const Vector v, FILE *stream) {
    const double *p = v.data;  // p starts at first element
    for (int i=0; i<v.size; i++) fprintf(stream, "%.4f ", *p++);  // print then advance
    fprintf(stream, "\n");
}
enum — named integer constants (quiz4 / Week 10 pattern)
typedef enum + string name array (quiz4 exact pattern)
// enum assigns integer values automatically: HR=0, SALES=1, IT=2, FINANCE=3
// Use typedef so you can write "Department" not "enum Department" everywhere
typedef enum {
    HR,
    SALES,
    IT,
    FINANCE
} Department;

// Parallel string array: index matches the enum value
// departmentName[SALES] == "SALES"  (SALES == 1, so index 1)
const char *departmentName[] = {"HR", "SALES", "IT", "FINANCE"};

// Struct that uses the enum as a field type
typedef struct {
    int        id;
    char       name[50];
    Department dept;       // stored as int, but readable as SALES etc.
} Employee;

// Initialize and print
Employee e;
e.id   = 356;
e.dept = SALES;
snprintf(e.name, sizeof(e.name), "Greg");  // safe string copy into struct field
printf("%s %d %s\n", e.name, e.id, departmentName[e.dept]);
// Output: Greg 356 SALES
struct — pass by value vs pass by pointer (Week 10 trap)
typedef struct { int level; char name[100]; } Player;

// BY VALUE — function gets a COPY — changes do NOT affect original
void levelUpCopy(Player p) {
    p.level = 99;   // only changes the local copy — caller never sees this
}

// BY POINTER — function gets the ADDRESS — changes DO affect original
void levelUpReal(Player *p) {
    p->level = 99;  // modifies the actual struct through the pointer
}

Player hero = {1, "David"};
levelUpCopy(hero);           // hero.level is still 1
levelUpReal(&hero);          // hero.level is now 99
printf("%d\n", hero.level);  // prints 99
// Rule: if the function needs to MODIFY the struct, pass &struct (pointer)
//       if it only READS, you can pass by value (but pointer is faster for large structs)
Pointers — notation, swap, bytes (Lab 7)
Pointer basics
int  x = 5;
int *p = &x;   // & = "address of" — p holds the memory address of x
*p = 10;       // * = "dereference" — go to address in p and write 10 there → changes x

// Arrays and pointers are closely related:
// arr by itself IS a pointer to arr[0]
// arr[i] is exactly equivalent to *(arr + i)
int arr[] = {1,2,3};
int *ptr = arr;       // ptr points at arr[0]
*ptr++;               // reads *ptr (value 1), THEN moves ptr forward one int
*(ptr+1);             // peek at next element without moving ptr
swap() — the canonical pattern
// Must take POINTERS so it can modify the caller's variables
// If you took int x, int y (by value), you'd only swap local copies
void swap(int *x, int *y) {
    int tmp = *x;  // save value at x
    *x = *y;       // write y's value to x's address
    *y = tmp;      // write saved value to y's address
}

// Two equivalent ways to call swap on adjacent elements:
swap(&arr[i], &arr[j]);          // array notation — &arr[i] = address of arr[i]
swap(arr + i, arr + i + 1);     // pointer notation — arr+i points to element i
Print array with pointer post-increment
// const int* = pointer to a constant int — you can move the pointer
// but you can't change what it points to (good for read-only params)
void printVector(const int *vec, int n) {
    const int *p = vec;     // local copy of the pointer so we can advance it
    for (int i = 0; i < n; i++)
        printf("%d ", *p++);  // print value at p, THEN move p to next element
    printf("\n");
}
Print raw bytes (cast to unsigned char*)
// Cast int* to unsigned char* so we can read ONE BYTE at a time
// sizeof(int) = 4, so each int is printed as 4 separate byte values
void printBytes(const int *vec, int n) {
    const int *p = vec;
    for (int i = 0; i < n; i++) {
        // reinterpret the 4 bytes of this int as individual bytes
        const unsigned char *b = (const unsigned char *)p++;
        for (int j = 0; j < (int)sizeof(int); j++)
            printf("%hhu ", *b++);  // %hhu = unsigned char
        printf(" ");   // extra space between integers
    }
    printf("\n");
}
// REVERSE endian: start at LAST byte and walk backward
// p + sizeof(int) - 1 points at the final byte of the current int
const unsigned char *b = (const unsigned char *)p + sizeof(int) - 1;
for (int j = 0; j < (int)sizeof(int); j++) printf("%hhu ", *b--);
2D Arrays — declaration, access, pointer notation, column sums
Declare and access a 2D array
// int array[ROWS][COLS] — row-major: rows stored consecutively in memory
int arr[3][2] = {{10,20},
                 {30,40},
                 {50,60}};

// Three equivalent ways to access element at row i, col j:
arr[i][j];                  // standard — use this
*(arr[i] + j);              // arr[i] is pointer to row i
*(*(arr + i) + j);          // fully dereferenced pointer form

// Nested loop to print all elements:
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 2; j++)
        printf("%d ", arr[i][j]);
    printf("\n");
}
Sum each column (classic exam pattern)
// 2 rows, 3 cols — sum DOWN each column
int arr[2][3] = {{2,8,1},
                  {5,6,4}};

int colSums[3] = {0};   // one sum per column, init to 0

// Outer loop = columns, inner = rows
// arr[row][col] → arr[j][i] when i=col, j=row
for (int i = 0; i < 3; i++) {          // i = column index
    for (int j = 0; j < 2; j++) {      // j = row index
        colSums[i] += arr[j][i];         // add row j, col i to col total
    }
    printf("Col %d sum: %d\n", i, colSums[i]);
}
// Output: Col 0: 7  Col 1: 14  Col 2: 5
Recursion — always: base case first (Lab 8)
Recursive printVector (Lab 8)
// PATTERN: base case → do one thing → recurse on smaller problem
// vec+1 advances the pointer past the first element (smaller array)
// n-1 reduces the count to match
void printVector(const int *vec, int n) {
    // BASE CASE: nothing left to print — print newline and stop
    if (n == 0) { printf("\n"); return; }

    printf("%d ", *vec);            // print first element
    printVector(vec + 1, n - 1);   // recurse on rest (vec+1 skips first element)
}
Recursive reverseVector (Lab 8)
// TWO POINTER approach: swap outermost elements, then recurse inward
// p1 = left index, p2 = right index
// Call initially as: reverseVector(arr, 0, N-1);
void reverseVector(int *vec, int p1, int p2) {
    // BASE CASE: pointers met or crossed — nothing left to swap
    if (p1 >= p2) return;

    // Swap elements at p1 and p2
    int tmp  = vec[p1];
    vec[p1]  = vec[p2];
    vec[p2]  = tmp;

    reverseVector(vec, p1 + 1, p2 - 1);  // move both pointers inward
}
Recursive factorial
// Classic recursion: factorial(5) = 5 * factorial(4) = 5*4*3*2*1 = 120
int factorial(int n) {
    // BASE CASE: factorial(0) = factorial(1) = 1
    if (n <= 1) return 1;
    return n * factorial(n - 1);  // recurse with smaller n
}
Bit manipulation / binary print (Week 11)
// Fill buffer from right to left: check lowest bit each time, then shift right
void printBinary(int num) {
    char buf[33];
    buf[32] = '\0';  // null-terminate the string at position 32
    for (int i = 31; i >= 0; i--) {
        buf[i] = (num & 1) ? '1' : '0';  // & 1 isolates the lowest bit
        num >>= 1;   // right-shift 1 bit (brings next bit into lowest position)
    }
    printf("%s\n", buf);
}
// Bit ops: & AND  | OR  ^ XOR  ~ NOT  << left-shift  >> right-shift
Stack — array-based (fixed size)
Full array stack (push / pop / peek / print)
#define MAX 100

// Array holds the data; top tracks the index of the topmost element
// top = -1 means the stack is empty (nothing has been pushed yet)
typedef struct {
    int array[MAX];
    int top;        // -1 = empty, 0 = one element, MAX-1 = full
} Stack;

// init: set top to -1 so all operations know the stack is empty
void init(Stack *s)  { s->top = -1; }

// push: increment top FIRST, then store — top points at the new element
void push(Stack *s, int a) {
    if (s->top == MAX - 1) { fprintf(stderr, "Stack full\n"); return; }
    s->array[++s->top] = a;  // ++top increments first, then uses new value as index
}

// pop: read the top element, THEN decrement top (removes it)
// Returns -1 as error sentinel if empty — check before trusting the result
int pop(Stack *s) {
    if (s->top == -1) { fprintf(stderr, "Stack empty\n"); return -1; }
    return s->array[s->top--];  // read array[top], THEN decrement top
}

// peek: read top WITHOUT removing — same empty check as pop
int peek(Stack *s) {
    if (s->top == -1) { fprintf(stderr, "Stack empty\n"); return -1; }
    return s->array[s->top];  // no top-- here — just look
}

// printStack: index from 0 (bottom) to top (newest element)
void printStack(Stack *s) {
    for (int i = 0; i <= s->top; i++) printf("%d ", s->array[i]);
    printf("\n");
}

// Usage: declare on the stack (no malloc), call init, then push/pop/peek
Stack s; init(&s);
push(&s, 5); push(&s, 7);
pop(&s);    peek(&s);
Stack — linked list (dynamic, no size limit)
Linked-list stack (Week 12 / linked list practice)
#include <stdbool.h>  // for bool, true, false

// Each node holds one value + a pointer to the node below it on the stack
// "struct node" self-reference lets next point to another Node
typedef struct node {
    int         item;
    struct node *next;  // pointer to node below this one (NULL if bottom)
} Node;

// Stack manages the chain: top = pointer to newest node, NULL = empty
typedef struct {
    int   size;
    Node *top;       // NULL = empty stack
} Stack;

// push: malloc a new node, wire it in at the top
// new node's next = old top → then top = new node
bool push(Stack *s, int val) {
    Node *n = (Node *) malloc(sizeof(Node));
    if (n == NULL) return false;  // malloc failed
    n->item = val;
    n->next = s->top;   // new node points DOWN to whatever was on top before
    s->top  = n;        // now the new node IS the top
    s->size++;
    return true;
}

// pop: save the top node, advance top to next, free the saved node
// Returns value via *out pointer so we can also return success/failure
bool pop(Stack *s, int *out) {
    if (s->top == NULL) return false;  // empty stack
    Node *tmp = s->top;     // save pointer so we can free it
    *out    = tmp->item;    // write value to caller's variable through pointer
    s->top  = tmp->next;    // advance top to the node that was below
    s->size--;
    free(tmp);             // free the removed node — tmp still holds the address
    return true;
}

// Walk the chain from top to bottom and print each node
void printStack(Stack *s) {
    Node *cur = s->top;
    while (cur != NULL) { printf("%d\n", cur->item); cur = cur->next; }
}

// Usage: initialize with size=0, top=NULL (no malloc needed for the Stack itself)
Stack s = {0, NULL};
push(&s, 5);
int v; pop(&s, &v);  // v is written by pop via the &v pointer
Queue — linked list with void* (Lab 10)
queue.h — types and declarations
// Change ItemType and ITEM_FORMAT here to change the queue's data type everywhere
typedef int ItemType;
#define ITEM_FORMAT "%d"
#define ITEM_PROMPT "an integer"

// Each node stores a GENERIC void* pointer — can point to any type
// This lets the queue work for int, double, struct, etc. without changing queue.c
// Caller is responsible for malloc-ing the item and casting back after dequeue
typedef struct listNode {
    struct listNode *next;
    void            *dataPtr;   // generic pointer — points to the actual item data
} ListNode;

// Queue tracks BOTH ends:
//   front = where items come OUT (dequeue)
//   rear  = where items go IN  (enqueue)
// This gives O(1) enqueue and dequeue
typedef struct {
    ListNode *front;  // oldest item (next to be removed)
    ListNode *rear;   // newest item (most recently added)
    int       size;
} Queue;

void *enqueue (Queue *q, void *item);   // returns item on success, NULL on malloc fail
void *dequeue (Queue *q);               // returns item pointer, NULL if empty
int   queueSize(const Queue q);
void  printQueue(const Queue q, FILE *stream);
queue.c — enqueue / dequeue implementations
// enqueue: malloc a new ListNode, attach it at the REAR
// The node wraps the item pointer — it does NOT copy the item data
void *enqueue(Queue *q, void *item) {
    ListNode *node = (ListNode *) malloc(sizeof(ListNode));
    if (node == NULL) return NULL;  // malloc failed — caller should check
    node->next    = NULL;           // new node is at the end — nothing after it
    node->dataPtr = item;           // store the pointer (not a copy of the data)
    if (q->size == 0) {
        q->front = q->rear = node;            // first ever item: both front AND rear point to it
    } else {
        q->rear->next = node; q->rear = node; // link after current rear, then update rear
    }
    q->size++;
    return item;  // return the original item so caller can confirm success
}

// dequeue: remove from the FRONT, free the ListNode, return the item pointer
// Caller must free the item pointer (they malloc-ed it, they free it)
void *dequeue(Queue *q) {
    if (q->size == 0) return NULL;  // empty — nothing to remove
    ListNode *old  = q->front;      // save pointer to front node
    void     *item = old->dataPtr;  // save item pointer before we free the node
    q->front = old->next;           // advance front to the next node
    q->size--;
    if (q->size == 0) q->rear = NULL;   // queue is now empty — clear rear too
    free(old);     // free the ListNode wrapper (NOT the item — caller frees that)
    return item;   // return item pointer so caller can use then free it
}

// printQueue: walk from front to rear, cast void* back to ItemType* to dereference
void printQueue(const Queue q, FILE *stream) {
    const ListNode *p = q.front;
    while (p != NULL) {
        fprintf(stream, ITEM_FORMAT "\n", *(ItemType *)p->dataPtr);  // cast void* → ItemType*
        p = p->next;
    }
}
queueMain.c — interactive menu pattern
// Init empty queue: front=NULL, rear=NULL, size=0
Queue q = {NULL, NULL, 0};

// ── ENQUEUE PATTERN ───────────────────────────────────────────────────
// Step 1: malloc the ITEM (not the node — queue.c handles the node)
// Step 2: read data into it via scanf
// Step 3: pass pointer to enqueue
ItemType *itemPtr = (ItemType *) malloc(sizeof(ItemType));  // alloc one item
scanf(ITEM_FORMAT, itemPtr);                                 // read value into it
enqueue(&q, itemPtr);                                       // queue stores the pointer

// ── DEQUEUE PATTERN ───────────────────────────────────────────────────
// dequeue returns void* — cast back to ItemType* to use it
// After using the value, YOU must free the item (you malloc-ed it)
ItemType *got = (ItemType *) dequeue(&q);       // remove from front
if (got != NULL) { printf(ITEM_FORMAT "\n", *got); free(got); }  // use then free
else printf("Queue empty\n");

// ── ENUM MENU PATTERN ─────────────────────────────────────────────────
// QUIT=-1 means the enum starts negative — useful as a sentinel
// N_CHOICES auto-counts how many non-quit choices exist
typedef enum {QUIT=-1, ENQUEUE, DEQUEUE, PRINT, N_CHOICES} Choice;
scanf("%d", &choice);
while (getchar() != '\n') {}  // flush rest of input line after scanf
Queue — array-based (simple, fixed size) — your queue practice
Array queue — front/rear index pattern
// Uses two indices into a fixed array — simpler than LL queue but limited to MAX items
// front = -1 means queue is EMPTY
// rear  = index of the last item added
// Both start at -1 (empty). enqueue only moves rear. dequeue only moves front.
#define MAX 100
typedef struct {
    int array[MAX];
    int front;   // -1 = empty, index of oldest item
    int rear;    // -1 = empty, index of newest item
} Queue;

void init(Queue *q) { q->front = -1; q->rear = -1; }

// enqueue: add at rear — only touches rear
void enqueue(Queue *q, int a) {
    if (q->rear == MAX - 1) { fprintf(stderr, "Queue full\n"); return; }
    q->rear++;                         // move rear forward
    q->array[q->rear] = a;             // store new item at rear
    if (q->front == -1) q->front = 0;  // first item: activate front
}

// dequeue: remove from front — only touches front
int dequeue(Queue *q) {
    if (q->front == -1) { fprintf(stderr, "Queue empty\n"); return -1; }
    int var = q->front;  // save front index before moving it
    q->front++;          // advance front past the removed item
    if (q->front > q->rear) { q->front = -1; q->rear = -1; }  // now empty — reset both
    return q->array[var];
}

int peek(Queue *q) {
    if (q->front == -1) { fprintf(stderr, "Queue empty\n"); return -1; }
    return q->array[q->front];  // look at front without removing
}

void printQueue(Queue *q) {
    for (int i = q->front; i <= q->rear; i++) printf("%d ", q->array[i]);
    printf("\n");
}

// Usage:
Queue q; init(&q);
enqueue(&q, 5); enqueue(&q, 7); enqueue(&q, 3);  // front→[5,7,3]←rear
dequeue(&q);   // removes 5, returns 5 — FIFO
printQueue(&q); // prints: 7 3
vs LL Queue: array queue is simpler but wastes slots as items are dequeued — items don't "slide down". LL queue (Lab 10) uses malloc/free per item with void* for any type.
Lab Templates — exact patterns from your labs
Lab 5 — Statistics: read file → compute → write output file
// ── statistics.h ───────────────────────────────────────────────────────
// Functions take pre-computed sum/sumsq/count — main loop does the reading
double mean  (const double sum, const int count);
double ssdev (const double sum, const double sumsq, const int count);

// ── statistics.c ───────────────────────────────────────────────────────
double mean(const double sum, const int count)  { return sum / count; }
double ssdev(const double sum, const double sumsq, const int count) {
    // sample standard deviation formula: sqrt((n*sumSq - sum^2) / (n*(n-1)))
    return sqrt(((count * sumsq) - (sum * sum)) / (count * (count - 1)));
}

// ── statsMain.c ────────────────────────────────────────────────────────
// PREFIX is prepended to input filename to create output filename
#define PREFIX "Result_"

// Helper: prints to ANY stream (stdout for screen, a FILE* for file output)
static void printStats(FILE *dest, const int count,
                        const double mean, const double stddev) {
    fprintf(dest, "%d Values, Mean = %g, Sample Standard Deviation = %g\n",
            count, mean, stddev);
}

// Read loop: accumulate sum and sum-of-squares in one pass
double x, sum=0, sumsq=0; int count=0;
while (fscanf(inFile, "%lf", &x) == 1) { sum+=x; sumsq+=x*x; count++; }

// Build output filename: PREFIX + argv[1]
// strlen+1 for null terminator; snprintf ensures no buffer overflow
int nc = strlen(PREFIX) + strlen(argv[1]) + 1;
char *outName = (char*)malloc(nc);
snprintf(outName, nc, "%s%s", PREFIX, argv[1]);
free(outName);
Lab 6 — Binary search: fread + linear search + interactive loop
// ── arraySearch.h ──────────────────────────────────────────────────────
// Returns INDEX of value if found, -1 if not found
int linearSearch(const int value, const int numbers[], const int n);

// ── arraySearch.c ──────────────────────────────────────────────────────
// Walk every element; return index on match, -1 if we exhaust the array
int linearSearch(const int value, const int numbers[], const int n) {
    for (int i = 0; i < n; i++)
        if (numbers[i] == value) return i;  // found — return its index
    return -1;  // not found
}

// ── searchMain.c ───────────────────────────────────────────────────────
// argv[1]=filename  argv[2]=number-of-ints-in-file (passed as string, need sscanf)
int n; sscanf(argv[2], "%d", &n);  // convert argv[2] string to int
int *arr = (int*)malloc((size_t)n * sizeof(int));  // heap array for n ints
size_t got = fread(arr, sizeof(arr[0]), n, fp);    // read ALL n ints at once

// Interactive search loop: read a string, quit if 'q', else parse and search
// scanf("%19s") reads up to 19 chars into input (prevents buffer overflow)
char input[20];
printf("Enter value ('q' to quit): ");
while (scanf("%19s", input) == 1 && input[0] != 'q') {
    int target; sscanf(input, "%d", &target);    // parse the string to int
    int idx = linearSearch(target, arr, n);
    if (idx >= 0) printf("%d found at %d\n", target, idx);
    else          printf("%d not found\n", target);
    printf("Enter value ('q' to quit): ");
}
free(arr);
Lab 7 — Pointers: swap adjacent pairs, print bytes
// typedef + #define together: change int → double in ONE place and everything updates
typedef int WORD;
#define WORD_FORMAT "%d "
#define N_NUMS 10
const WORD START = 1011;

// Fill array with consecutive values starting at START
WORD nums[N_NUMS];
for (int i=0; i<N_NUMS; i++) nums[i] = START + i;

// Swap adjacent PAIRS (0↔1, 2↔3, 4↔5…) using POINTER notation
// nums+i == &nums[i]  (pointer to element i)
for (int i=0; i<N_NUMS-1; i+=2)   // step by 2 to hit each pair once
    swap(nums+i, nums+i+1);         // pointer notation: nums+i = address of nums[i]

// Swap back using ARRAY notation (identical result, different syntax)
for (int i=0; i<N_NUMS-1; i+=2)
    swap(&nums[i], &nums[i+1]);      // array notation: &nums[i] = address of nums[i]
Lab 8 — Recursion: printVector and reverseVector
// RECURSIVE print: print first element, recurse on the rest
// vec+1 advances the pointer to skip the first element
// n-1 tells the next call there is one fewer element to process
void printVector(const WORD *vec, int n) {
    if (n == 0) { printf("\n"); return; }   // BASE CASE: done, print newline
    printf(WORD_FORMAT, *vec);                // print first element (*vec dereferences)
    printVector(vec+1, n-1);                  // recurse: skip first, count down
}

// RECURSIVE reverse: swap outer elements, recurse on inner subarray
// p1 = left index (starts 0), p2 = right index (starts N-1)
// Call as: reverseVector(arr, 0, N-1)
void reverseVector(WORD *vec, int p1, int p2) {
    if (p1 >= p2) return;               // BASE CASE: indices crossed — done
    WORD tmp=vec[p1]; vec[p1]=vec[p2]; vec[p2]=tmp;  // swap p1 and p2
    reverseVector(vec, p1+1, p2-1);      // recurse: move both pointers inward
}
Quick Reference
printf / scanf format specifiers
Typeprintfscanf
int%d%d
float%f%f
double%f or %g%lf ← must use lf!
char%c%c
string%s%s or %19s (safe width)
long%ld%ld
size_t%zu%zu
unsigned char%hhu
hex%x / %X%x
pointer%p
Common patterns — copy these directly
WhatHow
argc checkif (argc != N) { printf("Usage: %s ...\n", argv[0]); return 1; }
fopen checkif ((fp=fopen(f,"r"))==NULL) { printf("Can't open %s\n",f); return 1; }
malloc checkif (ptr==NULL) { fprintf(stderr,"malloc failed\n"); return EXIT_FAILURE; }
fread checkif (got!=(size_t)n) { if(feof(fp))… else if(ferror(fp))… }
flush stdinwhile(getchar()!='\n'){} ← after scanf, before fgets
free + NULLfree(p); p=NULL; ← prevents double-free bugs
NULL checkif (ptr == NULL) return NULL; ← propagate error up
string allocmalloc(strlen(s) + 1) ← +1 for the '\0' terminator
Data structure comparison
StructureAccessAddRemoveKey feature
Array stacktop index (-1=empty)push: array[++top] = valpop: return array[top--]Fixed MAX size, no malloc per element
LL stacktop pointer (NULL=empty)push: malloc node, node->next=top, top=nodepop: save top, top=top->next, free savedUnlimited size, malloc/free each push/pop
LL queuefront (out) + rear (in)enqueue: add node at reardequeue: remove node from front, free itFIFO order, void* makes it generic
Structdot (variable) or arrow (pointer)assign fieldsGroups related data into one named type
Midterm Questions — your answers + corrections
Midterm 1 · Q2 — Power / Remainder calculator  ·  5.5 / 7

Task: Read a double and an operator character (^ or /), then an int. For ^: only allow exponent 2 or 3, then call power(). For /: compute the floating-point remainder. Print an error for invalid inputs. A power() function that multiplies in a loop is provided.

Your answer — errors marked
#include <stdio.h>
#include <stdlib.h>
// ✗ MISSING: #include <math.h>  — needed for fmod()

double power(double base, int exp);

int main(void) {
    double inputA;
    char   line[11];
    char   type;
    int    inputB;

    printf("Please enter the first number with an operator: ");
    scanf("%lf%10s", &inputA, line);  // reads double then operator string into line[]
    printf("Please enter the second number: ");
    scanf("%d", &inputB);
    type = line[0];   // use first character of line as the operator

    // ✗ BUG — LOGIC ERROR (lost 0.5): || should be &&
    //   With ||: (inputB != 2 || inputB != 3) is ALWAYS true for any number
    //   e.g. inputB=2: (2!=2)=false || (2!=3)=true → TRUE (wrongly invalid)
    //   Fix: use && so BOTH must be not-2 AND not-3 to be truly invalid
    if (type == '^' && (inputB != 2 || inputB != 3))
    {
        printf("The second number is not valid");
    }
    else if (type == '^')
    {
        printf("The result is: %0.2lf", power(inputA, inputB));
    }
    else if (type == '/')
    {
        // ✗ BUG — TYPE ERROR (lost 0.5): % operator does NOT work on doubles
        //   % is integer-only. For doubles, use fmod(a, b) from <math.h>
        double remainResult = inputA % inputB;
        printf("The result is: %0.2lf", remainResult);
    }
    else
    {
        printf("The operator is not valid");
    }
    return EXIT_SUCCESS;
}

double power(double base, int exp) {
    double result = 1.0;
    for (int i = 0; i < exp; i++)
        result *= base;
    return result;
}
Corrected answer
#include <stdio.h>
#include <stdlib.h>
#include <math.h>    // ✓ needed for fmod()

double power(double base, int exp);

int main(void) {
    double inputA;
    char   line[11];
    char   type;
    int    inputB;

    printf("Please enter the first number with an operator: ");
    scanf("%lf%10s", &inputA, line);
    printf("Please enter the second number: ");
    scanf("%d", &inputB);
    type = line[0];

    // ✓ FIX 1: && instead of ||
    //   "invalid if inputB is not 2 AND not 3" → both conditions must hold
    //   Equivalent: !(inputB == 2 || inputB == 3)
    if (type == '^' && (inputB != 2 && inputB != 3))
    {
        printf("The second number is not valid");
    }
    else if (type == '^')
    {
        printf("The result is: %.2lf", power(inputA, inputB));
    }
    else if (type == '/')
    {
        // ✓ FIX 2: fmod(a, b) — floating-point remainder (same logic as %, but for doubles)
        //   fmod(7.5, 2.0) = 1.5   (7.5 = 3*2.0 + 1.5)
        double remainResult = fmod(inputA, inputB);
        printf("The result is: %.2lf", remainResult);
    }
    else
    {
        printf("The operator is not valid");
    }
    return EXIT_SUCCESS;
}

double power(double base, int exp) {
    double result = 1.0;
    for (int i = 0; i < exp; i++)
        result *= base;
    return result;
}
The 2 mistakes to burn into memory:
1. && vs || in "not valid" checks — "invalid if NOT 2 AND NOT 3" uses &&
2. % is integers only — use fmod(a, b) for doubles (needs #include <math.h>)
Format: Each question gives an objective — write the code from scratch to achieve it. The struct/header is given where noted (as it would be on the exam). Open the solution only after trying. The OUTPUT block shows exactly what the program prints.
Practice Q1 — Array Stack: write push() and pop()

Task: The Stack struct and init() are given. Fill in push() and pop().

// ── GIVEN (you see this on the exam) ──────────────────────────────────
#define MAX 50
typedef struct {
    int array[MAX];
    int top;         // -1 = empty
} Stack;

void init(Stack *s)  { s->top = -1; }
void push(Stack *s, int val);
int  pop (Stack *s);

// ── YOUR JOB ──────────────────────────────────────────────────────────
void push(Stack *s, int val) {
    /* fill in */
}
int pop(Stack *s) {
    /* fill in */
}
Show solution
void push(Stack *s, int val) {
    // check full: top == MAX-1 means every slot is used
    if (s->top == MAX - 1) { fprintf(stderr, "Stack full\n"); return; }
    // ++top increments FIRST, then uses the new index to store val
    s->array[++s->top] = val;
}

int pop(Stack *s) {
    // check empty: top == -1 means nothing has been pushed
    if (s->top == -1) { fprintf(stderr, "Stack empty\n"); return -1; }
    // read array[top] THEN decrement top (post-decrement)
    return s->array[s->top--];
}
OUTPUT — given: push(10); push(20); push(30); pop(); pop(); pop(); pop();
// LIFO — last in, first out
pop → 30
pop → 20
pop → 10
Stack empty ← printed to stderr, returns -1
Practice Q2 — Recursion: write sumArray()

Task: Write a recursive function that returns the sum of an int array. No loops allowed. Pattern is the same as Lab 8 printVector.

// ── YOUR JOB ──────────────────────────────────────────────────────────
int sumArray(const int *arr, int n) {
    /* fill in */
}

// Expected usage:
int nums[] = {1, 2, 3, 4, 5};
printf("%d\n", sumArray(nums, 5));   // prints 15
Show solution
int sumArray(const int *arr, int n) {
    // BASE CASE: empty array has sum 0 — without this it recurses forever
    if (n == 0) return 0;
    // add first element (*arr), recurse on the rest (arr+1, n-1)
    // same pattern as printVector: do one thing, shrink the problem
    return *arr + sumArray(arr + 1, n - 1);
}
OUTPUT — int nums[] = {1,2,3,4,5};
sumArray(nums, 5) → 15  // 1+2+3+4+5
sumArray(nums, 3) → 6  // 1+2+3 only
sumArray(nums, 0) → 0  // base case fires immediately
Practice Q3 — LL Stack: write push() with malloc

Task: The Node and Stack structs are given. Write push() that mallocs a new node and returns true on success, false if malloc fails.

// ── GIVEN ─────────────────────────────────────────────────────────────
typedef struct node {
    int         item;
    struct node *next;
} Node;

typedef struct {
    int   size;
    Node *top;    // NULL = empty
} Stack;

// ── YOUR JOB ──────────────────────────────────────────────────────────
bool push(Stack *s, int val) {
    /* fill in */
}
Show solution
bool push(Stack *s, int val) {
    // 1. malloc a new node — always check for NULL
    Node *n = (Node *) malloc(sizeof(Node));
    if (n == NULL) return false;

    // 2. fill in the node's data
    n->item = val;

    // 3. wire it in: new node points DOWN at whatever was on top before
    n->next = s->top;

    // 4. new node IS the new top
    s->top = n;
    s->size++;
    return true;
    // KEY ORDER: set n->next BEFORE updating s->top, or you lose the old chain
}
OUTPUT — push(1); push(2); push(3);
s.size → 3
Chain in memory:  top → [3]→[2]→[1]→NULL
// pop would return 3 first (LIFO)
Practice Q4 — Struct: write a function that operates on a struct

Task: Given the Student struct, write averageGrade() that returns the average of the grades array. Then write printStudent().

// ── GIVEN ─────────────────────────────────────────────────────────────
#define MAX_GRADES 10
typedef struct {
    char   name[50];
    int    numGrades;
    double grades[MAX_GRADES];
} Student;

// ── YOUR JOB ──────────────────────────────────────────────────────────
double averageGrade(const Student *s) {
    /* fill in */
}

void printStudent(const Student *s) {
    /* fill in: print name then average */
}
Show solution
double averageGrade(const Student *s) {
    // use -> because s is a POINTER to a Student (not a Student variable)
    // const Student* means we promise not to change anything through s
    if (s->numGrades == 0) return 0.0;  // guard against divide by zero

    double sum = 0.0;
    for (int i = 0; i < s->numGrades; i++)
        sum += s->grades[i];
    return sum / s->numGrades;
}

void printStudent(const Student *s) {
    // s->name is a char array — %s prints it as a string
    // call averageGrade and print result inline
    printf("Student: %s, Average: %.2f\n", s->name, averageGrade(s));
}
OUTPUT — Student alice = {"Alice", 3, {85.0, 92.0, 78.0}}; printStudent(&alice);
Student: Alice, Average: 85.00
// (85+92+78)/3 = 255/3 = 85.0
Practice Q5 — Queue: write enqueue() and dequeue()

Task: The Queue/ListNode structs are given (Lab 10 pattern). Write enqueue() and dequeue().

// ── GIVEN ─────────────────────────────────────────────────────────────
typedef struct listNode {
    struct listNode *next;
    void            *dataPtr;
} ListNode;

typedef struct {
    ListNode *front;
    ListNode *rear;
    int       size;
} Queue;

// ── YOUR JOB ──────────────────────────────────────────────────────────
void *enqueue(Queue *q, void *item) {
    /* fill in */
}
void *dequeue(Queue *q) {
    /* fill in */
}
Show solution
void *enqueue(Queue *q, void *item) {
    // malloc a wrapper node (NOT the item — item is already malloc-ed by caller)
    ListNode *node = (ListNode *) malloc(sizeof(ListNode));
    if (node == NULL) return NULL;
    node->next    = NULL;    // new node goes at the END — nothing after it
    node->dataPtr = item;    // store the pointer (not a copy of the data)

    if (q->size == 0)
        q->front = q->rear = node;             // first item: both ends point to it
    else {
        q->rear->next = node;                  // link new node after current rear
        q->rear = node;                        // new node is now the rear
    }
    q->size++;
    return item;
}

void *dequeue(Queue *q) {
    if (q->size == 0) return NULL;    // empty queue
    ListNode *old  = q->front;         // save front node pointer so we can free it
    void     *item = old->dataPtr;     // save item pointer BEFORE freeing the node
    q->front = old->next;              // advance front to the next node
    q->size--;
    if (q->size == 0) q->rear = NULL; // queue now empty — clear rear too or it dangles
    free(old);                         // free the ListNode wrapper (NOT the item)
    return item;                       // caller uses then frees the item
}
OUTPUT — enqueue 10, enqueue 20, enqueue 30, then dequeue twice:
dequeue → 10  // FIFO — first in, first out (opposite of stack)
dequeue → 20
queue still holds: front→[30]→NULL (rear also points to [30])
dequeue on empty → NULL
Practice Q6 — Fix the bug (like the midterm style)

Task: The function below should reverse an array recursively. It compiles but produces wrong output. Find and fix the bug.

void reverse(int *arr, int p1, int p2) {
    int tmp  = arr[p1];    // swap p1 and p2
    arr[p1]  = arr[p2];
    arr[p2]  = tmp;
    reverse(arr, p1 + 1, p2 - 1);
}
Show solution
// BUG: missing base case — the function recurses forever (stack overflow)
// p1 and p2 will eventually cross (p1 >= p2) meaning reversal is done
// Without the check, it keeps swapping and undoes its own work, then crashes

void reverse(int *arr, int p1, int p2) {
    // ✓ BASE CASE: when pointers meet or cross, every pair has been swapped
    if (p1 >= p2) return;

    int tmp  = arr[p1];
    arr[p1]  = arr[p2];
    arr[p2]  = tmp;
    reverse(arr, p1 + 1, p2 - 1);
}
// Rule: EVERY recursive function needs a base case that stops the recursion.
OUTPUT — int arr[] = {1,2,3,4,5}; reverse(arr, 0, 4);
Before: 1 2 3 4 5
After: 5 4 3 2 1
// without the base case: infinite recursion → crash (stack overflow)
Practice Q7 — linearSearch() (Lab 6 pattern)

Task: Write linearSearch(). Returns the index of the value if found, -1 if not. Then write the interactive search loop from Lab 6.

// ── YOUR JOB ──────────────────────────────────────────────────────────
int linearSearch(const int value, const int arr[], int n) {
    /* fill in */
}

// Also write the loop that reads a value from the user and prints the result
// Hint: use scanf("%19s", input) and check input[0] != 'q' to quit
Show solution
int linearSearch(const int value, const int arr[], int n) {
    for (int i = 0; i < n; i++)
        if (arr[i] == value) return i;   // found — return index immediately
    return -1;   // loop finished without a match
}

// Interactive loop (Lab 6 main pattern)
char input[20];
printf("Enter value ('q' to quit): ");
while (scanf("%19s", input) == 1 && input[0] != 'q') {
    int target;
    sscanf(input, "%d", &target);   // convert string to int
    int idx = linearSearch(target, arr, n);
    if (idx >= 0) printf("%d found at index %d\n", target, idx);
    else          printf("%d not found\n", target);
    printf("Enter value ('q' to quit): ");
}
OUTPUT — int arr[] = {15, 3, 42, 7, 99}; user types 42, then 100, then q
Enter value ('q' to quit): 42
42 found at index 2
Enter value ('q' to quit): 100
100 not found
Enter value ('q' to quit): q
// loop exits because input[0] == 'q'
Practice Q8 — LL Stack: write pop() (companion to Q3)

Task: Given the same Node/Stack structs from Q3, write pop(). It should write the value to *out and return true, or return false if empty.

// ── YOUR JOB ──────────────────────────────────────────────────────────
bool pop(Stack *s, int *out) {
    /* fill in */
}
Show solution
bool pop(Stack *s, int *out) {
    // 1. check empty — top == NULL means no nodes exist
    if (s->top == NULL) return false;

    // 2. save the top node so we can free it after unlinking
    Node *tmp = s->top;

    // 3. write value to caller's variable through the pointer
    *out = tmp->item;

    // 4. advance top to the next node (the one below)
    s->top = tmp->next;
    s->size--;

    // 5. free the removed node — tmp still holds its address
    free(tmp);
    return true;
    // KEY: save tmp BEFORE changing s->top, or you can't free the old node
}
OUTPUT — push(1); push(2); push(3); then pop three times:
int v;
pop(&s, &v) → true, v = 3
pop(&s, &v) → true, v = 2
pop(&s, &v) → true, v = 1
pop(&s, &v) → false (stack empty, v unchanged)
Practice Q9 — Recursive countEvens()

Task: Write a recursive function that counts how many even numbers are in an array. Same structure as sumArray — decide "does this element count?" then recurse on the rest.

// ── YOUR JOB ──────────────────────────────────────────────────────────
int countEvens(const int *arr, int n) {
    /* fill in */
}
// int a[] = {1,2,3,4,5,6};  countEvens(a, 6) should return 3
Show solution
int countEvens(const int *arr, int n) {
    // BASE CASE: empty array has zero evens
    if (n == 0) return 0;

    // Is the first element even? Add 1 if yes, 0 if no
    // Then recurse on the rest — same shrink pattern as every Lab 8 function
    int thisOne = (*arr % 2 == 0) ? 1 : 0;
    return thisOne + countEvens(arr + 1, n - 1);
}
// Shorter version (same thing):
// return (*arr % 2 == 0) + countEvens(arr + 1, n - 1);
OUTPUT — int a[] = {1,2,3,4,5,6};
countEvens(a, 6) → 3  // 2, 4, 6 are even
countEvens(a, 3) → 1  // only 2 in first 3
countEvens(a, 0) → 0  // base case
Practice Q10 — File read loop + compute (Lab 5 pattern)

Task: Write a complete main that opens a text file (filename from argv[1]), reads integers one per line, and prints the count and sum.

// ── YOUR JOB ──────────────────────────────────────────────────────────
int main(int argc, char *argv[]) {
    /* check argc, open file, read loop, print results, close */
}
Show solution
int main(int argc, char *argv[]) {
    // 1. check argument count
    if (argc != 2) {
        printf("Usage: %s filename\n", argv[0]);
        return 1;
    }

    // 2. open file — always check NULL
    FILE *fp;
    if ((fp = fopen(argv[1], "r")) == NULL) {
        printf("%s: Cannot open \"%s\"\n", argv[0], argv[1]);
        return 1;
    }

    // 3. read loop — fscanf returns 1 each successful read, EOF when done
    int x, count = 0, sum = 0;
    while (fscanf(fp, "%d", &x) == 1) {
        sum += x;
        count++;
    }

    // 4. close then print — always close before printing final results
    fclose(fp);
    printf("Count: %d, Sum: %d\n", count, sum);
    return 0;
}
OUTPUT — file contains: 10 20 30 40
Count: 4, Sum: 100
// bad argc: "Usage: ./prog filename"
// bad file: "./prog: Cannot open "bad.txt""
Practice Q11 — Pointer swap adjacent pairs (Lab 7 pattern)

Task: Given an int array of even length, swap every adjacent pair using pointer notation (arr+i, not &arr[i]). Write the swap() function and the loop.

// ── YOUR JOB ──────────────────────────────────────────────────────────
void swap(int *x, int *y) { /* fill in */ }

// Then write the loop that swaps pairs in: int arr[] = {1,2,3,4,5,6};
Show solution
void swap(int *x, int *y) {
    int tmp = *x;   // save value at x
    *x = *y;        // write y's value to x's address
    *y = tmp;       // write saved value to y's address
}

int arr[] = {1,2,3,4,5,6};
int n = 6;

// step by 2 — each iteration handles one pair (i and i+1)
// pointer notation: arr+i is identical to &arr[i]
for (int i = 0; i < n - 1; i += 2)
    swap(arr + i, arr + i + 1);

// print to verify
for (int i = 0; i < n; i++) printf("%d ", arr[i]);
printf("\n");
OUTPUT — arr = {1,2,3,4,5,6}
Before: 1 2 3 4 5 6
After: 2 1 4 3 6 5
// pair 0↔1 swapped, pair 2↔3 swapped, pair 4↔5 swapped
Practice Q12 — Fix the bug: binary fread (Lab 6 pattern)

Task: The code below tries to read n integers from a binary file. It compiles but crashes or reads garbage. Find and fix all bugs.

int n = 5;
FILE *fp = fopen(argv[1], "r");     // bug?
int arr[n];
size_t got = fread(arr, n, sizeof(int), fp);  // bug?
if (got != n) printf("Error\n");       // bug?
Show solution
// BUG 1: "r" opens as TEXT — binary files need "rb"
//         On Windows, "r" translates \r\n → \n which corrupts binary data
FILE *fp = fopen(argv[1], "rb");   // ✓ binary mode

int *arr = (int*) malloc(n * sizeof(int));  // ✓ heap array (VLAs are risky)

// BUG 2: fread argument order is (ptr, element_size, count, file)
//         The original had them swapped: (arr, n, sizeof(int), fp)
//         That reads sizeof(int)=4 elements each of size n=5 bytes — WRONG
size_t got = fread(arr, sizeof(int), n, fp);  // ✓ size first, count second

// BUG 3: comparing size_t (unsigned) to int (signed) — cast n to size_t
if (got != (size_t)n) {
    if (feof(fp))   printf("Unexpected EOF\n");
    else            printf("Read error\n");
}
fclose(fp);
free(arr);
3 BUGS SUMMARY
1. "r""rb"   binary file needs binary mode
2. fread(arr, n, sizeof(int), fp)fread(arr, sizeof(int), n, fp)   size before count
3. got != ngot != (size_t)n   avoid signed/unsigned mismatch warning
Practice Q13 — Fix the bug: midterm-style (void + braces)

Task: This function should print all multiples of 3 from 0 to n. It has the same two mistakes as your midterm. Find and fix them.

printMultiples(int n)
{
    int i = 0;
    printf("Multiples of 3 up to %d: ", n);
    while (i <= n)
    {
        if (i % 3 == 0)
            printf("%d ", i);
            i++;
    }
}
Show solution
// BUG 1: missing return type void — always required
// BUG 2: i++ looks indented inside the if, but WITHOUT braces it's NOT
//         i++ runs every iteration (which is actually correct behavior here)
//         but the indentation will cost marks — add braces to make intent clear

void printMultiples(int n)   // ✓ void return type
{
    int i = 0;
    printf("Multiples of 3 up to %d: ", n);
    while (i <= n)
    {
        if (i % 3 == 0)
            printf("%d ", i);
        i++;           // ✓ unambiguously outside the if — always increments
    }
    printf("\n");       // ✓ newline at end of output
}
OUTPUT — printMultiples(15);
Multiples of 3 up to 15: 0 3 6 9 12 15
// same two bugs as your midterm: no void, misleading indentation
Practice Q14 — average() + count above average (practice3 pattern)

Objective: Write float average(int *scores, int n) that returns the average. Then in main: read n scores from the user, call average(), and print how many scores are strictly above the average.

Show solution
// Returns float so a 3-person total of 250 → 83.33, not 83
float average(int *scores, int n) {
    int sum = 0;
    for (int i = 0; i < n; i++) sum += scores[i];
    return (float)sum / n;  // cast to float BEFORE dividing — int/int truncates
}

int main(void) {
    int n;
    printf("How many students? ");
    scanf("%d", &n);

    int scores[n];   // VLA — size known at runtime
    for (int i = 0; i < n; i++) {
        printf("Score %d: ", i + 1);
        scanf("%d", &scores[i]);
    }

    float avg = average(scores, n);
    printf("Average: %.2f\n", avg);

    int above = 0;
    for (int i = 0; i < n; i++)
        if (scores[i] > avg) above++;   // strictly greater than average
    printf("%d student(s) scored above average\n", above);
    return 0;
}
OUTPUT — user enters n=3, scores: 70 85 90
Average: 81.67
2 student(s) scored above average
// 85 and 90 are above 81.67
Practice Q15 — Array-based queue: write enqueue() and dequeue()

Objective: Given the struct below, write enqueue() and dequeue() using front/rear indices. front = -1 means empty.

// ── GIVEN ─────────────────────────────────────────────────────────────
#define MAX 100
typedef struct {
    int array[MAX];
    int front;   // -1 = empty
    int rear;    // -1 = empty
} Queue;
void init(Queue *q) { q->front = -1; q->rear = -1; }
Show solution
// enqueue: always adds at rear — only rear changes
void enqueue(Queue *q, int a) {
    if (q->rear == MAX - 1) { fprintf(stderr, "Queue full\n"); return; }
    q->rear++;                          // advance rear to next open slot
    q->array[q->rear] = a;              // store item there
    if (q->front == -1) q->front = 0;   // first item ever: activate front
}

// dequeue: always removes from front — only front changes
int dequeue(Queue *q) {
    if (q->front == -1) { fprintf(stderr, "Queue empty\n"); return -1; }
    int idx = q->front;   // save current front index
    q->front++;            // advance front past the item we're removing
    if (q->front > q->rear) { q->front = -1; q->rear = -1; }  // now empty
    return q->array[idx]; // return the removed item's value
}
OUTPUT — enqueue(5,7,8); dequeue(); dequeue(); printQueue();
dequeue → 5  // first in, first out
dequeue → 7
printQueue → 8  // only 8 remains
Practice Q16 — enum + struct: Employee with department (quiz4 pattern)

Objective: Define a Department enum (HR, SALES, IT, FINANCE), an Employee struct with id, name, and dept, and print an employee's name, id, and department name as a string.

Show solution
// enum values are ints: HR=0, SALES=1, IT=2, FINANCE=3
// Use them as array indices to look up the matching string
typedef enum { HR, SALES, IT, FINANCE } Department;
const char *deptName[] = {"HR", "SALES", "IT", "FINANCE"};

typedef struct {
    int        id;
    char       name[50];
    Department dept;
} Employee;

int main(void) {
    Employee e;
    e.id   = 356;
    e.dept = SALES;
    snprintf(e.name, sizeof(e.name), "Greg");  // snprintf = safe string write into fixed-size array
    printf("%s %d %s\n", e.name, e.id, deptName[e.dept]);
    return 0;
}
OUTPUT
Greg 356 SALES
// deptName[SALES] = deptName[1] = "SALES"
Practice Q17 — 2D array: sum each column

Objective: Given a 2×3 int array, write a nested loop that computes and prints the sum of each column. Watch the index order carefully — outer loop is columns, inner is rows.

Show solution
int arr[2][3] = {{2,8,1},
                  {5,6,4}};

int colSums[3] = {0};   // one accumulator per column

// KEY: outer = column index (i), inner = row index (j)
//      arr[j][i] → row j, column i
for (int i = 0; i < 3; i++) {          // i = column
    for (int j = 0; j < 2; j++) {      // j = row
        colSums[i] += arr[j][i];
    }
    printf("Col %d sum = %d\n", i, colSums[i]);
}
OUTPUT — arr = {{2,8,1},{5,6,4}}
Col 0 sum = 7  // 2+5
Col 1 sum = 14  // 8+6
Col 2 sum = 5  // 1+4
Practice Q18 — Recursive binary search

Objective: Write int binarySearch(int a[], int key, int low, int high) recursively. The array is sorted. Return the index if found, -1 if not. Check mid, recurse left or right.

Show solution
// Binary search: each call cuts the search space in half
// low = leftmost index to consider, high = rightmost
// Call as: binarySearch(arr, key, 0, n-1)
int binarySearch(int a[], int key, int low, int high) {
    // BASE CASE: search space exhausted — key not in array
    if (low > high) return -1;

    int mid = (low + high) / 2;   // middle index

    if      (key == a[mid]) return mid;                         // found!
    else if (key <  a[mid]) return binarySearch(a, key, low, mid - 1);  // search left half
    else                    return binarySearch(a, key, mid + 1, high); // search right half
}

// Usage:
int arr[] = {3, 7, 9, 10, 13, 20};
printf("%d\n", binarySearch(arr, 9, 0, 5));   // prints 2
printf("%d\n", binarySearch(arr, 5, 0, 5));   // prints -1
OUTPUT — arr = {3,7,9,10,13,20}
binarySearch(arr, 9, 0, 5) → 2  // found at index 2
binarySearch(arr, 5, 0, 5) → -1  // not in array
binarySearch(arr, 20, 0, 5) → 5  // found at last index
// vs linearSearch: binary is O(log n) but REQUIRES sorted array
Practice Q19 — Temperature converter (Lab 2 exact pattern)

Objective: Read a temperature as a double immediately followed by a unit (F, C, or K) in one scanf call (e.g. user types 100F). Convert and print all three equivalents. Print an error for unknown units. Formulas: C=(F-32)×5/9 · F=C×9/5+32 · K=C+273.15

Show solution
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    double inputTemp;
    char   line[11];
    char   type;
    const double kOffset = 273.15;  // named constant — never magic numbers

    printf("Enter temperature with unit (e.g. 24.5C): ");
    // KEY: no & before line[] — array name is already a pointer
    // %10s reads the unit string (e.g. "F" or "K") after the number
    scanf("%lf%10s", &inputTemp, line);
    type = line[0];   // first character of line is the unit letter

    if (type == 'F' || type == 'f') {
        double c = (inputTemp - 32.0) * 5.0 / 9.0;
        double k = c + kOffset;
        printf("%8.3fK = %8.3fC = %8.3fF\n", k, c, inputTemp);
    } else if (type == 'C' || type == 'c') {
        double k = inputTemp + kOffset;
        double f = inputTemp * 9.0 / 5.0 + 32.0;
        printf("%8.3fK = %8.3fC = %8.3fF\n", k, inputTemp, f);
    } else if (type == 'K' || type == 'k') {
        double c = inputTemp - kOffset;
        double f = c * 9.0 / 5.0 + 32.0;
        printf("%8.3fK = %8.3fC = %8.3fF\n", inputTemp, c, f);
    } else {
        printf("Unknown unit '%c'\n", type);
    }
    return EXIT_SUCCESS;
}
OUTPUT — user types: 100C
373.150K = 100.000C = 212.000F
OUTPUT — user types: 32F
273.150K = 0.000C = 32.000F
// %8.3f = 8 chars wide, 3 decimal places, right-aligned
Practice Q20 — Grade letter lookup (Lab 1 pattern)

Objective: Read a percentage (0–100) from the user. Validate it (reject bad scanf and out-of-range values). Print the letter grade using a const char * pointer and a chained if-else. Grades: A+(≥90) A(≥85) A-(≥80) B+(≥77) B(≥73) B-(≥70) C+(≥65) C(≥60) D(≥50) F(below 50).

Show solution
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    double percent;

    printf("Enter grade percent: ");
    // Validate scanf return — if user types "abc", scanf returns 0, not 1
    if (scanf("%lf", &percent) != 1) {
        fprintf(stderr, "Invalid input.\n");
        return 1;
    }
    // Validate range
    if (percent < 0.0 || percent > 100.0) {
        fprintf(stderr, "Percent must be between 0 and 100.\n");
        return 1;
    }

    // const char* grade points at a string literal — just reassign the pointer
    // Start with the default (lowest) and chain up
    const char *grade = "F";
    if      (percent >= 90.0) grade = "A+";
    else if (percent >= 85.0) grade = "A";
    else if (percent >= 80.0) grade = "A-";
    else if (percent >= 77.0) grade = "B+";
    else if (percent >= 73.0) grade = "B";
    else if (percent >= 70.0) grade = "B-";
    else if (percent >= 65.0) grade = "C+";
    else if (percent >= 60.0) grade = "C";
    else if (percent >= 50.0) grade = "D";

    printf("Letter grade: %s\n", grade);
    return 0;
}
OUTPUT examples
Input 92.5 → Letter grade: A+
Input 73.0 → Letter grade: B
Input 45.0 → Letter grade: F
Input "abc" → Invalid input. (stderr)
Input 105 → Percent must be between 0 and 100. (stderr)
Midterm 1 — printNumbers even countdown  ·  3 / 5

Task: Write a function printNumbers(int a) that prints all even numbers from a down to 0. Call it with printNumbers(20).

Your answer — errors marked
#include <stdio.h>
#include <stdlib.h>

// ✗ MISSING return type — implicit int is wrong, should be void
//   In C, a function with no return type is treated as returning int (old C89 rule)
//   Modern C requires an explicit type — always write void if nothing is returned
printNumbers(int a);

int main(void) {
    printNumbers(20);
    return EXIT_SUCCESS;
}

// ✗ MISSING return type here too — must match the declaration above
printNumbers(int a)
{
    int b = a;
    printf("David says even numbers within %d are: ", a);

    while(b > -1)
    {
        // ✗ LOOP STRUCTURE — b -= 1 is indented to look like it's inside the if,
        //   but WITHOUT braces only printf belongs to the if block.
        //   b -= 1 actually ALWAYS runs (every loop iteration), not just when even.
        //   The code happens to produce correct output, but the indentation is
        //   misleading — this will cost marks every time. Always use {} on while body.
        if(b % 2 == 0)
            printf("%d ", b);  // ← only this line is inside the if

            b -= 1;            // ← looks indented inside if, but is NOT — runs every loop
    }
    // ✗ OUTPUT — no newline printed at the end of the list
}
Corrected answer
#include <stdio.h>
#include <stdlib.h>

// ✓ void makes the return type explicit — function returns nothing
void printNumbers(int a);

int main(void) {
    printNumbers(20);
    return EXIT_SUCCESS;
}

void printNumbers(int a)
{
    int b = a;
    printf("David says even numbers within %d are: ", a);

    // ✓ LOOP STRUCTURE — braces on the while body make it crystal clear:
    //   - the if only controls printf
    //   - b -= 1 is UNCONDITIONAL (runs every iteration, not just when even)
    //   Without braces on the while, a reader can't easily tell what's in the loop
    while(b > -1)
    {
        if(b % 2 == 0)
            printf("%d ", b);   // print only even numbers

        b -= 1;               // ✓ always decrement — unambiguously outside the if
    }
    printf("\n");  // ✓ end the output line
}
The 3 mistakes to burn into memory:
1. Always write void (or a real type) on every function — no implicit int
2. Indentation means NOTHING to the compiler — b -= 1 after an if without {} is NOT inside the if
3. Add printf("\n") after printing a list so the output ends on its own line