← Back to all guides

C TLDR

A rapid reference guide to the C programming language. The foundation of modern computing.

C23 — December 2025

What is C?

C is a general-purpose, procedural programming language developed in 1972. It provides low-level access to memory, maps efficiently to machine instructions, and is the foundation for operating systems, embedded systems, and countless other languages.

Low-Level Control

Direct memory access via pointers. Manual memory management. Minimal runtime overhead.

Portable

Compiles to native code on virtually every platform. Write once, compile anywhere.

Fast

Minimal abstraction between code and hardware. Predictable performance characteristics.

Foundational

Linux, Windows, databases, interpreters — most system software is written in C.

Basics

Hello World

#include <stdio.h>

int main(void) {
    printf("Hello, world!\n");
    return 0;
}

Variables

// Declaration and initialization
int x = 42;
int y;           // uninitialized (contains garbage)
y = 10;

// Constants
const int MAX = 100;      // runtime constant
#define MAX_SIZE 100     // compile-time constant

// Multiple declarations
int a = 1, b = 2, c = 3;

// Static (persists between calls, file scope)
static int counter = 0;

// Volatile (prevent optimization)
volatile int hardware_reg;

Functions

// Function declaration (prototype)
int add(int a, int b);

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

// No return value
void greet(const char *name) {
    printf("Hello, %s!\n", name);
}

// No parameters
int get_value(void) {
    return 42;
}

// Static function (file-local)
static int helper(int x) {
    return x * 2;
}

Control Flow

// if-else
if (x > 0) {
    printf("positive\n");
} else if (x < 0) {
    printf("negative\n");
} else {
    printf("zero\n");
}

// Ternary operator
int abs_x = (x >= 0) ? x : -x;

// switch
switch (choice) {
    case 1:
        printf("one\n");
        break;
    case 2:
    case 3:
        printf("two or three\n");
        break;
    default:
        printf("other\n");
}

// while
while (x > 0) {
    x--;
}

// do-while (runs at least once)
do {
    x++;
} while (x < 10);

// for
for (int i = 0; i < 10; i++) {
    printf("%d\n", i);
}

// break and continue
for (int i = 0; i < 100; i++) {
    if (i == 5) continue;  // skip this iteration
    if (i == 50) break;    // exit loop
}

Operators

// Arithmetic
a + b    a - b    a * b    a / b    a % b

// Comparison
a == b   a != b   a < b    a > b    a <= b   a >= b

// Logical
!a       a && b   a || b

// Bitwise
~a       a & b    a | b    a ^ b    a << n   a >> n

// Assignment
a = b    a += b   a -= b   a *= b   a /= b
a %= b   a &= b   a |= b   a ^= b   a <<= n  a >>= n

// Increment/Decrement
++a  a++  --a  a--

// Pointer/Address
*ptr     // dereference
&var     // address-of

// sizeof
sizeof(int)      // size in bytes
sizeof(arr)      // array size in bytes

Types

Primitive Types

Type Size (typical) Range
char 1 byte -128 to 127 or 0 to 255
short 2 bytes -32,768 to 32,767
int 4 bytes -2.1B to 2.1B
long 4-8 bytes Platform dependent
long long 8 bytes -9.2E18 to 9.2E18
float 4 bytes ~6 decimal digits
double 8 bytes ~15 decimal digits

Fixed-Width Types (stdint.h)

#include <stdint.h>

// Exact width
int8_t   int16_t   int32_t   int64_t
uint8_t  uint16_t  uint32_t  uint64_t

// Pointer-sized
intptr_t   uintptr_t

// Size type
size_t     // unsigned, for sizes/indices
ptrdiff_t  // signed, for pointer differences

Type Modifiers

// Signed/Unsigned
signed int x;      // can be negative (default for int)
unsigned int y;    // non-negative only
unsigned char byte; // 0 to 255

// Common shortcuts
unsigned x;        // same as unsigned int
long y;            // same as long int

// Boolean (stdbool.h or C23)
#include <stdbool.h>
bool flag = true;
bool done = false;

Type Casting

int x = 10;
double d = (double)x;       // explicit cast
int y = (int)3.14;          // truncates to 3

// Pointer casts
void *generic = &x;
int *int_ptr = (int *)generic;

Typedef

// Create type aliases
typedef unsigned int uint;
typedef unsigned char byte;

// Function pointer typedef
typedef int (*Comparator)(const void *, const void *);

// Struct typedef
typedef struct {
    int x, y;
} Point;

Pointers

A pointer stores the memory address of a variable. Pointers enable dynamic memory, data structures, and efficient function arguments.

Pointer Basics

int x = 42;
int *ptr = &x;      // ptr holds address of x

