← All notes

C Basics Cheatsheet

Cheatsheet · 28 Sep 2026

AI-generated Written with AI, solely for personal reference.

C is a small, fast language that sits close to the hardware: you manage memory yourself, and the compiler trusts you. Snippets without a main go inside int main(void) { ... } with #include <stdio.h> at the top.

Compile and run

cc hello.c -o hello                  # compile (cc is gcc or clang)
./hello                              # run
cc -Wall -Wextra -g hello.c -o hello # show warnings, add debug info
cc -fsanitize=address -g app.c -o app  # catch memory bugs at runtime
#include <stdio.h>

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

Every statement ends with ;. Blocks use { }. Comments are /* ... */ or // ....

Types and printf formats

Type Holds Format
int Whole number %d
long Larger whole number %ld
unsigned int Non-negative whole number %u
float Decimal (about 7 digits) %f
double Decimal (about 15 digits) %f (%.2f for 2 places)
char One character %c
char[] / char * String %s
size_t Sizes and lengths %zu
pointer Memory address %p
int age = 30;
double price = 9.5;
char grade = 'A';                    /* single quotes for a char */
const int MAX = 100;                 /* can't be changed */

printf("%d %.2f %c %d\n", age, price, grade, MAX);   /* 30 9.50 A 100 */
printf("int is %zu bytes\n", sizeof(int));           /* usually 4 */

Variables aren’t set to zero automatically: always give them a value before use. bool needs #include <stdbool.h> (before C23).

Operators

printf("%d %d\n", 7 / 2, 7 % 2);     /* 3 1: int division drops the decimals */
printf("%.1f\n", 7 / 2.0);           /* 3.5: one side is a double */
printf("%.1f\n", (double)7 / 2);     /* 3.5: cast before dividing */

int n = 5;
n += 2;                              /* 7 */
n++;                                 /* 8 */
printf("%d %d %d\n", n > 5 && n < 10, n == 8, !0);   /* 1 1 1 (true is 1) */

Control flow

int score = 72;

if (score >= 80) {
    printf("A\n");
} else if (score >= 60) {
    printf("B\n");
} else {
    printf("C\n");
}

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

int count = 3;
while (count > 0) {
    count--;
}

int day = 2;
switch (day) {
    case 1:  printf("Mon\n"); break;
    case 2:  printf("Tue\n"); break;  /* without break, it falls through */
    default: printf("Other\n");
}

break leaves a loop; continue skips to its next round.

Functions

Declare (prototype) a function before using it, or define it above main.

#include <stdio.h>

int add(int a, int b);               /* prototype */

void greet(const char *name) {       /* void: returns nothing */
    printf("Hello, %s!\n", name);
}

int main(void) {
    printf("%d\n", add(2, 3));       /* 5 */
    greet("Maxine");
    return 0;
}

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

Arguments are copied into the function. To let a function change a caller’s variable, pass a pointer (below).

Arrays

int nums[5] = {10, 20, 30, 40, 50};
nums[0] = 11;                               /* indexes start at 0 */

size_t len = sizeof(nums) / sizeof(nums[0]); /* 5: only works on real arrays */
int total = 0;
for (size_t i = 0; i < len; i++) {
    total += nums[i];
}
printf("%zu items, total %d\n", len, total); /* 5 items, total 151 */

int grid[2][3] = {{1, 2, 3}, {4, 5, 6}};
printf("%d\n", grid[1][2]);                  /* 6 */

C does not check bounds: nums[5] compiles, then reads memory that isn’t yours.

Strings

A string is a char array ending in '\0' (the null terminator).

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

int main(void) {
    char name[20] = "Maxine";            /* room for 19 chars + '\0' */
    char copy[20];

    printf("%zu\n", strlen(name));       /* 6 (the '\0' isn't counted) */

    strcpy(copy, name);                  /* copy (dest must be big enough) */
    strcat(copy, "!");                   /* append */
    printf("%s\n", copy);                /* Maxine! */

    if (strcmp(name, "Maxine") == 0) {   /* compare with strcmp, not == */
        printf("match\n");
    }

    char msg[32];
    snprintf(msg, sizeof msg, "age=%d", 30);  /* safe formatting into a buffer */
    printf("%s\n", msg);                 /* age=30 */
    return 0;
}

Pointers

A pointer holds the memory address of another value.

#include <stdio.h>

void swap(int *a, int *b) {              /* receives addresses */
    int tmp = *a;                        /* * reads the value at the address */
    *a = *b;
    *b = tmp;
}

int main(void) {
    int x = 1, y = 2;
    int *p = &x;                         /* & takes an address */

    *p = 10;                             /* changes x through the pointer */
    printf("%d\n", x);                   /* 10 */

    swap(&x, &y);
    printf("%d %d\n", x, y);             /* 2 10 */

    int arr[3] = {5, 6, 7};
    int *q = arr;                        /* an array name acts as a pointer to its first item */
    printf("%d %d\n", *(q + 1), q[2]);   /* 6 7: pointer arithmetic */

    int *nothing = NULL;                 /* points nowhere; never dereference it */
    if (nothing == NULL) printf("null\n");
    return 0;
}
Syntax Meaning
int *p p is a pointer to an int
&x Address of x
*p Value at the address in p
p->field Field of the struct p points to (same as (*p).field)

Structs

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

typedef struct {
    char name[20];
    int age;
} Person;

void birthday(Person *p) {
    p->age++;                            /* -> through a pointer */
}

int main(void) {
    Person a = {"Aisha", 29};
    Person b;
    strcpy(b.name, "Ali");               /* . on the struct itself */
    b.age = 35;

    birthday(&a);
    printf("%s is %d\n", a.name, a.age); /* Aisha is 30 */
    printf("%s is %d\n", b.name, b.age); /* Ali is 35 */
    return 0;
}

Dynamic memory

Use the heap when the size is only known at runtime. Every malloc needs a matching free.

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

int main(void) {
    size_t n = 5;
    int *nums = malloc(n * sizeof *nums);    /* room for 5 ints */
    if (nums == NULL) {
        return 1;                            /* allocation failed */
    }

    for (size_t i = 0; i < n; i++) {
        nums[i] = (int)(i * i);
    }

    int *bigger = realloc(nums, 10 * sizeof *nums);  /* grow to 10 */
    if (bigger == NULL) {
        free(nums);
        return 1;
    }
    nums = bigger;

    printf("%d\n", nums[4]);                 /* 16 */
    free(nums);                              /* give the memory back */
    nums = NULL;                             /* avoid using it after free */
    return 0;
}

Splitting code into files

/* math_utils.h: declarations */
#ifndef MATH_UTILS_H
#define MATH_UTILS_H
int square(int x);
#endif

/* math_utils.c: definitions */
#include "math_utils.h"
int square(int x) { return x * x; }

/* main.c */
#include <stdio.h>
#include "math_utils.h"
int main(void) { printf("%d\n", square(4)); return 0; }
cc main.c math_utils.c -o app

Classic mistakes

Mistake What goes wrong
if (x = 5) Assigns instead of compares; use ==
Using a variable before setting it It holds garbage
arr[len] in a loop Off by one: the last index is len - 1
strcpy into a small buffer Overflows memory; size buffers carefully, prefer snprintf
== on strings Compares addresses; use strcmp
Forgetting free Memory leak
Using memory after free Crashes or silent corruption
Returning a pointer to a local variable It disappears when the function returns
Missing break in switch Falls through into the next case

Compile with -Wall -Wextra to catch many of these, and -fsanitize=address to catch memory errors while the program runs.