Learn C Programming
Master memory management, pointers, and systems-level thinking with project-based exercises, automated grading, and GitHub integration.
Curated GitHub Repositories
Explore hand-picked repositories to accelerate your C learning. Filter by category or search by name and topics.
Why Learn C?
- Master fundamentals: C makes memory, pointers, and low-level concepts explicit rather than hidden
- Performance: Suitable for systems, embedded devices, and performance-critical applications
- Universal: Runs on microcontrollers and supercomputers, opening platform opportunities
- Career impact: Systems programming, embedded, and infrastructure roles value C expertise
C vs Other Languages
| Feature | C | Python | C++ | Rust |
|---|---|---|---|---|
| Memory Control | Manual | Automatic | Manual/Auto | Automatic (safe) |
| Performance | Excellent | Moderate | Excellent | Excellent |
| Learning Curve | Steep | Gentle | Very Steep | Very Steep |
| Best For | Systems, Embedded | Scripting, AI, Web | Games, High-perf | Safe systems code |
Your Learning Path
Follow this structured progression to master C. Each step builds on the previous one.
2 hours
1-2 hours
1-2 hours
2-3 hours
Interactive Lessons
Click each lesson to expand and learn. Try the code examples in Compiler Explorer to experiment and understand how C works.
Variables, Types & Operators
C is a strongly-typed language, meaning every variable must have a declared type. This helps catch errors early and ensures efficient memory usage.
Primitive Data Types
C has several built-in types for different purposes:
int- integers (typically 4 bytes, range −2,147,483,648 to 2,147,483,647)float- single-precision decimals (32-bit)double- double-precision decimals (64-bit, preferred for accuracy)char- single character (8-bit, stores ASCII values)unsigned- modifiers for non-negative values (doubles the max positive value)
Variable Declaration & Initialization
Declare a variable by specifying its type, name, and optionally an initial value. Good practice is to initialize variables when declaring them to avoid undefined behavior.
#include <stdio.h>
int main() {
// Variable declarations and initialization
int age = 25;
float salary = 50000.50;
double pi = 3.14159265359;
char initial = 'A';
// Print values
printf("Age: %d\n", age);
printf("Salary: %.2f\n", salary);
printf("Pi: %.11f\n", pi);
printf("Initial: %c\n", initial);
// Size of types (in bytes)
printf("\nSize of int: %lu bytes\n", sizeof(int));
printf("Size of float: %lu bytes\n", sizeof(float));
printf("Size of double: %lu bytes\n", sizeof(double));
printf("Size of char: %lu bytes\n", sizeof(char));
return 0;
}
Operators in C
Operators let you perform calculations and comparisons. C has several categories:
- Arithmetic:
+,-,*,/,%(modulo) - Comparison:
==,!=,<,>,<=,>= - Logical:
&&(AND),||(OR),!(NOT) - Assignment:
=,+=,-=,*=,/= - Bitwise:
&,|,^,<<,>>(advanced)
#include <stdio.h>
int main() {
int x = 10, y = 3;
// Arithmetic operators
printf("x + y = %d\n", x + y); // 13
printf("x - y = %d\n", x - y); // 7
printf("x * y = %d\n", x * y); // 30
printf("x / y = %d\n", x / y); // 3 (integer division)
printf("x %% y = %d\n", x % y); // 1 (remainder)
// Comparison operators return 1 (true) or 0 (false)
printf("\nx > y: %d\n", x > y); // 1
printf("x == y: %d\n", x == y); // 0
// Logical operators
int a = 1, b = 0;
printf("\na && b: %d\n", a && b); // 0
printf("a || b: %d\n", a || b); // 1
printf("!a: %d\n", !a); // 0
return 0;
}
Key Takeaway: Variables are containers for values. Types define how much memory is used and what operations are valid. Always initialize variables before use to avoid garbage values.
Try in Compiler ExplorerControl Flow (if/else, loops)
Control flow statements let your program make decisions and repeat actions. C provides several ways to control program execution.
If/Else Statements
#include
int main() {
int age = 18;
if (age >= 18) {
printf("You can vote\n");
} else {
printf("Too young to vote\n");
}
// Multiple conditions with else if
int score = 85;
if (score >= 90) {
printf("Grade: A\n");
} else if (score >= 80) {
printf("Grade: B\n");
} else if (score >= 70) {
printf("Grade: C\n");
} else {
printf("Grade: F\n");
}
return 0;
}
Switch Statement
#include
int main() {
int day = 3;
switch (day) {
case 1:
printf("Monday\n");
break;
case 2:
printf("Tuesday\n");
break;
case 3:
printf("Wednesday\n");
break;
default:
printf("Other day\n");
}
return 0;
}
Loops
#include
int main() {
// For loop
for (int i = 0; i < 5; i++) {
printf("%d ", i);
}
printf("\n");
// While loop
int count = 0;
while (count < 3) {
printf("Count: %d\n", count);
count++;
}
// Do-while loop (runs at least once)
int x = 0;
do {
printf("x = %d\n", x);
x++;
} while (x < 2);
// Break and continue
for (int i = 0; i < 10; i++) {
if (i == 3) continue; // Skip 3
if (i == 7) break; // Stop at 7
printf("%d ", i);
}
return 0;
}
Try in Compiler Explorer
Functions and Scope
Functions let you break programs into reusable pieces. They help organize code and avoid repetition.
Function Declaration and Definition
#include
// Function declaration (prototype)
int add(int a, int b);
void greet(char* name);
int main() {
int result = add(5, 3);
printf("5 + 3 = %d\n", result);
greet("Alice");
return 0;
}
// Function definition
int add(int a, int b) {
return a + b;
}
void greet(char* name) {
printf("Hello, %s!\n", name);
}
Variable Scope
#include
// Global variable (accessible everywhere)
int global_count = 0;
void increment() {
// Local variable (only in this function)
int local_count = 0;
local_count++;
global_count++;
printf("Local: %d, Global: %d\n", local_count, global_count);
}
int main() {
increment(); // Local: 1, Global: 1
increment(); // Local: 1, Global: 2
// printf("%d", local_count); // ERROR: local_count not visible here
return 0;
}
Recursion
#include
// Recursive function to calculate factorial
int factorial(int n) {
if (n <= 1) {
return 1; // Base case
}
return n * factorial(n - 1); // Recursive case
}
// Recursive Fibonacci
int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
int main() {
printf("5! = %d\n", factorial(5)); // 120
printf("Fib(7) = %d\n", fibonacci(7)); // 13
return 0;
}
Try in Compiler Explorer
Pointers & Addresses
Pointers are one of C's most powerful and misunderstood features. A pointer is a variable that stores the memory address of another variable. Understanding pointers unlocks the power of C.
Addresses and the & Operator
Every variable in memory has an address. The & operator gives you that address. Think of your computer's memory as a grid of numbered mailboxes; the address is the mailbox number.
#include <stdio.h>
int main() {
int age = 25;
// The & operator gets the address of age
printf("Value of age: %d\n", age);
printf("Address of age: %p\n", (void*)&age);
char letter = 'A';
printf("\nValue of letter: %c\n", letter);
printf("Address of letter: %p\n", (void*)&letter);
return 0;
}
When you run this, you'll see output like:
Value of age: 25
Address of age: 0x7ffc8b2c4a44
Value of letter: A
Address of letter: 0x7ffc8b2c4a43
The addresses change each time you run the program (ASLR - address space layout randomization - for security).
The * Operator (Dereferencing)
If & gives you an address, the * operator lets you access what's at that address. A pointer variable is declared with * and stores an address.
#include <stdio.h>
int main() {
int x = 42;
// Declare a pointer to an int
int *ptr; // ptr is a pointer to an int
// Make ptr point to x
ptr = &x;
// Access x through ptr
printf("x = %d\n", x); // Direct access: 42
printf("*ptr = %d\n", *ptr); // Indirect access: 42
printf("Address of x: %p\n", (void*)&x);
printf("Value of ptr: %p\n", (void*)ptr);
// Pointers can be modified
*ptr = 100; // Change x through the pointer!
printf("\nAfter *ptr = 100:\n");
printf("x = %d\n", x); // 100
printf("*ptr = %d\n", *ptr); // 100
return 0;
}
Why Pointers Matter
Pointers enable four critical capabilities:
- Dynamic memory allocation: Request memory at runtime (malloc/free)
- Pass by reference: Modify function arguments directly
- Arrays and strings: Arrays decay to pointers in C
- Complex data structures: Build linked lists, trees, graphs
Pointer Arithmetic
You can add and subtract integers from pointers. This moves the pointer through memory. The amount it moves depends on the pointer type.
#include <stdio.h>
int main() {
int arr[] = {10, 20, 30, 40, 50};
int *ptr = arr; // arr decays to pointer to first element
printf("ptr[0] via *ptr: %d\n", *ptr); // 10
printf("ptr[1] via *(ptr+1): %d\n", *(ptr + 1)); // 20
printf("ptr[2] via *(ptr+2): %d\n", *(ptr + 2)); // 30
// Pointer arithmetic moves by sizeof(type) bytes
printf("\nptr = %p\n", (void*)ptr);
printf("ptr+1 = %p\n", (void*)(ptr + 1)); // ptr + 4 bytes (size of int)
printf("ptr+2 = %p\n", (void*)(ptr + 2)); // ptr + 8 bytes
return 0;
}
Common Pointer Mistakes
- Uninitialized pointers:
int *ptr; *ptr = 5;- ptr points to garbage, undefined behavior - Null pointer dereference:
int *ptr = NULL; *ptr = 5;- crashes - Dangling pointers: Pointer to freed memory that gets reused (use-after-free)
- Forgetting & or *:
ptr = x;is wrong (assigns value, not address)
Key Takeaway: A pointer is a variable holding a memory address. Use &variable to get the address, and *pointer to access the value at that address. Pointers are how C gives you low-level memory control.
Arrays and Strings
Arrays store multiple values of the same type. Strings in C are arrays of characters with a special null terminator.
Array Basics
#include
int main() {
// Array declaration and initialization
int numbers[5] = {10, 20, 30, 40, 50};
// Access elements by index (starts at 0)
printf("First element: %d\n", numbers[0]);
printf("Third element: %d\n", numbers[2]);
// Modify elements
numbers[1] = 25;
// Loop through array
for (int i = 0; i < 5; i++) {
printf("%d ", numbers[i]);
}
printf("\n");
// Partial initialization (rest are zero)
int grades[10] = {85, 90, 78}; // Rest are 0
return 0;
}
Strings as Character Arrays
#include
#include
int main() {
// String literal (automatically null-terminated)
char name[] = "Alice"; // Same as {'A','l','i','c','e','\\0'}
// Manual char array (must add \\0)
char greeting[20];
greeting[0] = 'H';
greeting[1] = 'i';
greeting[2] = '\\0'; // Null terminator marks end
printf("Name: %s\n", name);
printf("Greeting: %s\n", greeting);
// String length (doesn't count \\0)
printf("Length of name: %lu\n", strlen(name)); // 5
return 0;
}
Common String Functions
#include
#include
int main() {
char str1[50] = "Hello";
char str2[50] = "World";
char str3[50];
// strcpy - copy string
strcpy(str3, str1);
printf("Copied: %s\n", str3); // Hello
// strcat - concatenate strings
strcat(str1, " ");
strcat(str1, str2);
printf("Concatenated: %s\n", str1); // Hello World
// strcmp - compare strings (returns 0 if equal)
if (strcmp(str2, "World") == 0) {
printf("Strings are equal\n");
}
// strlen - get string length
printf("Length: %lu\n", strlen(str1));
return 0;
}
Try in Compiler Explorer
Dynamic Memory Allocation (malloc/free)
Dynamic memory allocation lets you request memory at runtime. Use malloc to allocate, free to release.
int *arr = malloc(10 * sizeof(int));
for (int i = 0; i < 10; i++) arr[i] = i;
free(arr);
Try in Compiler Explorer
Structs and File I/O
Lesson content placeholder. Topics: struct definition, member access (. and ->), typedef, file operations (fopen, fread, fwrite, fclose), file modes.
Try in Compiler ExplorerCommon Pitfalls & Undefined Behavior
Lesson content placeholder. Topics: buffer overflow, integer overflow, uninitialized variables, race conditions, the "undefined behavior" concept.
Try in Compiler ExplorerPractice Projects
The best way to learn C is by building. These projects progressively build your skills.
-
1. Simple Calculator
Build a command-line calculator that reads two numbers and an operator, then outputs the result.
Key Concepts: variables, operators, control flow, input/output#include <stdio.h> int main(void) { double a, b, result; char op; if (scanf("%lf %c %lf", &a, &op, &b) != 3) return 1; switch (op) { case '+': result = a + b; break; case '-': result = a - b; break; case '*': result = a * b; break; case '/': result = (b != 0.0) ? a / b : 0.0; break; default: return 1; } printf("%g\n", result); return 0; } -
2. Text File Parser
Read a text file, count lines/words/characters, and output statistics. Handle different line endings (Unix vs Windows).
Key Concepts: file I/O, loops, string handling, getline() -
3. Dynamic Array Library
Create a flexible array data structure that grows automatically. Include push, pop, insert, remove, and get operations.
Key Concepts: malloc/free, pointers, structs, memory management -
4. Simple Key-Value Store
Build a hash table or simple database to store key-value pairs. Support insert, lookup, delete, and save/load to file.
Key Concepts: structs, pointers, hashing, file I/O, dynamic memory -
5. Memory Allocator Wrapper
Wrap malloc/free to track allocations. Log all allocs/frees, detect memory leaks, and provide statistics.
Key Concepts: malloc/free, linked lists, memory debugging, function pointers
Resources Hub
Books
- The C Programming Language (K&R) - The definitive reference
- Modern C - Updated best practices
- Pointers in C - Deep dive into pointers
Online Learning
- learn-c.org - Interactive tutorials
- CS50 (Harvard) - University-level course, includes C
- TutorialsPoint - Structured lessons
Tools & Compilers
- Compiler Explorer - Online C compiler (GCC, Clang)
- Replit - Cloud IDE with C support
- Valgrind - Memory debugging tool (Linux)
Practice Sites
- Exercism - Guided practice with feedback
- HackerRank - Coding challenges
- CodeWars - Gamified practice
Community
- r/C_Programming - Active subreddit
- Libera.Chat IRC - ##c channel, real-time help
- Stack Overflow - Q&A for C questions
Standards & Specs
- cppreference C - Language reference
- POSIX C Library - Standard functions
- GCC Documentation - Compiler guides
Your Progress
Check off lessons as you complete them. Your progress is saved locally in your browser.