CFieldnotes
LANGUAGE / C THE POCKET REFERENCE

C cheatsheet.

Syntax, patterns, and the details that are easy to forget.

C17

Start with the essentials. Jump to a topic when you need a quick reminder.

01

Getting started

Every C program starts with a little structure.

Your first program

hello.c
#include <stdio.h>

int main(void)
{
    puts("Hello, world!");
    return 0;
}

main is the entry point in a hosted program. Return 0 for success.

Compile & run

TERMINAL
cc -std=c17 -Wall -Wextra -Wpedantic \
   hello.c -o hello
./hello

Use GCC or Clang. These commands assume a Unix-like shell.

The punctuation

;
End a statement
{ }
Group a block of statements
//
Comment to the end of the line
/* */
Comment across multiple lines
02

Types & variables

Choose a type, give it a name, and initialize it.

Common declarations

int count = 42;
double price = 19.95;
char initial = 'C';
const int limit = 100;

#include <stdbool.h>
bool ready = true;

Single quotes hold a character. Double quotes hold a string.

Portable habits

Integer widths depend on the implementation. Use sizeof to inspect a type or object.

#include <stddef.h>

size_t bytes = sizeof(double);

sizeof returns size_t. A C byte is not guaranteed to have exactly eight bits; see CHAR_BIT in <limits.h>.

03

Operators

Calculate, compare, and combine conditions.

The everyday operators

OperatorsPurpose
+ - * / %Arithmetic; % is integer remainder
= += -= *= /=Assignment and compound assignment
== != < > <= >=Comparison; produces 0 or 1
&& || !Logical AND, OR, NOT
& | ^ ~ << >>Bitwise operations and shifts
++ --Increment and decrement
condition ? a : bChoose between two expressions

&& and || short-circuit: the right side runs only when needed.

Details that matter

int whole = 7 / 2;         // 3
double ratio = 7.0 / 2;   // 3.5
int remainder = 7 % 2;    // 1

int max = (a > b) ? a : b;

// Short-circuit the null check.
// p must otherwise point to a valid int.
if (p != NULL && *p > 0) {
    /* p points to a positive value */
}

Integer division truncates toward zero. Division by zero and signed integer overflow are undefined behavior.

Use parentheses when mixing operators. Prefer unsigned types for bit operations, and keep shift counts nonnegative and below the width of the promoted left operand.
04

Control flow

Branch when you need to. Repeat while it makes sense.

Conditions & choices

if (score >= 80) {
    puts("Great work");
} else if (score >= 50) {
    puts("Passed");
} else {
    puts("Try again");
}

switch (choice) {
case 1:
    puts("Start");
    break;
default:
    puts("Unknown choice");
    break;
}

Zero is false; nonzero is true. Without break, a switch case falls through into the next case.

Three ways to loop

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

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

do {
    ++n;
} while (n < 3);
break
Exit the nearest loop or switch
continue
Skip to the next loop iteration

A do … while loop runs at least once. Use <stdio.h> for puts and printf.

05

Functions

Declare an interface. Give each function one clear job.

Declare, define, call

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

int main(void)
{
    int result = add(2, 3);
    return result == 5 ? 0 : 1;
}

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

Declare a function before calling it. In C17, f(void) means no parameters; a declaration f() leaves the parameter types unspecified.

Values in, pointers for changes

void increment(int *value)
{
    ++*value;
}

int main(void)
{
    int count = 0;
    increment(&count);
    return 0;
}

Arguments are passed by value, including pointers. Pass an address to let a function change the caller’s object.

void
No return value
static
At file scope, a function is visible only within its translation unit
06

Arrays & strings

Contiguous elements. Zero-based indexes. Explicit bounds.

Arrays & their length

#include <stddef.h>

int values[] = {10, 20, 30};
size_t length = sizeof values
              / sizeof values[0];

for (size_t i = 0; i < length; ++i) {
    values[i] += 1;
}

int grid[2][3] = {
    {1, 2, 3}, {4, 5, 6}
};

The length formula works on an actual array. A function parameter declared as an array is adjusted to a pointer; pass its length separately.

Strings are character arrays

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

char name[] = "Ada";  // 4 bytes with '\0'
const char *label = "C";

size_t length = strlen(name); // 3
int same = strcmp(name, "Ada") == 0;

char message[32];
int written = snprintf(message,
    sizeof message, "Hi, %s", name);

A C string ends with '\0'. Don’t modify a string literal. strcmp compares contents; == on pointers compares addresses.

snprintf returns the length it would have written, excluding the terminator. A negative result is an error; a result at least as large as the buffer means truncation.
07

Pointers

An address is useful only while the object it refers to is valid.

Address & dereference

#include <stddef.h>

int value = 42;
int *p = &value;  // address of value
int copy = *p;    // read through p
*p = 7;          // changes value

int *empty = NULL;

int items[] = {10, 20, 30};
int second = *(items + 1); // items[1]

&x gets an address; *p accesses the pointed-to object. Never dereference a null, dangling, or uninitialized pointer.

Read const from the inside out

int a = 1;
int b = 2;

const int *read_only = &a;
read_only = &b;  // pointer can change
// *read_only = 3;  // not allowed

int *const fixed = &a;
*fixed = 3;       // object can change
// fixed = &b;     // not allowed

const int *const both = &a;

Pointer arithmetic stays within one array, or one past its end. A one-past pointer is valid to form, but not to dereference.

08

Memory

Every allocation needs a failure check and a clear owner.

Allocate, use, release

#include <stdlib.h>

