C
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Arrays and Strings
-
Section 5: Pointers
-
Section 6: Memory Management
-
Section 7: Structures and Unions
-
Section 8: The Preprocessor and Build Process
-
Section 9: Standard Library: stdio.h
-
Section 10: Standard Library: stdlib.h
-
Section 11: Standard Library: string.h
-
Section 12: Standard Library: ctype.h and wctype.h
-
Section 13: Standard Library: math.h, complex.h, fenv.h, tgmath.h
-
Section 14: Standard Library: Type and Limit Headers
-
Section 15: Standard Library: Error Handling and Debugging
-
Section 16: Standard Library: Localization and Encoding
-
Section 17: Standard Library: time.h
-
Section 18: Standard Library: Concurrency (C11)
-
Section 19: POSIX and System Programming (unistd.h)
-
Section 20: More Data Structures
-
Section 21: Algorithms in C
-
Section 22: Bitwise Operations
-
Section 23: Command-Line Programs
-
Section 24: Debugging and Best Practices
-
Section 25: Compiler and Language Internals
-
Section 26: Embedded and Cross-Platform Considerations
-
Section 27: Networking Basics
-
Section 28: Practical Projects
-
Section 29: Interview Practice
-
Section 30: C23 Modern Features
-
Section 31: More Practice and Review
233: Practice Exercise: Simple Command-Line Todo List in C
Think of building a todo list in C like keeping a handwritten ledger in a physical notebook. You have a fixed number of pages (your array size), and each line on the page has a specific format: a checkbox and a space for the task description. When you add a task, you find the first empty line and write it down. When you finish a task, you don't usually rip the page out; you just check the box. If you absolutely have to remove a task to make room, you have to erase it and physically shift every subsequent line up to fill the gap.
In our C code, we map these physical actions directly to memory management. That "line in the notebook" becomes a struct. The "fixed number of pages" is a statically allocated array of those structs. Checking the box is just flipping a boolean or an integer from 0 to 1. And that tedious process of shifting lines? That's a for loop moving elements in your array to avoid leaving "holes" in your data.
Defining the Blueprint for a Task
You can't just throw strings into an array if you want to track whether a task is finished. I always recommend using a struct here. It keeps the description and the status bundled together so they don't get out of sync. I like to keep the description length fixed—say, 100 characters—to avoid the headache of malloc and free for a simple exercise like this. It's not the most memory-efficient way, but for a CLI tool, it's plenty.
typedef struct {
char description[100];
int is_done;
} TodoItem;
Handling the Input Friction
Here is where most people trip up: scanf. If you use scanf("%s", ...) to get a task description, it will stop reading the moment it hits a space. Your "Buy milk" task becomes just "Buy". To fix this, I use fgets. It reads the whole line, including the spaces. Just be mindful that fgets also grabs the newline character \n when you hit Enter, so you'll want to strip that out manually if you care about your formatting looking clean.
Managing the List State
Since we're using a fixed array, you need a separate variable to track how many tasks are actually in the list. I call this task_count. When you add a task, you place it at index task_count and then increment the counter. When you delete a task at index i, you have to run a loop from i to task_count - 1, moving every item one slot to the left. It feels clunky, but that's the reality of contiguous memory in C.
for (int i = index_to_remove; i < task_count - 1; i++) {
tasks[i] = tasks[i + 1];
}
task_count--;
I've found that the most satisfying part of this exercise isn't the logic itself, but building the "Command Loop." You wrap everything in a while(1) loop, present a small menu, and use a switch statement to handle the user's choice. It turns a collection of functions into an actual piece of software you can interact with.
📋 Practical Task
Build the "Get-It-Done" CLI Task Manager
Your goal is to implement a fully functional command-line todo list. Create a program that satisfies the following requirements:
- Storage: Use an array of structs capable of holding up to 10 tasks. Each struct should have a string for the task and an integer for the completion status.
- The Menu: Implement a loop that allows the user to:
- Add a new task (ensure it handles spaces in the task description).
- List all current tasks, showing their index and whether they are [ ] incomplete or [X] complete.
- Mark a specific task as complete by its index.
- Delete a task entirely, shifting subsequent tasks up to fill the gap.
- Exit the program.
- Edge Cases: Your code must prevent the user from adding tasks beyond the array limit and prevent them from marking or deleting a task index that doesn't exist.
There are no comments for now.