Skip to content

Chapter 21 of 21

Best Practices and Optimization

Write portable, testable, secure C and optimize only from measurements.

34 minutes 10 quick checksBy Subha Prasad
Lesson 21 of 21Course navigation

Lesson content

Read, practise, then check your understanding

Production C is built on explicit contracts, strong tooling, small interfaces, and measured performance. Clever syntax is less valuable than code whose lifetime and bounds are obvious.

Compile strictly

Use a declared standard and a broad warning set during development:

cc -std=c17 -Wall -Wextra -Wpedantic -Wconversion -Wshadow \
   -g -fsanitize=address,undefined source.c -o app

Treat warnings as defects after third-party boundaries are configured appropriately. Run static analysis, sanitizers, unit tests, integration tests, and fuzz tests for parsers or binary formats. Test on more than one compiler when portability matters.

Design clear interfaces

  • Carry array length and writable capacity explicitly.
  • State ownership: borrowed, transferred, or returned to the caller.
  • Use const for read-only access and narrow integer types only when required.
  • Keep module internals static; expose a small header.
  • Validate external sizes before arithmetic, allocation, indexing, or conversion.
  • Prefer status enums over overloaded sentinel values.

Document preconditions and postconditions close to declarations.

int buffer_write(struct Buffer *buffer, const void *source, size_t length) {
    if (buffer == NULL || (source == NULL && length != 0)) return 0;
    if (length > buffer->capacity - buffer->used) return 0;
    memcpy(buffer->data + buffer->used, source, length);
    buffer->used += length;
    return 1;
}

This interface checks pointers and remaining capacity before writing. The subtraction form avoids overflowing an addition such as used + length when sizes are untrusted.

Avoid undefined behavior

Common sources include signed overflow, invalid shifts, out-of-bounds access, uninitialized reads, use-after-free, data races, incorrect format specifiers, and violating aliasing rules. The compiler may optimize under the assumption that undefined behavior never occurs, so an apparently harmless mistake can change distant code.

Use %zu for size_t, <inttypes.h> macros for fixed-width integers, and checked arithmetic for untrusted lengths.

Optimize from evidence

First choose a suitable algorithm and data representation. Then measure a representative workload with a profiler. Establish a baseline and preserve correctness tests before changing code.

High-impact improvements often include:

  • reducing algorithmic complexity;
  • improving cache locality with contiguous storage;
  • eliminating unnecessary allocation and copying;
  • batching I/O;
  • moving invariant work outside loops;
  • enabling an appropriate release optimization level such as -O2.

Do not assume register, macros, or manual loop tricks beat a modern optimizer. Inspect generated code only after profiling identifies a real hotspot.

Portability and maintainability

Separate platform-dependent code behind small interfaces. Avoid assumptions about endianness, type width, structure padding, character encoding, and filesystem behavior. Pin a formatter and style, review changes in small units, and keep generated or vendored code distinct.

The fastest safe program is one that uses the right algorithm, owns resources clearly, rejects invalid input, and is continuously measured. Optimization that weakens correctness is a regression.

Knowledge check

Answer every question correctly to complete this chapter.

What should guide optimization work first?
Which compiler warnings are useful during development?
What usually produces a larger performance gain than micro-optimization?
Why establish a performance baseline?
Which build tool detects many undefined and invalid memory operations?
What should a writable buffer interface receive?
Why validate size arithmetic before allocation?
What does internal linkage help enforce?
What should portable code avoid assuming?
When is an optimization a regression?

0 of 10 checks passed

Your progress is saved on this device.