Copy-paste templates from every lab · All structures ready to go
// 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) }
// 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
#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); }
// 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); }
// 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; }
// 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(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);
// 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)
fscanf returns 1 — not just non-zero// "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);
// 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
// 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);
// 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
// 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));
// 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);
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
// ── 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 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
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)
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
// 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
// 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"); }
// 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--);
// 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"); }
// 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
// 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) }
// 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 }
// 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 }
// 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
#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);
#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
// 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);
// 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; } }
// 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
// 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
// ── 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);
// ── 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);
// 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]
// 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 }
| Type | printf | scanf |
|---|---|---|
| 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 | — |
| What | How |
|---|---|
| argc check | if (argc != N) { printf("Usage: %s ...\n", argv[0]); return 1; } |
| fopen check | if ((fp=fopen(f,"r"))==NULL) { printf("Can't open %s\n",f); return 1; } |
| malloc check | if (ptr==NULL) { fprintf(stderr,"malloc failed\n"); return EXIT_FAILURE; } |
| fread check | if (got!=(size_t)n) { if(feof(fp))… else if(ferror(fp))… } |
| flush stdin | while(getchar()!='\n'){} ← after scanf, before fgets |
| free + NULL | free(p); p=NULL; ← prevents double-free bugs |
| NULL check | if (ptr == NULL) return NULL; ← propagate error up |
| string alloc | malloc(strlen(s) + 1) ← +1 for the '\0' terminator |
| Structure | Access | Add | Remove | Key feature |
|---|---|---|---|---|
| Array stack | top index (-1=empty) | push: array[++top] = val | pop: return array[top--] | Fixed MAX size, no malloc per element |
| LL stack | top pointer (NULL=empty) | push: malloc node, node->next=top, top=node | pop: save top, top=top->next, free saved | Unlimited size, malloc/free each push/pop |
| LL queue | front (out) + rear (in) | enqueue: add node at rear | dequeue: remove node from front, free it | FIFO order, void* makes it generic |
| Struct | dot (variable) or arrow (pointer) | assign fields | — | Groups related data into one named type |
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.
#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; }
#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; }
&& vs || in "not valid" checks — "invalid if NOT 2 AND NOT 3" uses &&% is integers only — use fmod(a, b) for doubles (needs #include <math.h>)
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 */ }
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--]; }
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
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); }
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 */ }
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 }
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 */ }
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)); }
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 */ }
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 }
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); }
// 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.
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
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): "); }
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 */ }
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 }
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
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);
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 */ }
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; }
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};
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");
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?
// 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);
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++; } }
// 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 }
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.
// 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; }
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; }
// 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 }
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.
// 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; }
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.
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]); }
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.
// 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
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
#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; }
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).
#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; }
Task: Write a function printNumbers(int a) that prints all even numbers
from a down to 0. Call it with printNumbers(20).
#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 }
#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 }
void (or a real type) on every function — no implicit intb -= 1 after an if without {} is NOT inside the ifprintf("\n") after printing a list so the output ends on its own line