C 9

Phase 9 — Projects + Interview + Real-World C

Phase 9 is where we stop learning C as isolated concepts and start using it like a real C programmer.

The focus is:

BUILD → DEBUG → REVIEW → BREAK → FIX → INTERVIEW


Module 1 — CLI Programs

Chapter 1 — Command-Line Arguments

Question

The program receives arguments from the command line.

#include <stdio.h>

int main(int argc, char *argv[]) {
    // Print how many command-line arguments were supplied.
    printf("Count: %d\n", argc);

    // Print the first two user-supplied arguments if they exist.
    if (argc > 1)
        printf("First: %s\n", argv[1]);

    if (argc > 2)
        printf("Second: %s\n", argv[2]);

    return 0;
}

Suppose we run:

./app hello C

What is the output?

Answer

Count: 3
First: hello
Second: C

Step-by-step explanation

argc means argument count.

argv means argument vector—an array of strings.

The command:

./app hello C

contains:

argv[0] = "./app"
argv[1] = "hello"
argv[2] = "C"

Therefore:

argc = 3

How to read it

Read:

int main(int argc, char *argv[])

as:

"The program receives a number of arguments and an array containing those arguments."

Key takeaway

Command-line arguments let a C program receive input when it starts.


Module 2 — File-Based Applications

Chapter 2 — File Persistence

A real application often needs to preserve data after the program exits.

Question

#include <stdio.h>

int main(void) {
    // Open a file for writing.
    FILE *file = fopen("data.txt", "w");

    // Make sure the file opened successfully.
    if (file == NULL)
        return 1;

    // Write data into the file.
    fprintf(file, "Alice 95\n");
    fprintf(file, "Bob 87\n");

    // Close the file.
    fclose(file);

    return 0;
}

After successful execution, what happens?

Answer

A file named:

data.txt

is created containing:

Alice 95
Bob 87

Step-by-step

FILE *file

stores a handle representing the opened file.

fopen("data.txt", "w")

opens it in write mode.

fprintf(file, ...)

writes formatted text.

fclose(file)

closes the file and releases the associated resource.

Beginner trap

"w" can overwrite an existing file.

For adding to the end, use:

"a"

Key takeaway

Files allow program data to survive after the process terminates.


Module 3 — Student/Contact Management System

Chapter 3 — Structures + Arrays + Functions

This is your first realistic mini-application.

Question

#include <stdio.h>

struct Student {
    int id;
    char name[30];
    float marks;
};

void print_student(struct Student s) {
    // Display one student's information.
    printf("%d | %s | %.1f\n", s.id, s.name, s.marks);
}

int main(void) {
    // Store multiple students in an array.
    struct Student students[2] = {
        {1, "Alice", 91.5},
        {2, "Bob", 84.0}
    };

    // Print every student.
    for (int i = 0; i < 2; i++)
        print_student(students[i]);

    return 0;
}

What is the output?

Answer

1 | Alice | 91.5
2 | Bob | 84.0

What this demonstrates

A real application combines:

struct
   ↓
array
   ↓
function
   ↓
loop
   ↓
formatted output

This is much closer to real programming than isolated syntax exercises.

Key takeaway

Real C programs are compositions of the fundamentals you've already learned.


Module 4 — Dynamic Data Structures

Chapter 4 — Linked List

Question

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

struct Node {
    int value;
    struct Node *next;
};

int main(void) {
    // Allocate the first node dynamically.
    struct Node *a = malloc(sizeof *a);

    // Allocate the second node dynamically.
    struct Node *b = malloc(sizeof *b);

    // Store values.
    a->value = 10;
    b->value = 20;

    // Connect the nodes.
    a->next = b;
    b->next = NULL;

    // Walk through the list.
    for (struct Node *p = a; p != NULL; p = p->next)
        printf("%d ", p->value);

    // Release both nodes.
    free(b);
    free(a);

    return 0;
}

What is printed?

Answer

10 20

Step-by-step

Memory conceptually looks like:

a
↓
+-------+-------+
|  10   |   ●---|----+
+-------+-------+    |
                     ↓
                +-------+-------+
                |  20   | NULL  |
                +-------+-------+
                    b

a->next = b means:

The first node points to the second node.

The loop:

p = p->next

moves through the list.

Key takeaway

A linked list is dynamically allocated nodes connected through pointers.


Module 5 — Mini Database-Style Application

Chapter 5 — CRUD Thinking

A practical application usually performs:

Create
Read
Update
Delete

These are commonly called CRUD operations.

Question

#include <stdio.h>
#include <string.h>

struct User {
    int id;
    char name[20];
};