printf("%p\n", (void *)ptr);  // print address
printf("%d\n", *ptr);          // dereference: prints 42

*ptr = 100;         // modify x through pointer
printf("%d\n", x);  // prints 100

// Null pointer
int *null_ptr = NULL;
if (ptr != NULL) {
    // safe to dereference
}

Pointer Arithmetic

int arr[] = {10, 20, 30, 40};
int *ptr = arr;      // points to arr[0]

ptr++;               // now points to arr[1]
ptr += 2;            // now points to arr[3]

// Pointer subtraction
int *start = arr;
int *end = &arr[3];
ptrdiff_t diff = end - start;  // 3 (elements, not bytes)

// Array indexing is pointer arithmetic
arr[2] == *(arr + 2)  // equivalent

Pointers and Functions

// Pass by reference
void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int x = 1, y = 2;
swap(&x, &y);  // x=2, y=1

// Return pointer (be careful of scope!)
int *create_array(size_t size) {
    int *arr = malloc(size * sizeof(int));
    return arr;  // caller must free
}

Pointers to Pointers

int x = 5;
int *ptr = &x;
int **pptr = &ptr;   // pointer to pointer

printf("%d\n", **pptr);  // prints 5

// Common use: 2D arrays, modifying pointers in functions
void allocate(int **ptr, size_t size) {
    *ptr = malloc(size * sizeof(int));
}

Function Pointers

// Declaration
int (*operation)(int, int);

// Functions to point to
int add(int a, int b) { return a + b; }
int mul(int a, int b) { return a * b; }

// Usage
operation = add;
int result = operation(3, 4);  // 7

operation = mul;
result = operation(3, 4);      // 12

// As function parameter (callback)
void apply(int *arr, size_t len, int (*fn)(int)) {
    for (size_t i = 0; i < len; i++) {
        arr[i] = fn(arr[i]);
    }
}
Always initialize pointers. Dereferencing uninitialized or NULL pointers causes undefined behavior (usually a crash).

Arrays & Strings

Arrays

// Fixed-size array
int arr[5];                    // uninitialized
int arr[5] = {1, 2, 3, 4, 5};
int arr[] = {1, 2, 3};        // size inferred (3)
int arr[5] = {0};              // all zeros
int arr[5] = {1, 2};          // {1, 2, 0, 0, 0}

// Access
arr[0] = 10;
int x = arr[2];

// Array size
size_t len = sizeof(arr) / sizeof(arr[0]);

// Arrays decay to pointers when passed to functions
void process(int *arr, size_t len);
void process(int arr[], size_t len);  // equivalent

Multidimensional Arrays

// 2D array
int matrix[3][4];   // 3 rows, 4 columns
int matrix[2][3] = {
    {1, 2, 3},
    {4, 5, 6}
};

// Access
matrix[0][1] = 10;

// Pass to function (must specify all but first dimension)
void process(int mat[][4], size_t rows);

Strings

// String = char array with null terminator '\0'
char str[] = "hello";          // {'h','e','l','l','o','\0'}
char str[10] = "hello";        // remaining filled with '\0'
char *str = "hello";           // pointer to string literal (read-only!)

// String length
#include <string.h>
size_t len = strlen(str);      // doesn't count '\0'

// Common string functions
strcpy(dest, src);              // copy string
strncpy(dest, src, n);          // copy up to n chars
strcat(dest, src);              // concatenate
strncat(dest, src, n);          // concat up to n chars
strcmp(s1, s2);                 // compare (0 if equal)
strncmp(s1, s2, n);             // compare up to n chars
strchr(str, c);                 // find char, return ptr
strstr(haystack, needle);       // find substring

String Formatting

char buffer[100];

// Format into buffer
sprintf(buffer, "Name: %s, Age: %d", "Alice", 30);

// Safe version (prevents overflow)
snprintf(buffer, sizeof(buffer), "Value: %d", 42);

// Parse from string
int num;
sscanf("42", "%d", &num);

// String to number
#include <stdlib.h>
int n = atoi("42");
long l = strtol("42", NULL, 10);
double d = strtod("3.14", NULL);
Always use strncpy/snprintf instead of strcpy/sprintf to prevent buffer overflows.

Structs & Unions

Structs

// Define struct
struct Point {
    int x;
    int y;
};

// Create instance
struct Point p1;
p1.x = 10;
p1.y = 20;

// Initialize
struct Point p2 = {10, 20};
struct Point p3 = {.x = 10, .y = 20};  // designated initializers

// Typedef for cleaner syntax
typedef struct {
    char name[50];
    int age;
} Person;

Person alice = {"Alice", 30};

