For readers past the basics: const, enum, typedef, errno, variadic, conditional compilation.
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; }
const int *p β *p is read-only, p can changeint * const p β p is fixed, *p is writableconst int * const p β neither* β what's on the left describes the target; what's on the right describes the pointer.
const int MAX = 100; // type-safer than #define const double PI = 3.14159265358979;
const in library APIs β it's a contract with your callers.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 */ }
enum HttpStatus { HTTP_OK = 200, HTTP_NOT_FOUND = 404, HTTP_SERVER_ERROR = 500 };
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; } }
-Wswitch-enum warns when a switch doesn't cover all enum values.// 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]
| typedef | underlying | use |
|---|---|---|
size_t | unsigned long | sizes & indices |
ssize_t | long | signed sizes |
int32_t / uint64_t | int / unsigned long | explicit width |
time_t | long | UNIX time |
FILE | struct ... | file handle |
typedef struct S *SPtr;) β readers can no longer tell which variables are pointers.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; }
| Constant | Meaning |
|---|---|
ENOENT | No such file or directory |
EACCES | Permission denied |
ENOMEM | Out of memory |
EINVAL | Invalid argument |
EAGAIN | Resource temporarily unavailable |
EINTR | Interrupted by a signal |
errno after a function reports failure.strtol, set errno = 0; before the call so you can distinguish success from overflow.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 }
va_arg; a mismatch is undefined behavior.char and short are promoted to int β use va_arg(ap, int), not va_arg(ap, char).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);
#ifdef DEBUG printf("x = %d\n", x); #endif // built with 'gcc -DDEBUG' β printf enabled // otherwise removed
#define VERSION 3 #if VERSION >= 2 // v2+ features #else // v1 fallback #endif
#if defined(_WIN32) #include <windows.h> Sleep(1000); #elif defined(__APPLE__) || defined(__linux__) #include <unistd.h> sleep(1); #else #error "unsupported platform" #endif
| Macro | Meaning |
|---|---|
__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
#ifndef FOO_H / #define / #endif include guard. Widely supported, but non-standard.Check your understanding of this lesson!
const int *p and int * const p?The side of * that const sits on determines whether it applies to the pointed-to value or the pointer itself.
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.
va_list, va_start, va_arg, and va_end are all defined in <stdarg.h>. That's the same header printf relies on.