int main(void) {
    struct User users[3] = {
        {1, "Alice"},
        {2, "Bob"},
        {3, "Carol"}
    };

    // Update Bob's name.
    strcpy(users[1].name, "Robert");

    // Delete conceptually by shifting later records left.
    users[1] = users[2];

    // Display remaining logical records.
    for (int i = 0; i < 2; i++)
        printf("%d %s\n", users[i].id, users[i].name);

    return 0;
}

What is printed?

Answer

1 Alice
3 Carol

Important insight

C doesn't automatically provide:

database.delete()

You must design the data representation and operations yourself.

This is why C is excellent for learning how abstractions actually work.

Key takeaway

In C, you build the machinery behind higher-level abstractions yourself.


Module 6 — Multi-File Project

Chapter 6 — .h + .c

A real C project shouldn't necessarily put everything into one file.

Typical structure:

project/
├── main.c
├── math.c
└── math.h

Question

math.h:

#ifndef MATH_H
#define MATH_H

int add(int a, int b);

#endif

math.c:

#include "math.h"

int add(int a, int b) {
    return a + b;
}

main.c:

#include <stdio.h>
#include "math.h"

int main(void) {
    // Call a function implemented in another source file.
    printf("%d\n", add(10, 20));

    return 0;
}

What is printed?

Answer

30

The important distinction

Header:

int add(int a, int b);

is a declaration.

Implementation:

int add(int a, int b) {
    return a + b;
}

is the definition.

Key takeaway

Headers describe interfaces; .c files contain implementations.


Module 7 — Reusable C Library

Chapter 7 — Designing an API

Suppose we want a small stack library.

Question

#include <stdio.h>

struct Stack {
    int values[3];
    int top;
};

void push(struct Stack *s, int value) {
    // Add a value at the top.
    if (s->top < 3)
        s->values[s->top++] = value;
}

int pop(struct Stack *s) {
    // Remove and return the top value.
    if (s->top == 0)
        return -1;

    return s->values[--s->top];
}

int main(void) {
    struct Stack s = {0};

    push(&s, 10);
    push(&s, 20);

    printf("%d\n", pop(&s));
    printf("%d\n", pop(&s));

    return 0;
}

Output?

Answer

20
10

Why pass &s?

Because push() and pop() need to modify the original stack.

push(&s, 10);

passes its address.

Inside:

struct Stack *s

is a pointer to the original object.

Key takeaway

A C API is a carefully designed set of functions and types through which other code interacts with your component.


Module 8 — Memory-Intensive Programming

Chapter 8 — Dynamic Allocation

Question

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

int main(void) {
    // Allocate space for five integers.
    int *numbers = malloc(5 * sizeof *numbers);

    if (numbers == NULL)
        return 1;

    // Initialize the allocated array.
    for (int i = 0; i < 5; i++)
        numbers[i] = i * 10;

    // Print the values.
    for (int i = 0; i < 5; i++)
        printf("%d ", numbers[i]);

    // Release the allocated memory.
    free(numbers);

    return 0;
}

Output?

Answer

0 10 20 30 40

Critical concept

This memory:

malloc(...)

belongs to your program until you release it.

Therefore:

free(numbers);

is essential.

Key takeaway

Every successful dynamic allocation needs a deliberate lifetime and eventual release.


Module 9 — Debugging Projects

Chapter 9 — Finding a Memory Bug

Question

What's wrong?

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

int main(void) {
    // Allocate one integer.
    int *p = malloc(sizeof *p);

    if (p == NULL)
        return 1;

    *p = 42;

    // Release the memory.
    free(p);

    // BUG: p no longer points to valid allocated storage.
    printf("%d\n", *p);

    return 0;
}

Answer

The program has use-after-free.

After:

free(p);

the allocation is no longer valid.

Therefore:

*p

is undefined behavior.

Correct approach

If the value is needed first:

printf("%d\n", *p);
free(p);

Key takeaway

free() ends the lifetime of the allocated object; the pointer does not magically become safe.


Module 10 — Output Prediction

Chapter 10 — Pointer Interview Question

Question

#include <stdio.h>

int main(void) {
    int x = 10;

    // p stores the address of x.
    int *p = &x;

    // q stores the address of p.
    int **q = &p;

    // Modify x through the pointer-to-pointer chain.
    **q = 50;

    printf("%d %d %d\n", x, *p, **q);

    return 0;
}

Output?

Answer

50 50 50

Why?

Think:

q
↓
p
↓
x

Therefore:

**q

eventually reaches x.

Changing:

**q = 50;

changes the original variable.

Key takeaway

A pointer-to-pointer is simply another level of indirection.


Module 11 — C Interview Problems

Chapter 11 — Array vs Pointer

Question

#include <stdio.h>

void change(int *p) {
    // Modify the original value.
    *p = 100;
}

int main(void) {
    int x = 10;

    change(&x);

    printf("%d\n", x);

    return 0;
}

Output?

Answer

100

Interview point

C uses pass-by-value.

The function receives a copy of the pointer:

p

but that pointer points to the original x.

Therefore:

*p = 100;

changes x.

Key takeaway

C passes arguments by value; passing a pointer value allows a function to modify the pointed-to object.


Module 12 — Code Review

Chapter 12 — Review AI-Generated C

Question

An AI generates:

char *copy_name(const char *name) {
    char buffer[20];

    strcpy(buffer, name);

    return buffer;
}

Is this safe?

Answer

No.

There are actually two major problems.

Problem 1 — Returning a dead object

char buffer[20];

is local automatic storage.

It stops existing when the function returns.

Therefore:

return buffer;

returns a pointer to invalid storage.

This creates a dangling pointer.

Problem 2 — Buffer overflow

strcpy(buffer, name);

doesn't know that buffer only has room for 20 bytes.

A sufficiently long name can overflow it.

Better design

One possible design is:

char *copy_name(const char *name) {
    size_t length = strlen(name) + 1;

    char *copy = malloc(length);

    if (copy == NULL)
        return NULL;

    memcpy(copy, name, length);

    return copy;
}

Now the caller owns the returned allocation and must eventually:

free(copy);

Key takeaway

Never trust generated C code merely because it compiles.


Module 13 — Security-Oriented C

Chapter 13 — Buffer Overflow

Question

#include <stdio.h>

int main(void) {
    char name[8];

    // Dangerous: no size limit is supplied.
    scanf("%s", name);

    printf("Hello %s\n", name);

    return 0;
}

What's dangerous?

Answer

name can hold only a limited number of characters.

But:

scanf("%s", name);

can accept a much longer input.

That can write beyond the array's bounds.

This is a buffer overflow.

Safer approach

Use a bounded input strategy, for example:

fgets(name, sizeof name, stdin);

Key takeaway

Every input operation must respect the size of the destination buffer.


Module 14 — Resource Leaks

Chapter 14 — Memory Isn't the Only Resource

Question

#include <stdio.h>

int main(void) {
    FILE *file = fopen("data.txt", "r");

    if (file == NULL)
        return 1;

    // Read the file...

    return 0;
}

What's missing?

Answer

fclose(file);

The file is a resource and should be released.

Correct structure:

FILE *file = fopen("data.txt", "r");

if (file == NULL)
    return 1;

/* use file */

fclose(file);

Important insight

Resources include:

memory
files
sockets
locks
handles
process resources

Key takeaway

Resource management is a core professional C skill.


Module 15 — Debugging Methodology

Chapter 15 — Don't Guess, Isolate

When a C program crashes, don't randomly change code.

Use this process:

1. Reproduce
      ↓
2. Minimize
      ↓
3. Identify exact failure
      ↓
4. Inspect state
      ↓
5. Find violated assumption
      ↓
6. Fix root cause
      ↓
7. Test again

Question

Suppose this crashes:

int *p = NULL;

// Somewhere later:
*p = 10;

What should you investigate first?

Answer

The immediate issue is:

p == NULL

and therefore:

*p

attempts to dereference a null pointer.

Professional mindset

Don't ask:

"How do I stop the crash?"

Ask:

"Why was p NULL at this point?"

That distinction is extremely important.

Key takeaway

Debug the cause, not merely the symptom.


Module 16 — Testing

Chapter 16 — Test Edge Cases

Question

Consider:

int divide(int a, int b) {
    return a / b;
}

What cases should you test?

Answer

At minimum:

10 / 2
10 / 1
10 / -2
0 / 5
5 / 0
INT_MAX / 1
INT_MIN / -1

The important case is:

5 / 0

which is invalid.

Testing mindset

Don't test only:

normal input

Test:

empty input
zero
negative values
maximum values
minimum values
very large input
unexpected input
NULL
allocation failure
missing files
duplicate data

Key takeaway

Good tests attack the assumptions your code makes.


Module 17 — Build Systems

Chapter 17 — Makefile Thinking

A multi-file C project might contain:

main.c
student.c
student.h

Compilation conceptually becomes:

main.c ──→ main.o ──┐
                    ├──→ executable
student.c → student.o┘

Question

If student.c changes, do you necessarily need to recompile main.c?

Answer

No.

Ideally only the affected source file is recompiled:

student.c
   ↓
student.o
   ↓
link

This is one reason build systems such as make are useful.

Key takeaway

Build systems automate dependency-aware compilation and linking.


Module 18 — Performance

Chapter 18 — Know Where Time Goes

Question

Which is generally more expensive?

for (int i = 0; i < n; i++) {
    printf("%d\n", array[i]);
}

or simply:

for (int i = 0; i < n; i++) {
    int x = array[i];
}

Answer

The first is generally much more expensive because I/O is costly compared with ordinary memory access.

Important lesson

Don't optimize based on appearance.

Measure.

Typical workflow:

Correctness
   ↓
Profile
   ↓
Find bottleneck
   ↓
Optimize bottleneck
   ↓
Measure again

Key takeaway

Performance optimization should be driven by measurements, not guesses.


Module 19 — Capstone Project

Chapter 19 — Contact Management System

Now combine everything.

Your capstone should support:

CREATE contact
READ contacts
SEARCH contact
UPDATE contact
DELETE contact
SAVE to file
LOAD from file
EXIT

Suggested structure:

contact-manager/
│
├── main.c
├── contact.c
├── contact.h
├── storage.c
├── storage.h
└── Makefile

Architecture:

                 main
                  │
          ┌───────┴────────┐
          ↓                ↓
      contact API       storage API
          ↓                ↓
      structures        file I/O
          │                │
          └───────┬────────┘
                  ↓
               disk file

This project forces you to use:

  • structures

  • arrays/dynamic memory

  • pointers

  • strings

  • functions

  • multiple files

  • headers

  • file handling

  • error handling

  • searching

  • updating

  • deletion

  • memory management

  • build systems

Key takeaway

A capstone is where individual C concepts become an actual software system.


Module 20 — Interview Preparation

Chapter 20 — What You Should Be Able to Explain

At the end of Phase 9, you should be able to answer questions such as:

Fundamentals

  • What happens when C code is compiled?

  • What is the difference between declaration and definition?

  • What is the difference between compiler and linker?

  • What is an object file?

  • What is sizeof?

  • What is undefined behavior?

Pointers

  • What is a pointer?

  • What does &x mean?

  • What does *p mean?

  • What is pointer arithmetic?

  • What is void *?

  • What is a dangling pointer?

  • What is a NULL pointer?

  • What is a wild pointer?

  • What is int **?

  • What is a function pointer?

Memory

  • Stack vs heap?

  • What does malloc() do?

  • malloc() vs calloc()?

  • What does realloc() do?

  • What happens after free()?

  • What is a memory leak?

  • What is use-after-free?

  • What is double free?

Strings

  • How are C strings represented?

  • What is '\0'?

  • Why can strcpy() be dangerous?

  • Why is buffer size important?

Structures

  • struct vs union?

  • What does -> mean?

  • What is typedef?

  • How are structures passed to functions?

Compilation

  • What does preprocessing do?

  • What does compilation do?

  • What does assembly do?

  • What does linking do?

  • Why do we use header files?

  • Why do we need include guards?

Professional C

  • How do you debug a segmentation fault?

  • How do you find a memory leak?

  • Why should compiler warnings be enabled?

  • What are sanitizers?

  • How do you review AI-generated C?

  • How do you make C code portable?

  • How do you handle errors?


Module 21 — AI/Vibe-Code Review

Chapter 21 — The Ultimate Skill

This is one of the most important exercises for your goal.

When AI gives you:

char *get_data(void) {
    char buffer[100];

    // AI says this returns the data.
    return buffer;
}

You should immediately ask:

Where does buffer live?
When does its lifetime end?
Who owns the returned pointer?
Can the caller safely use it?

You should recognize:

local array
      ↓
function returns
      ↓
lifetime ends
      ↓
returned pointer becomes dangling

Then challenge the AI.


Phase 9 Final Skill Test

You are not finished with C merely because you can write this:

printf("Hello World");

You are approaching professional competency when you can look at unfamiliar C and mentally reason about:

         SOURCE CODE
              ↓
        preprocessing
              ↓
         compilation
              ↓
          assembly
              ↓
           linking
              ↓
         executable
              ↓
           process
              ↓
      ┌───────┴────────┐
      ↓                ↓
    stack             heap
      ↓                ↓
 local objects     allocations
      ↓                ↓
 pointers ─────→ memory
      ↓
 data structures
      ↓
 algorithms
      ↓
 files/resources
      ↓
 errors/UB/security
      ↓
 debugging/testing

And when AI generates C, your mental checklist should become:

1. Does it compile?
2. Are the compiler warnings clean?
3. Is the logic correct?
4. Are array bounds respected?
5. Are pointers valid?
6. Are lifetimes correct?
7. Who owns allocated memory?
8. Who frees it?
9. Can allocation fail?
10. Can input overflow buffers?
11. Can NULL occur?
12. Can integer overflow occur?
13. Is there undefined behavior?
14. Are files/resources closed?
15. Is the code portable?
16. Is error handling adequate?
17. Can it be tested?
18. Can it be maintained?
19. Can it be attacked?
20. Can I explain every important line?

Final Phase 9 Objective

You should now be moving from:

“I know C syntax.”

to:

“I can build a C program.”

and ultimately:

“I can inspect a C program and understand what it is doing with memory, data, files, and the operating system—and I can tell when AI-generated C is wrong.”

That is the real purpose of Phase 9.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.