int main(void)
{
    size_t count = 4;
    int *values = malloc(count * sizeof *values);
    if (values == NULL) {
        return EXIT_FAILURE;
    }

    values[0] = 42;
    free(values);
    values = NULL;
    return EXIT_SUCCESS;
}

malloc leaves contents uninitialized. calloc(count, size) initializes all bits to zero. Use <stdlib.h>; no allocation cast is needed in C.

For a variable count, check that count * sizeof *values cannot overflow before allocating.

Resize without losing the original

// values owns an existing allocation.
// new_count is positive; size does not overflow.
int *temporary = realloc(values,
    new_count * sizeof *values);

if (temporary != NULL) {
    values = temporary;
} else {
    // values is still valid; handle failure.
}
malloc
Allocate raw storage
calloc
Allocate and zero all bits
realloc
Resize; may move the allocation
free
Release an allocation; free(NULL) is safe

After a successful realloc, old pointers into the allocation are invalid. Avoid zero-size requests. Free each allocation exactly once.

09

Structs & enums

Give related data a shared shape and named values.

Group data with a struct

typedef struct {
    int x;
    int y;
} Point;

Point origin = {.x = 0, .y = 0};
Point *p = &origin;

origin.x = 10; // access an object
p->y = 20;    // access through a pointer

p->y is shorthand for (*p).y. Struct assignment copies member values; pointer members still point to the same objects.

Named constants & aliases

enum Status {
    STATUS_OK = 0,
    STATUS_ERROR = 1
};

enum Status result = STATUS_OK;

#include <stdint.h>
typedef uint_least32_t Counter;
Counter visits = 0;

typedef adds a name for an existing type. uint_least32_t is at least 32 bits; exact-width types such as uint32_t are optional.

Structs may contain padding. Don’t assume their memory layout is a portable file format or use memcmp for logical equality.
10

Input & output

Match formats to types, bound your input, and check results.

Format specifiers

<stdio.h>
Typeprintf outputscanf input pointerNote
int%d%dDecimal integer
unsigned int%u / %x%u / %xDecimal / hexadecimal
long / long long%ld / %lld%ld / %lldMatch the length modifier
size_t%zu%zuSizes and array lengths
float%f%fPromoted to double in printf
double%f / %g%lfscanf needs the l
char%c%cUse " %c" to skip input whitespace
Character string%s%31s for char[32]Leave space for the terminator
Pointer%p—Pass a (void *) cast to printf

For <stdint.h> types, use the PRI… and SCN… macros in <inttypes.h>. A format/type mismatch can cause undefined behavior.

Read a bounded line

#include <stdio.h>

char line[128];
if (fgets(line, sizeof line, stdin) != NULL) {
    printf("You entered: %s", line);
} else {
    // EOF or an input error.
}

fgets keeps the newline if it fits and adds a terminator on success. A long input line may arrive in several chunks.

For numeric input, read a line and parse with strtol or strtod, checking range and trailing characters. Always check scanf’s conversion count when using it.

Read a text file

#include <stdio.h>

FILE *file = fopen("notes.txt", "r");
if (file != NULL) {
    int ch;
    while ((ch = fgetc(file)) != EOF) {
        if (putchar(ch) == EOF) {
            // Handle an output error.
            break;
        }
    }
    if (ferror(file)) {
        // Handle a read error.
    }
    if (fclose(file) == EOF) {
        // Handle a close error.
    }
} else {
    perror("notes.txt");
}

Store fgetc’s result in an int to preserve EOF. Modes: r read, w create/truncate, a append; add b for binary.

11

Preprocessor

Include declarations and choose code before compilation.

Headers & include guards

point.h
#ifndef PROJECT_POINT_H
#define PROJECT_POINT_H

typedef struct {
    int x;
    int y;
} Point;

int point_distance_squared(Point p);

#endif

Use #include <stdio.h> for a standard header and #include "point.h" for a project header. Guards prevent repeated declarations.

Macros & conditional code

#define BUFFER_SIZE 256

#ifdef DEBUG
    /* Debug-only code */
#endif

// Prefer a function for calculated values.
static inline int square(int x)
{
    return x * x;
}

Macros substitute tokens. A macro such as SQUARE(i++) may evaluate its argument multiple times; a function avoids that trap.

Prefer const for typed objects and enum for integer constant expressions. const int is not an integer constant expression in C17.
12

Common pitfalls

The quick check before you trust a program.

Watch the boundaries

  • Initialize before reading. Automatic local variables are not initialized for you.
  • Check indexes. An array of n elements has indexes 0 through n - 1.
  • Leave room for '\0'. A three-character string needs at least four bytes.
  • Respect lifetimes. Don’t return a pointer to an automatic local object or use an object after free.
  • Sequence side effects. Avoid expressions such as i = i++; unsequenced modifications cause undefined behavior.
  • Use == to compare. = assigns, even inside a condition.

Let the compiler help

GCC / CLANG
cc -std=c17 -Wall -Wextra -Wpedantic \
   -g -fsanitize=address,undefined \
   -fno-omit-frame-pointer \
   program.c -o program
./program

On supported platforms, sanitizers help find memory errors and undefined behavior while you run the program. They don’t catch every bug.

Before you move on

01
Read every compiler warning
02
Check library return values
03
Test empty, boundary, and failure cases
Keep reading

C17 baseline for hosted programs; examples may be fragments. The WG14 C11 draft covers the core language and library used here. C17 incorporates defect corrections. Compiler details: GCC language options and Clang user manual.