Struct Pointers

Person *ptr = &alice;

// Access via pointer
(*ptr).age = 31;    // dereference then access
ptr->age = 31;      // arrow operator (preferred)

// Allocate struct on heap
Person *p = malloc(sizeof(Person));
p->age = 25;
strcpy(p->name, "Bob");
free(p);

Nested Structs

typedef struct {
    int x, y;
} Point;

typedef struct {
    Point top_left;
    Point bottom_right;
} Rectangle;

Rectangle rect = {{0, 0}, {100, 50}};
rect.top_left.x = 10;

Unions

// All members share same memory location
union Data {
    int i;
    float f;
    char str[20];
};

union Data data;
data.i = 10;        // data contains int
data.f = 3.14;      // now contains float (i is overwritten)

// Size = size of largest member
printf("%zu\n", sizeof(union Data));  // 20

Tagged Unions

// Common pattern: struct with enum tag + union
typedef enum { INT, FLOAT, STRING } DataType;

typedef struct {
    DataType type;
    union {
        int i;
        float f;
        char *s;
    } value;
} Variant;

Variant v = {.type = INT, .value.i = 42};

Enums

// Define enumeration
enum Color { RED, GREEN, BLUE };        // 0, 1, 2
enum Status { OK = 0, ERROR = -1 };    // explicit values

enum Color c = RED;

// With typedef
typedef enum {
    MONDAY = 1,
    TUESDAY,    // 2
    WEDNESDAY,  // 3
    // ...
} Day;

Memory Management

C requires manual memory management. You must explicitly allocate and free heap memory.

Stack vs Heap

// Stack allocation (automatic, fast, limited size)
int x = 42;                  // freed when scope ends
int arr[100];               // fixed size at compile time

// Heap allocation (manual, slower, large capacity)
int *ptr = malloc(sizeof(int));
*ptr = 42;
free(ptr);                  // must free when done!

Memory Functions (stdlib.h)

#include <stdlib.h>

// malloc: allocate uninitialized memory
int *arr = malloc(n * sizeof(int));
if (arr == NULL) {
    // allocation failed!
}

// calloc: allocate and zero-initialize
int *arr = calloc(n, sizeof(int));

// realloc: resize allocation
arr = realloc(arr, new_size * sizeof(int));

// free: release memory
free(arr);
arr = NULL;  // good practice: avoid dangling pointer

Memory Operations (string.h)

#include <string.h>

// Copy memory
memcpy(dest, src, n);       // copy n bytes (no overlap)
memmove(dest, src, n);      // copy n bytes (handles overlap)

// Set memory
memset(ptr, 0, n);          // set n bytes to 0
memset(ptr, 0xFF, n);       // set n bytes to 0xFF

// Compare memory
int result = memcmp(a, b, n);  // 0 if equal

Common Pitfalls

// Memory leak: forgetting to free
void leak() {
    int *p = malloc(100);
    // forgot free(p) - memory leaked!
}

// Use after free
free(ptr);
*ptr = 10;   // UNDEFINED BEHAVIOR!

// Double free
free(ptr);
free(ptr);   // UNDEFINED BEHAVIOR!

// Buffer overflow
char buf[10];
strcpy(buf, "this is way too long");  // overflow!
Use tools like Valgrind or AddressSanitizer (-fsanitize=address) to detect memory errors.

Preprocessor

The C preprocessor runs before compilation, handling includes, macros, and conditional compilation.

Include Directives

#include <stdio.h>      // system header (search system paths)
#include "myheader.h"   // local header (search current dir first)

Macros

// Object-like macros (constants)
#define PI 3.14159
#define MAX_SIZE 1024

// Function-like macros
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define SQUARE(x) ((x) * (x))

// Multi-line macro
#define SWAP(a, b) do { \
    typeof(a) _tmp = (a); \
    (a) = (b); \
    (b) = _tmp; \
} while(0)

// Undefine
#undef MAX_SIZE

// Stringification
#define STR(x) #x
printf("%s\n", STR(hello));  // prints "hello"

// Token concatenation
#define CONCAT(a, b) a##b
int CONCAT(my, var) = 10;   // creates: int myvar = 10;

Conditional Compilation

// Check if defined
#ifdef DEBUG
    printf("Debug mode\n");
#endif

#ifndef HEADER_H
#define HEADER_H
    // header contents (include guard)
#endif

// if-elif-else
#if VERSION >= 2
    // version 2+ code
#elif VERSION == 1
    // version 1 code
#else
    // fallback
#endif

// Check if defined (alternative)
#if defined(A) && !defined(B)
    // ...
#endif

Predefined Macros

