Patterns#

A handful of conventions help C programs stay correct and maintainable.

Header Guards#

Prevent multiple inclusion either with the classic guard or with #pragma once:

#ifndef MYLIB_H
#define MYLIB_H
/* declarations */
#endif

Opaque Pointers#

Hide implementation details by exposing only a forward declaration in the header.

// queue.h
typedef struct Queue Queue;
Queue *queue_new(void);
void   queue_free(Queue *q);

Resource Cleanup#

A single goto cleanup label keeps error paths simple:

int run(void) {
    FILE *f = NULL;
    char *buf = NULL;
    int rc = -1;

    f = fopen("data", "rb");
    if (!f) goto cleanup;
    buf = malloc(1024);
    if (!buf) goto cleanup;
    /* ... work ... */
    rc = 0;

cleanup:
    free(buf);
    if (f) fclose(f);
    return rc;
}

Return-Code Errors#

C has no exceptions. Functions return a status (0 for success, negative or errno on failure) and write results through out-parameters.

int parse_int(const char *s, int *out);

Const Correctness#

Mark inputs that won’t be modified as const so the compiler can catch accidental writes and the intent is clear.

size_t my_strlen(const char *s);

Defensive sizeof#

Use sizeof *p (matched to the variable, not the type) so renames stay correct.

T *p = malloc(n * sizeof *p);