πŸ‡―πŸ‡΅ ζ—₯本θͺž | πŸ‡ΊπŸ‡Έ English

Intermediate C Topics

For readers past the basics: const, enum, typedef, errno, variadic, conditional compilation.

1. const correctness

const is how you tell the type system "I promise not to modify this." It clarifies intent and catches mistakes at compile time.
int sum_array(const int *a, int n) {
    int s = 0;
    for (int i = 0; i < n; i++) s += a[i];
    // a[i] = 0; // compile error β€” read-only
    return s;
}

Where the const goes

const int *p β†’ *p is read-only, p can change
int * const p β†’ p is fixed, *p is writable
const int * const p β†’ neither
Trick: read around the * β€” what's on the left describes the target; what's on the right describes the pointer.

Constant declarations

const int MAX = 100;      // type-safer than #define
const double PI = 3.14159265358979;
Always mark read-only arguments as const in library APIs β€” it's a contract with your callers.

2. enum

Groups related integer constants under meaningful names. Pairs well with switch and eliminates magic numbers.
// Bad: magic number
int state = 2;
if (state == 3) { /* what's 3? */ }

// Good: enum
enum Color { RED, GREEN, BLUE };      // 0, 1, 2
enum Color c = GREEN;
if (c == RED) { /* meaning is clear */ }

Explicit values

enum HttpStatus {
    HTTP_OK = 200,
    HTTP_NOT_FOUND = 404,
    HTTP_SERVER_ERROR = 500
};

With switch

enum State { STATE_IDLE, STATE_RUN, STATE_STOP };

void handle(enum State s) {
    switch (s) {
        case STATE_IDLE: printf("idle\n"); break;
        case STATE_RUN:  printf("run\n");  break;
        case STATE_STOP: printf("stop\n"); break;
    }
}
Compiler help: -Wswitch-enum warns when a switch doesn't cover all enum values.

3. typedef

Gives an existing type a new name. Makes code cleaner and more self-documenting.
// struct typedef
typedef struct {
    int x;
    int y;
} Point;

Point p = {3, 4};

// function pointer
typedef int (*CmpFn)(const void *, const void *);
CmpFn cmp;

// arrays too
typedef char Buffer[256];
Buffer name;                  // same as char name[256]

Common standard typedefs

typedefunderlyinguse
size_tunsigned longsizes & indices
ssize_tlongsigned sizes
int32_t / uint64_tint / unsigned longexplicit width
time_tlongUNIX time
FILEstruct ...file handle
Avoid hiding pointers behind a typedef (typedef struct S *SPtr;) β€” readers can no longer tell which variables are pointers.

4. Error handling with errno

Many standard functions set the global errno when they fail. perror or strerror turns that code into a human-readable message.
#include <stdio.h>
#include <string.h>
#include <errno.h>

int main(void) {
    FILE *fp = fopen("no_such_file.txt", "r");
    if (fp == NULL) {
        perror("fopen");
        fprintf(stderr, "error %d: %s\n", errno, strerror(errno));
        return 1;
    }
    fclose(fp);
    return 0;
}

Common errno values

ConstantMeaning
ENOENTNo such file or directory
EACCESPermission denied
ENOMEMOut of memory
EINVALInvalid argument
EAGAINResource temporarily unavailable
EINTRInterrupted by a signal
Rules:
β‘  Only check errno after a function reports failure.
β‘‘ Other library calls can overwrite errno, so save it to a local variable if you need it later.
β‘’ For functions like strtol, set errno = 0; before the call so you can distinguish success from overflow.

5. Variadic functions (va_list)

How to write functions like printf that accept a variable number of arguments. Uses <stdarg.h>.
#include <stdio.h>
#include <stdarg.h>

int sum(int count, ...) {
    va_list ap;
    va_start(ap, count);
    int total = 0;
    for (int i = 0; i < count; i++) {
        total += va_arg(ap, int);
    }
    va_end(ap);
    return total;
}

int main(void) {
    printf("%d\n", sum(3, 10, 20, 30));  // 60
    printf("%d\n", sum(5, 1, 2, 3, 4, 5));  // 15
}
Important constraints:
β‘  There's no runtime way to know the types or count of variadic arguments. Caller and callee must agree (using a count as the first arg, a sentinel, or a format string like printf).
β‘‘ Pass the actual type to va_arg; a mismatch is undefined behavior.
β‘’ char and short are promoted to int β€” use va_arg(ap, int), not va_arg(ap, char).

Typical logging wrapper

void log_info(const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    fprintf(stderr, "[INFO] ");
    vfprintf(stderr, fmt, ap);
    fprintf(stderr, "\n");
    va_end(ap);
}

log_info("user %s logged in at %d", name, ts);

6. Conditional compilation (#ifdef / #if)

Switch code on or off at build time. Essential for cross-platform builds and optional debug instrumentation.

#ifdef / #ifndef

#ifdef DEBUG
    printf("x = %d\n", x);
#endif

// built with 'gcc -DDEBUG' β†’ printf enabled
// otherwise removed

#if with numeric tests

#define VERSION 3

#if VERSION >= 2
    // v2+ features
#else
    // v1 fallback
#endif

Platform branching

#if defined(_WIN32)
    #include <windows.h>
    Sleep(1000);
#elif defined(__APPLE__) || defined(__linux__)
    #include <unistd.h>
    sleep(1);
#else
    #error "unsupported platform"
#endif

Built-in macros

MacroMeaning
__FILE__current file
__LINE__current line
__func__function name (C99+)
__DATE__ / __TIME__build date/time
__STDC_VERSION__C standard version
// Handy debug macro
#define LOG(...) \
    fprintf(stderr, "[%s:%d %s] ", __FILE__, __LINE__, __func__), \
    fprintf(stderr, __VA_ARGS__), \
    fprintf(stderr, "\n")

LOG("x = %d", x);
// output: [main.c:42 main] x = 10
#pragma once: a compact alternative to the classic #ifndef FOO_H / #define / #endif include guard. Widely supported, but non-standard.

Review Quiz

Check your understanding of this lesson!

Q1. What's the difference between const int *p and int * const p?

The former makes the pointed-to value immutable; the latter makes the pointer itself immutable
They mean the same thing
The former is a compile error

The side of * that const sits on determines whether it applies to the pointed-to value or the pointer itself.

Q2. When enum values are not explicitly assigned, what is the value of the first element?

0
1
Implementation-defined / random

Without explicit values, an enum starts at 0 and increments by 1. You can assign a value partway through to change the sequence from that point on.

Q3. Which header defines the macros for handling variadic arguments?

<stdarg.h>
<variadic.h>
<args.h>

va_list, va_start, va_arg, and va_end are all defined in <stdarg.h>. That's the same header printf relies on.