__FILE__     // current filename
__LINE__     // current line number
__DATE__     // compilation date
__TIME__     // compilation time
__func__     // current function name (C99)

// Common debug macro
#define LOG(msg) printf("[%s:%d] %s\n", __FILE__, __LINE__, msg)

Pragma

// Compiler-specific directives
#pragma once           // include guard (non-standard but widely supported)
#pragma pack(push, 1)  // pack struct with no padding
#pragma pack(pop)

Input/Output

Standard I/O

#include <stdio.h>

// Output
printf("Hello %s, you are %d years old\n", name, age);
putchar('A');           // single character
puts("Hello");          // string with newline

// Input
int n;
scanf("%d", &n);         // read int

char buf[100];
fgets(buf, sizeof(buf), stdin);  // safe string input

int c = getchar();       // single character

Format Specifiers

Specifier Type Example
%d, %i signed int -42
%u unsigned int 42
%ld, %lu long 123456789
%lld, %llu long long large numbers
%f float/double 3.140000
%e scientific 3.14e+00
%c char A
%s string hello
%p pointer 0x7fff...
%x, %X hex ff, FF
%zu size_t 100
%% literal % %

Format Modifiers

printf("%10d", 42);      // width 10, right-aligned
printf("%-10d", 42);     // width 10, left-aligned
printf("%010d", 42);     // zero-padded
printf("%.2f", 3.14159); // 2 decimal places: 3.14
printf("%8.2f", 3.14);   // width 8, 2 decimals

File I/O

FILE *fp;

// Open file
fp = fopen("file.txt", "r");   // read
fp = fopen("file.txt", "w");   // write (creates/truncates)
fp = fopen("file.txt", "a");   // append
fp = fopen("file.bin", "rb");  // read binary

if (fp == NULL) {
    perror("Error opening file");
    return 1;
}

// Read/Write
char buf[256];
while (fgets(buf, sizeof(buf), fp) != NULL) {
    printf("%s", buf);
}

fprintf(fp, "Value: %d\n", 42);  // formatted write
fputs("Hello\n", fp);            // write string
fputc('A', fp);                  // write char

// Binary I/O
fread(buffer, sizeof(char), count, fp);
fwrite(buffer, sizeof(char), count, fp);

// Seek
fseek(fp, 0, SEEK_SET);  // beginning
fseek(fp, 0, SEEK_END);  // end
long pos = ftell(fp);    // current position
rewind(fp);              // back to start

// Close
fclose(fp);

Error Handling

if (ferror(fp)) {
    perror("File error");
    clearerr(fp);
}

if (feof(fp)) {
    printf("End of file reached\n");
}

Compilation

GCC/Clang Basics

# Compile and link
gcc main.c -o program
./program

# Compile multiple files
gcc main.c utils.c -o program

# Compile only (create object file)
gcc -c main.c -o main.o
gcc -c utils.c -o utils.o
gcc main.o utils.o -o program

# With warnings (always use these!)
gcc -Wall -Wextra -Werror main.c -o program

# Optimization levels
gcc -O0 main.c -o program   # no optimization (debug)
gcc -O2 main.c -o program   # recommended for release
gcc -O3 main.c -o program   # aggressive optimization
gcc -Os main.c -o program   # optimize for size

# Debug symbols
gcc -g main.c -o program

# C standard
gcc -std=c99 main.c -o program
gcc -std=c11 main.c -o program
gcc -std=c17 main.c -o program
gcc -std=c23 main.c -o program

# Include paths and libraries
gcc -I./include main.c -o program
gcc main.c -lm -o program   # link math library
gcc main.c -L./lib -lmylib -o program

Header Files

// myheader.h
#ifndef MYHEADER_H
#define MYHEADER_H

// Declarations only
void my_function(int x);
extern int global_var;

typedef struct {
    int x, y;
} Point;

#endif

// myheader.c
#include "myheader.h"

int global_var = 0;

void my_function(int x) {
    // implementation
}

Simple Makefile

CC = gcc
CFLAGS = -Wall -Wextra -std=c17 -g
LDFLAGS = -lm

SRCS = main.c utils.c
OBJS = $(SRCS:.c=.o)
TARGET = program

$(TARGET): $(OBJS)
	$(CC) $(OBJS) -o $(TARGET) $(LDFLAGS)

%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

clean:
	rm -f $(OBJS) $(TARGET)

.PHONY: clean

Useful Compiler Flags

-Wall
Enable common warnings
-Wextra
Enable extra warnings
-Werror
Treat warnings as errors
-pedantic
Strict ISO C compliance
-fsanitize=address
Enable AddressSanitizer
-fsanitize=undefined
Catch undefined behavior