C standard library cheatsheet from first input and output to memory safety, parsing, files, callbacks, concurrency, portability, and production patterns.
libc Cheatsheet
Section titled “libc Cheatsheet”libc is the library interface that makes ordinary C programs useful: formatted I/O, memory allocation, strings, numeric conversion, files, math, time, error handling, sorting, process termination, and more.
your program -> ISO C library API -> libc implementation -> operating system/runtime <stdio.h>, <stdlib.h>, ...This page uses ISO C17 as its portable baseline. Functions marked C11 require C11 support; names marked POSIX are common on Unix-like systems but are not part of ISO C. C23 and implementation-specific additions are noted only where useful.
| Name | Meaning |
|---|---|
| ISO C standard library | The portable interfaces declared by standard C headers |
libc | An implementation of much of that library plus platform extensions |
| glibc | Common GNU/Linux libc implementation |
| musl | Small Linux libc implementation often used in static containers |
| MSVC Universal CRT | The Microsoft C runtime library on modern Windows |
| POSIX API | Additional Unix-like interfaces such as getline, strdup, and localtime_r |
In C++, headers such as <cstdio> and <cstdlib> expose corresponding C facilities in the C++ standard library. This page is about writing C.
1. Beginner: First Program
Section titled “1. Beginner: First Program”Hello, libc
Section titled “Hello, libc”#include <stdio.h>
int main(void) { puts("Hello, libc!"); return 0;}cc -std=c17 -Wall -Wextra -Wpedantic hello.c -o hello./helloputs writes text followed by a newline to standard output.
Standard Streams
Section titled “Standard Streams”Every hosted C program begins with three text streams already available:
| Stream | Purpose | Common use |
|---|---|---|
stdin | Standard input | Keyboard input or piped data |
stdout | Standard output | Normal results |
stderr | Standard error | Diagnostics and errors |
#include <stdio.h>
int main(void) { fprintf(stdout, "normal result\n"); fprintf(stderr, "diagnostic message\n"); return 0;}Keep diagnostics on stderr so users can redirect program output separately:
./app >results.txt 2>errors.txtInclude the Declaring Header
Section titled “Include the Declaring Header”Do not rely on implicit declarations or transitive includes. Include the header that declares the function, type, or macro you use:
#include <stdio.h> // printf, FILE#include <stdlib.h> // malloc, strtol, EXIT_SUCCESS#include <string.h> // strlen, memcpy2. Library Map
Section titled “2. Library Map”Everyday Headers
Section titled “Everyday Headers”| Header | Main purpose | Representative names |
|---|---|---|
<stdio.h> | Streams and formatted I/O | printf, fgets, fopen, fread |
<stdlib.h> | Allocation, conversion, algorithms, termination | malloc, strtol, qsort, exit |
<string.h> | Byte strings and raw memory | strlen, strcmp, memcpy, memmove |
<ctype.h> | Byte character classification/conversion | isdigit, isspace, tolower |
<errno.h> | Error indicator macro | errno, ERANGE |
<assert.h> | Debug assertions | assert |
<limits.h> | Integer limits | INT_MAX, CHAR_BIT |
<stdint.h> | Exact/least/fast width integers | uint32_t, INT64_MAX |
<inttypes.h> | Portable integer formatting | PRIu64, SCNd64 |
<stddef.h> | Core types and macros | size_t, ptrdiff_t, NULL, offsetof |
<stdbool.h> | Boolean spelling through C17 | bool, true, false |
Specialized Headers
Section titled “Specialized Headers”| Header | Purpose | Representative names |
|---|---|---|
<math.h> | Floating-point mathematics | sqrt, sin, isfinite |
<float.h> | Floating-point limits | DBL_EPSILON, DBL_MAX |
<time.h> | Calendar and processor time | time, strftime, clock |
<locale.h> | Locale selection/querying | setlocale, localeconv |
<signal.h> | Signals | signal, raise, SIGINT |
<setjmp.h> | Non-local jumps | setjmp, longjmp |
<stdarg.h> | Variadic argument access | va_list, va_start |
<wchar.h> | Wide strings and wide I/O | wchar_t, wprintf |
<wctype.h> | Wide character classification | iswalpha, towupper |
<complex.h> | Complex arithmetic | double complex, cabs |
<fenv.h> | Floating-point environment | feclearexcept, fetestexcept |
<tgmath.h> | Type-generic math macros | sqrt-style dispatch |
<stdatomic.h> | Atomics, C11 | atomic_int, atomic_fetch_add |
<threads.h> | Threads, C11 optional in implementations | thrd_create, mtx_lock |
<uchar.h> | UTF-related character conversion, C11 | char16_t, mbrtoc16 |
<stdalign.h> | Alignment convenience macros, C11 | alignas, alignof |
<stdnoreturn.h> | Non-returning function macro, C11 | noreturn |
<iso646.h> | Alternative operator macro spellings | and, or, not |
Freestanding Versus Hosted C
Section titled “Freestanding Versus Hosted C”A hosted implementation provides the complete standard-library environment expected by desktop/server applications. A freestanding implementation, often used for kernels or embedded firmware, only guarantees a smaller language/library subset. Check your platform before assuming files, allocation, signals, or a console exist.
3. Formatted Output
Section titled “3. Formatted Output”printf Family
Section titled “printf Family”| Function | Destination | Notes |
|---|---|---|
printf(format, ...) | stdout | Formatted terminal/redirected output |
fprintf(stream, format, ...) | A FILE * | Use for files or stderr |
snprintf(buf, size, format, ...) | Character array | Bounded formatted output |
sprintf(buf, format, ...) | Character array | Avoid: has no buffer-size argument |
#include <stdio.h>
int main(void) { const char *name = "Mira"; int score = 42; double ratio = 0.875;
printf("%s scored %d (%.1f%%)\n", name, score, ratio * 100.0); fprintf(stderr, "loaded user: %s\n", name);
char line[64]; int needed = snprintf(line, sizeof line, "%s:%d", name, score); if (needed < 0) { fputs("formatting failed\n", stderr); return 1; } if ((size_t)needed >= sizeof line) { fputs("formatted line was truncated\n", stderr); return 1; } puts(line);}snprintf returns the number of characters that would have been written, excluding the terminating null character. A nonnegative result at least as large as the capacity means truncation.
Output Format Specifiers
Section titled “Output Format Specifiers”| Value type | Specifier | Example |
|---|---|---|
int | %d or %i | printf("%d", n); |
unsigned int | %u | printf("%u", n); |
long / unsigned long | %ld / %lu | printf("%ld", n); |
long long / unsigned long long | %lld / %llu | printf("%lld", n); |
| Hexadecimal unsigned | %x, %X | printf("%08x", bits); |
double | %f, %e, %g | printf("%.3f", value); |
long double | %Lf, %Le, %Lg | printf("%Lg", value); |
| Character | %c | printf("%c", ch); |
| Null-terminated string | %s | printf("%s", text); |
Pointer converted to void * | %p | printf("%p", (void *)ptr); |
| Literal percent sign | %% | printf("90%%"); |
size_t | %zu | printf("%zu", count); |
ptrdiff_t | %td | printf("%td", distance); |
For fixed-width integer types, use <inttypes.h> macros:
#include <inttypes.h>#include <stdint.h>#include <stdio.h>
uint64_t bytes = UINT64_C(9000000000);printf("bytes=%" PRIu64 "\n", bytes);Width and Precision
Section titled “Width and Precision”printf("|%8d|\n", 17); // Right aligned: | 17|printf("|%-8d|\n", 17); // Left aligned: |17 |printf("|%08x|\n", 0x2a); // Zero padded: |0000002a|printf("%.2f\n", 3.14159); // 3.14printf("%.5s\n", "library"); // libraNever let untrusted data become the format string:
printf("%s", user_text); // Correct// printf(user_text); // Format-string vulnerability4. Input and Parsing
Section titled “4. Input and Parsing”Read Lines with fgets
Section titled “Read Lines with fgets”For interactive or text input, read a line first, then parse it. This gives you control over malformed input and leftover characters.
#include <stdio.h>
int main(void) { char line[128];
fputs("Name: ", stdout); fflush(stdout);
if (fgets(line, sizeof line, stdin) == NULL) { if (ferror(stdin)) { perror("stdin"); } return 1; }
printf("You entered: %s", line); return 0;}fgets stores the newline when it fits. Remove one trailing newline when needed:
#include <string.h>
line[strcspn(line, "\n")] = '\0';If a line is longer than the buffer, fgets returns a partial line. A robust program detects the missing newline and consumes or handles the remainder.
Parse Integers with strtol
Section titled “Parse Integers with strtol”atoi cannot distinguish invalid input from a valid zero and gives poor range handling. Prefer strtol, strtoul, strtoll, or strtoull.
#include <errno.h>#include <limits.h>#include <stdint.h>#include <stdio.h>#include <stdlib.h>
int parse_int(const char *text, int *out) { char *end; long value;
errno = 0; value = strtol(text, &end, 10);
if (end == text) { return 0; // No digits } if (errno == ERANGE || value < INT_MIN || value > INT_MAX) { return 0; // Out of int range } while (*end == ' ' || *end == '\t' || *end == '\n') { ++end; } if (*end != '\0') { return 0; // Unexpected suffix }
*out = (int)value; return 1;}| Function | Parses | Useful base behavior |
|---|---|---|
strtol, strtoll | Signed integer | Base 10 for decimal; base 0 recognizes common prefixes |
strtoul, strtoull | Unsigned integer | Validate whether negative spelling should be rejected |
strtof, strtod, strtold | Floating point | Check end and ERANGE |
scanf When the Format Is Truly Fixed
Section titled “scanf When the Format Is Truly Fixed”Formatted scanning is useful for controlled formats, but checking its return count is mandatory:
int x;double y;
if (sscanf("12 3.5", "%d %lf", &x, &y) == 2) { printf("%d %.1f\n", x, y);}| Scan type | Specifier |
|---|---|
int * | %d |
unsigned int * | %u |
long * | %ld |
long long * | %lld |
float * | %f |
double * | %lf |
long double * | %Lf |
char * single character | %c |
Limit string input width so room remains for '\0':
char word[32];if (scanf("%31s", word) == 1) { puts(word);}For user-entered lines with spaces or recovery from errors, fgets plus conversion functions is generally easier to make reliable than scanf.
5. Strings and Raw Memory
Section titled “5. Strings and Raw Memory”C Strings
Section titled “C Strings”A C string is an array of char terminated by a null character:
char mutable_text[] = "cat"; // {'c', 'a', 't', '\0'}, writable arrayconst char *literal = "cat"; // Do not modify string-literal storage| Function | Meaning | Important condition |
|---|---|---|
strlen(s) | Number of characters before '\0' | s must be terminated |
strcmp(a, b) | Lexicographic comparison | Result is negative, zero, or positive |
strncmp(a, b, n) | Compare up to n characters | Not a general constant-time comparison |
strchr(s, c) | Find first character | Returns pointer or NULL |
strrchr(s, c) | Find last character | Returns pointer or NULL |
strstr(s, needle) | Find substring | Returns pointer or NULL |
strspn(s, accept) | Initial accepted run length | Useful for token validation |
strcspn(s, reject) | Initial run before rejected char | Useful for stripping newline |
strpbrk(s, chars) | Find any listed char | Returns pointer or NULL |
Copy and Concatenate Carefully
Section titled “Copy and Concatenate Carefully”The destination must always be large enough for copied text and its terminating null character.
#include <stdio.h>#include <string.h>
int make_label(char *dst, size_t cap, const char *name, unsigned id) { int n = snprintf(dst, cap, "%s-%u", name, id); return n >= 0 && (size_t)n < cap;}| Function | Trap |
|---|---|
strcpy, strcat | Cannot know destination capacity; safe only after a proven size check |
strncpy | May leave destination unterminated and pads remaining space; it is not a simple safe strcpy |
strncat | Third argument counts appended characters, not destination capacity |
For constructing text, a checked snprintf is often clearer than a chain of copies and appends.
Memory Operations
Section titled “Memory Operations”Unlike string operations, memory operations work with an explicit number of bytes and do not care about '\0'.
| Function | Purpose | Rule |
|---|---|---|
memcpy(dst, src, n) | Copy n bytes | Source and destination must not overlap |
memmove(dst, src, n) | Copy n bytes | Handles overlap |
memset(dst, byte, n) | Set each byte | A byte fill, not general typed initialization |
memcmp(a, b, n) | Compare bytes | Useful for byte representations/data, not padded structs in general |
memchr(data, byte, n) | Find a byte | Works on non-string data |
int values[] = {10, 20, 30, 40};memmove(&values[1], &values[0], 3 * sizeof values[0]);// values is now {10, 10, 20, 30}Do not use memcpy for overlapping portions of one array; overlapping arguments invoke undefined behavior.
Tokenization
Section titled “Tokenization”strtok modifies its input string and keeps hidden iteration state:
#include <stdio.h>#include <string.h>
char csv[] = "red,green,blue";for (char *part = strtok(csv, ","); part != NULL; part = strtok(NULL, ",")) { puts(part);}This is acceptable for simple one-at-a-time parsing. It does not preserve empty fields and is awkward for nested or concurrent parsing. POSIX strtok_r and Microsoft’s strtok_s are platform-specific alternatives with different portability considerations.
6. Character Classification
Section titled “6. Character Classification”<ctype.h> classifies or maps single-byte character values in the current locale.
| Function | Test or conversion |
|---|---|
isalpha(c) | Letter |
isdigit(c) | Decimal digit |
isalnum(c) | Letter or digit |
isspace(c) | Whitespace |
isxdigit(c) | Hexadecimal digit |
isprint(c) | Printable including space |
tolower(c) | Lowercase mapping if available |
toupper(c) | Uppercase mapping if available |
The argument must be EOF or representable as unsigned char. Cast potentially signed char data:
#include <ctype.h>
for (char *p = text; *p != '\0'; ++p) { *p = (char)tolower((unsigned char)*p);}ctype is not a UTF-8 string-processing library. UTF-8 characters can occupy multiple bytes.
7. Dynamic Memory
Section titled “7. Dynamic Memory”Allocation API
Section titled “Allocation API”| Function | Meaning |
|---|---|
malloc(bytes) | Allocate uninitialized storage |
calloc(count, size) | Allocate storage whose bytes are initialized to zero |
realloc(ptr, bytes) | Resize allocation, possibly moving it |
free(ptr) | Release allocation; free(NULL) is allowed |
aligned_alloc(alignment, size) | C11 aligned allocation; size requirements apply |
In C, do not cast malloc’s void * result; including <stdlib.h> is what provides the correct declaration.
#include <stdint.h>#include <stdio.h>#include <stdlib.h>
int main(void) { size_t count = 100;
if (count > SIZE_MAX / sizeof(int)) { fputs("requested array is too large\n", stderr); return EXIT_FAILURE; }
int *values = calloc(count, sizeof *values); if (values == NULL) { perror("calloc"); return EXIT_FAILURE; }
values[0] = 7; free(values); values = NULL; return EXIT_SUCCESS;}Include <stdint.h> for SIZE_MAX where it is provided, or use count > (size_t)-1 / sizeof *values when writing to environments with different header availability.
Correct realloc Pattern
Section titled “Correct realloc Pattern”If realloc fails for a nonzero request, the original allocation remains valid. Do not overwrite its only pointer until success:
if (count > SIZE_MAX / 2) { free(values); return EXIT_FAILURE;}size_t new_count = count * 2;if (new_count > SIZE_MAX / sizeof *values) { free(values); return EXIT_FAILURE;}
int *grown = realloc(values, new_count * sizeof *values);if (grown == NULL) { free(values); return EXIT_FAILURE;}
values = grown;count = new_count;Avoid using realloc(ptr, 0) as a portable free idiom; zero-size behavior has varied across standards and implementations. Write free(ptr) explicitly.
Ownership Rules
Section titled “Ownership Rules”| Rule | Why it matters |
|---|---|
| Decide who owns each allocation | Prevents leaks and double frees |
| Free exactly once after the last use | Prevents dangling-pointer access |
| Never free stack storage or string literals | Only allocated blocks may be freed |
Store allocation counts as size_t | Matches library sizes and sizeof |
Check count * sizeof element for overflow | Prevents undersized allocations |
Do not read uninitialized malloc memory | Its values are indeterminate |
Common Memory Bugs
Section titled “Common Memory Bugs”| Bug | Example symptom | Detection tool |
|---|---|---|
| Out-of-bounds write | Crash later or corrupt output | AddressSanitizer |
| Use after free | Intermittent failures | AddressSanitizer |
| Leak | Growing memory consumption | LeakSanitizer or Valgrind |
| Double free | Allocator abort/crash | AddressSanitizer |
| Integer overflow in allocation size | Small allocation followed by overflow | Warnings, reviews, sanitizers |
cc -std=c17 -Wall -Wextra -Wpedantic -fsanitize=address,undefined -g app.c -o app./appSanitizer availability and flag spelling depend on the compiler and platform.
8. Files and Streams
Section titled “8. Files and Streams”Opening and Closing Files
Section titled “Opening and Closing Files”#include <stdio.h>#include <stdlib.h>
int main(void) { FILE *fp = fopen("notes.txt", "r"); if (fp == NULL) { perror("notes.txt"); return EXIT_FAILURE; }
char line[256]; while (fgets(line, sizeof line, fp) != NULL) { fputs(line, stdout); }
if (ferror(fp)) { perror("reading notes.txt"); fclose(fp); return EXIT_FAILURE; }
if (fclose(fp) == EOF) { perror("closing notes.txt"); return EXIT_FAILURE; } return EXIT_SUCCESS;}| Mode | Meaning | Existing contents |
|---|---|---|
"r" | Read text | File must exist |
"w" | Write text | Truncated or created |
"a" | Append text | Created if absent; writes go at end |
"r+" | Read/write text | File must exist |
"w+" | Read/write text | Truncated or created |
"a+" | Read/append text | Created if absent |
"rb", "wb", etc. | Binary equivalents | Important on systems distinguishing text and binary |
Character, Line, Formatted, and Block I/O
Section titled “Character, Line, Formatted, and Block I/O”| Task | Reading | Writing |
|---|---|---|
| One character | fgetc | fputc |
| Text line/string | fgets | fputs |
| Formatted values | fscanf | fprintf |
| Blocks/records | fread | fwrite |
Do not write while (!feof(fp)). EOF is set only after an attempted read fails. Loop on the read operation:
unsigned char buf[4096];size_t n;
while ((n = fread(buf, 1, sizeof buf, fp)) != 0) { if (fwrite(buf, 1, n, out) != n) { perror("write"); /* handle error */ }}if (ferror(fp)) { perror("read");}File Position and Buffering
Section titled “File Position and Buffering”| Function | Purpose |
|---|---|
fseek(fp, offset, origin) | Seek in a stream where supported |
ftell(fp) | Obtain current position for matching use with fseek |
rewind(fp) | Return to start and clear status indicators |
fgetpos, fsetpos | Portable stream position save/restore |
fflush(fp) | Push pending output for an output/update stream |
setvbuf(fp, buffer, mode, size) | Configure buffering before I/O |
feof(fp) | Test EOF indicator after a read failed |
ferror(fp) | Test error indicator |
clearerr(fp) | Clear EOF and error indicators |
fflush(stdin) is not a portable way to discard unread input; its standard use is for output streams and certain update-stream transitions.
Temporary Files and File Operations
Section titled “Temporary Files and File Operations”| Function | Purpose | Note |
|---|---|---|
remove(path) | Remove a file | Exact filesystem behavior is platform-dependent |
rename(old, new) | Rename a file | Replacement behavior may vary |
tmpfile() | Create an automatically removed temporary binary file | Check for NULL |
tmpnam() | Generate a temporary name | Avoid for secure programs because of race risk |
9. Errors, Assertions, and Termination
Section titled “9. Errors, Assertions, and Termination”Return Values First, errno Second
Section titled “Return Values First, errno Second”errno is meaningful only when a function documents that it reports errors through it and its return value indicates failure. A successful call need not clear old errno values.
#include <errno.h>#include <stdio.h>#include <stdlib.h>
FILE *fp = fopen("settings.txt", "r");if (fp == NULL) { int saved = errno; errno = saved; perror("settings.txt");}| Facility | Use |
|---|---|
errno | Integer-like macro holding an error code for the current execution context |
perror(prefix) | Print prefix plus message corresponding to current errno |
strerror(error_number) | Obtain error-message text |
EDOM | Domain error reported by some math functions |
ERANGE | Range error from conversions or math functions |
Assertions
Section titled “Assertions”Use assert to reveal programmer mistakes and broken internal assumptions, not expected bad user input:
#include <assert.h>
double average(const int *values, size_t count) { assert(values != NULL); assert(count != 0); /* compute result */ return 0.0;}Compiling with -DNDEBUG disables assert evaluations. Never put required side effects inside an assertion.
Program Termination
Section titled “Program Termination”| Function or macro | Behavior |
|---|---|
return EXIT_SUCCESS; from main | Normal successful termination |
return EXIT_FAILURE; from main | Portable indication of failure |
exit(status) | Normal termination; flushes/closes standard streams and runs atexit callbacks |
_Exit(status) | Immediate C99 termination without normal cleanup |
abort() | Abnormal termination, generally signaling a fatal internal condition |
atexit(callback) | Register normal-exit callbacks in reverse registration order |
#include <stdio.h>#include <stdlib.h>
static void report_shutdown(void) { fputs("shutdown complete\n", stderr);}
int main(void) { if (atexit(report_shutdown) != 0) { return EXIT_FAILURE; } return EXIT_SUCCESS;}10. Numeric Conversion, Randomness, and Algorithms
Section titled “10. Numeric Conversion, Randomness, and Algorithms”Conversion Summary
Section titled “Conversion Summary”| Function | Converts |
|---|---|
strtol, strtoll | Text to signed integer |
strtoul, strtoull | Text to unsigned integer |
strtof, strtod, strtold | Text to floating point |
atoi, atol, atoll, atof | Simpler conversion without useful error reporting; avoid for validated input |
Pseudorandom Numbers
Section titled “Pseudorandom Numbers”rand is suitable for simple simulation examples, not passwords, keys, tokens, nonces, or security decisions.
#include <stdio.h>#include <stdlib.h>#include <time.h>
int main(void) { srand((unsigned)time(NULL)); int die = rand() % 6 + 1; // Small demonstration only; may have modulo bias printf("%d\n", die);}Use the operating system’s cryptographically secure random interface for security-sensitive values; that interface is outside ISO C.
Sorting with qsort
Section titled “Sorting with qsort”#include <stdio.h>#include <stdlib.h>
static int compare_ints(const void *left, const void *right) { int a = *(const int *)left; int b = *(const int *)right; return (a > b) - (a < b); // Avoid subtraction overflow}
int main(void) { int values[] = {7, 2, 9, 2, -1}; size_t count = sizeof values / sizeof values[0];
qsort(values, count, sizeof values[0], compare_ints);
for (size_t i = 0; i < count; ++i) { printf("%d%s", values[i], i + 1 == count ? "\n" : " "); }}Binary Search with bsearch
Section titled “Binary Search with bsearch”The input array must already be sorted under the same comparison ordering:
int key = 7;int *found = bsearch(&key, values, count, sizeof values[0], compare_ints);if (found != NULL) { puts("found");}11. Math and Floating Point
Section titled “11. Math and Floating Point”Compile and Link
Section titled “Compile and Link”On many Unix-like toolchains, math functions require linking with -lm:
cc -std=c17 geometry.c -lm -o geometryCommon <math.h> Functions
Section titled “Common <math.h> Functions”| Function family | Examples |
|---|---|
| Roots and powers | sqrt, cbrt, pow, hypot |
| Exponential/logarithmic | exp, log, log10, log2 |
| Trigonometric | sin, cos, tan, atan2 |
| Rounding | floor, ceil, trunc, round, nearbyint |
| Absolute/remainder | fabs, fmod, remainder |
| Decomposition/scaling | frexp, ldexp, modf, scalbn |
| Classification | isfinite, isinf, isnan, fpclassify, signbit |
Functions normally use double; suffix f selects float and suffix l selects long double, such as sqrtf and sqrtl.
#include <math.h>#include <stdio.h>
double distance(double x, double y) { return hypot(x, y);}
int main(void) { double value = sqrt(2.0); if (isfinite(value)) { printf("%.17g\n", value); }}Floating-Point Rules of Thumb
Section titled “Floating-Point Rules of Thumb”| Topic | Guidance |
|---|---|
| Equality | Many computed decimals cannot be compared reliably using exact == |
| Precision | Use DBL_EPSILON from <float.h> to understand representation, not as a universal tolerance |
| NaN | Comparisons involving a NaN are unusual; test using isnan |
| Overflow | Results can become infinity; use isfinite when required |
| Domain errors | Calls such as sqrt(-1.0) may report domain issues; requirements depend on the math environment |
12. Time, Locale, and Text Encodings
Section titled “12. Time, Locale, and Text Encodings”Time Basics
Section titled “Time Basics”| Function/type | Purpose |
|---|---|
time_t | Calendar-time representation |
time(NULL) | Obtain current calendar time where available |
struct tm | Broken-down calendar fields |
gmtime | Convert to UTC broken-down time |
localtime | Convert using local time zone |
mktime | Convert local broken-down time back to time_t |
difftime | Difference in seconds between calendar times |
strftime | Format a struct tm into text |
clock | Processor time used by the program, not wall-clock timing |
timespec_get | C11 calendar time into struct timespec |
#include <stdio.h>#include <time.h>
int main(void) { time_t now = time(NULL); struct tm *local = localtime(&now); char text[64];
if (local == NULL || strftime(text, sizeof text, "%Y-%m-%d %H:%M:%S", local) == 0) { return 1; }
puts(text);}localtime and gmtime may share internal storage overwritten by a later conversion. POSIX provides localtime_r/gmtime_r; Microsoft provides differently ordered _s variants. Copy the struct tm promptly or use the relevant platform wrapper in concurrent code.
Locale
Section titled “Locale”The program begins in the "C" locale. Locale affects facilities such as character classification, collation, numeric formatting, and formatted input/output.
#include <locale.h>
if (setlocale(LC_ALL, "") == NULL) { /* requested environment locale was unavailable */}Changing global locale can make reusable libraries and threaded programs surprising. Decide explicitly whether a program needs localized behavior or stable machine-readable formatting.
Wide and Multibyte Text
Section titled “Wide and Multibyte Text”| Header | Handles |
|---|---|
<wchar.h> | Wide characters/strings, multibyte conversions, wide streams |
<wctype.h> | Wide character classification |
<uchar.h> | char16_t/char32_t conversion facilities in C11 |
The C library’s multibyte behavior depends on the active locale. A byte-oriented UTF-8 application often uses a dedicated Unicode library for normalization, grapheme handling, case folding, and robust validation.
13. Variadic Functions
Section titled “13. Variadic Functions”<stdarg.h> lets a function accept an argument list whose types are specified by a convention, commonly a format string.
#include <stdarg.h>#include <stdio.h>
static void log_error(const char *format, ...) { va_list args;
fputs("error: ", stderr); va_start(args, format); vfprintf(stderr, format, args); va_end(args); fputc('\n', stderr);}
int main(void) { log_error("could not open %s (attempt %d)", "config.ini", 2);}| Macro/function | Meaning |
|---|---|
va_list | Iterator state for unnamed arguments |
va_start(args, last_named) | Begin consuming unnamed arguments |
va_arg(args, type) | Obtain next argument using its promoted type |
va_copy(dst, src) | C99 duplicate iterator state before consuming separately |
va_end(args) | Finish each initialized/copied iterator |
vprintf, vfprintf, vsnprintf | Forward an existing va_list to formatting |
Variadic arguments carry no automatic type metadata. The caller and function must agree exactly through a protocol such as a format string.
14. Signals and Non-Local Jumps
Section titled “14. Signals and Non-Local Jumps”Signals
Section titled “Signals”ISO C exposes a small portable signal interface:
#include <signal.h>
static volatile sig_atomic_t stop_requested = 0;
static void request_stop(int signal_number) { (void)signal_number; stop_requested = 1;}
int main(void) { if (signal(SIGINT, request_stop) == SIG_ERR) { return 1; }
while (!stop_requested) { /* perform work in small units */ }}Inside a signal handler, keep operations minimal. Assigning to a volatile sig_atomic_t object is the central portable pattern; ordinary allocation and stdio operations are not safe assumptions there. POSIX programs usually use the more capable sigaction interface.
setjmp and longjmp
Section titled “setjmp and longjmp”setjmp records an execution environment; longjmp returns to it without normal call-stack unwinding:
#include <setjmp.h>
jmp_buf recovery_point;
if (setjmp(recovery_point) == 0) { /* work that may call longjmp(recovery_point, 1) */} else { /* recovery path */}Use this facility sparingly. It makes ownership and cleanup harder, and automatic local values changed after setjmp require special care.
15. C11 Atomics and Threads
Section titled “15. C11 Atomics and Threads”Atomics
Section titled “Atomics”<stdatomic.h> supplies operations for data intentionally shared between threads without ordinary data races:
#include <stdatomic.h>
atomic_uint requests = 0;
void note_request(void) { atomic_fetch_add(&requests, 1);}| Name | Meaning |
|---|---|
atomic_int, atomic_uint | Convenient atomic integer types |
atomic_init | Initialize an atomic object where needed |
atomic_load, atomic_store | Read/write atomically |
atomic_fetch_add | Atomic read-modify-write |
atomic_compare_exchange_strong | Conditional update |
memory_order_* | Explicit ordering controls for advanced synchronization |
Default sequential consistency is easier to reason about. Use weaker memory ordering only with a proven synchronization design.
Threads
Section titled “Threads”The C11 <threads.h> interface is optional in implementations, so some platforms use POSIX threads or Windows threading APIs instead.
#if !defined(__STDC_NO_THREADS__)#include <stdio.h>#include <threads.h>
static int worker(void *unused) { (void)unused; puts("working"); return 0;}
int main(void) { thrd_t thread; int result;
if (thrd_create(&thread, worker, NULL) != thrd_success) { return 1; } if (thrd_join(thread, &result) != thrd_success) { return 1; } return result;}#endif| Facility | Purpose |
|---|---|
thrd_create, thrd_join | Run/join a thread |
mtx_t, mtx_init, mtx_lock, mtx_unlock | Mutual exclusion |
cnd_t, cnd_wait, cnd_signal | Condition variables |
tss_t | Thread-specific storage |
call_once | One-time initialization |
Library calls that mutate shared global state, such as global locale changes, require special design in threaded programs.
16. Portability: ISO C, POSIX, and Extensions
Section titled “16. Portability: ISO C, POSIX, and Extensions”Know Which Layer You Use
Section titled “Know Which Layer You Use”| Need | ISO C option | Common non-ISO option |
|---|---|---|
| Read bounded line | fgets | POSIX getline dynamically grows a line buffer |
| Duplicate string | Allocate plus memcpy | POSIX strdup |
| Thread-safe time conversion | Copy promptly / synchronize | POSIX localtime_r, Microsoft localtime_s |
| Environment lookup | getenv | Modification APIs differ by platform |
| Processes/pipes/directories/sockets | Not generally supplied by ISO C | POSIX or Windows APIs |
| Secure random bytes | Not supplied by ISO C | Operating-system APIs |
Feature Tests and Platform Checks
Section titled “Feature Tests and Platform Checks”Prefer configuration tests in a build system over guessing from operating-system macros. When using POSIX APIs, request the appropriate declarations before system headers on relevant systems:
#define _POSIX_C_SOURCE 200809L#include <stdio.h>#include <stdlib.h>Project configuration tools such as Autoconf or CMake can check whether functions and headers really exist for the selected compiler and target.
Environment and Command Execution
Section titled “Environment and Command Execution”| Function | Purpose | Warning |
|---|---|---|
getenv(name) | Read environment variable | Returned storage must not be modified; subsequent environment operations may invalidate it |
system(command) | Invoke implementation command processor | Avoid with untrusted input; quoting and behavior are platform-specific |
17. Undefined Behavior and Security Checklist
Section titled “17. Undefined Behavior and Security Checklist”The library cannot protect a program whose arguments violate an API contract. In C, a bug can silently become memory corruption or exploitable behavior.
| Avoid | Prefer |
|---|---|
gets | Never use it; read with fgets or an appropriate platform line reader |
sprintf into fixed buffers | Checked snprintf |
strcpy/strcat with unknown lengths | Size first or construct using checked formatting |
printf(user_input) | printf("%s", user_input) |
atoi for validated input | strtol family with end/range checks |
while (!feof(fp)) | Loop on fgets or fread result |
tolower(ch) for possibly negative char | tolower((unsigned char)ch) |
memcpy with overlapping regions | memmove |
qsort comparator return a - b; | (a > b) - (a < b) |
ptr = realloc(ptr, size); | Assign through a temporary pointer |
| Multiplying allocation dimensions unchecked | Check against SIZE_MAX before allocation |
Assuming sizeof(int) == 4 | Use limits/fixed-width types only where required and available |
fflush(stdin) | Handle input by reading and parsing explicitly |
rand() for secrets | Platform secure randomness |
Compiler Assistance
Section titled “Compiler Assistance”cc -std=c17 -Wall -Wextra -Wpedantic -Wconversion -g app.c -o appcc -std=c17 -Wall -Wextra -Wpedantic -fsanitize=address,undefined -g app.c -o appFor optimized deployments on toolchains that support them, hardening flags may add defenses around some libc misuse. They supplement correct bounds and ownership logic; they do not replace it.
18. Practical Patterns
Section titled “18. Practical Patterns”Duplicate a String Portably
Section titled “Duplicate a String Portably”#include <stdint.h>#include <stdlib.h>#include <string.h>
char *copy_string(const char *source) { size_t length = strlen(source); if (length == SIZE_MAX) { return NULL; }
char *copy = malloc(length + 1); if (copy != NULL) { memcpy(copy, source, length + 1); } return copy;}For this particular expression, strlen(source) == SIZE_MAX cannot describe a usable C object in practice, but the guard documents the + 1 size rule and keeps the pattern applicable to computed lengths.
Read All of a Text File Incrementally
Section titled “Read All of a Text File Incrementally”#include <stdint.h>#include <stdio.h>#include <stdlib.h>
char *read_text(FILE *fp) { size_t used = 0; size_t cap = 4096; char *data = malloc(cap); if (data == NULL) { return NULL; }
for (;;) { if (used + 1 == cap) { if (cap > SIZE_MAX / 2) { free(data); return NULL; } size_t new_cap = cap * 2; char *new_data = realloc(data, new_cap); if (new_data == NULL) { free(data); return NULL; } data = new_data; cap = new_cap; }
size_t n = fread(data + used, 1, cap - used - 1, fp); used += n;
if (n == 0) { if (ferror(fp)) { free(data); return NULL; } data[used] = '\0'; return data; } }}This text helper appends '\0'. For arbitrary binary files, return both a byte buffer and its explicit length instead of treating data as a string.
Parse One Configuration Line
Section titled “Parse One Configuration Line”#include <ctype.h>#include <string.h>
int split_setting(char *line, char **key, char **value) { line[strcspn(line, "\r\n")] = '\0';
char *equals = strchr(line, '='); if (equals == NULL) { return 0; }
*equals = '\0'; char *left = line; char *right = equals + 1;
while (isspace((unsigned char)*left)) { ++left; } while (isspace((unsigned char)*right)) { ++right; }
char *end = left + strlen(left); while (end != left && isspace((unsigned char)end[-1])) { *--end = '\0'; }
*key = left; *value = right; return *left != '\0';}Growable Integer Vector
Section titled “Growable Integer Vector”#include <stdint.h>#include <stdlib.h>
struct int_vector { int *items; size_t length; size_t capacity;};
int int_vector_push(struct int_vector *v, int value) { if (v->length == v->capacity) { size_t next = v->capacity == 0 ? 8 : v->capacity * 2; if (next < v->capacity || next > SIZE_MAX / sizeof *v->items) { return 0; } int *items = realloc(v->items, next * sizeof *items); if (items == NULL) { return 0; } v->items = items; v->capacity = next; }
v->items[v->length++] = value; return 1;}
void int_vector_destroy(struct int_vector *v) { free(v->items); *v = (struct int_vector){0};}19. Function Reference by Task
Section titled “19. Function Reference by Task”| Task | Start with |
|---|---|
| Print a message | puts, fprintf, printf |
| Build bounded text | snprintf |
| Read a line | fgets |
| Parse a checked integer | strtol, strtoul, strtoll, strtoull |
| Compare/search text | strcmp, strchr, strstr |
| Move bytes safely | memmove |
| Allocate an array | calloc, checked malloc |
| Grow allocated storage | checked temporary-pointer realloc pattern |
| Read/write binary data | fread, fwrite |
| Report system/library failure | Return check plus perror/strerror where documented |
| Sort/search an array | qsort, bsearch |
| Work with floating point | <math.h>, <float.h> |
| Format dates | time, localtime/gmtime, strftime |
| Handle debug invariants | assert |
| Portable fixed-width formatting | <stdint.h> plus <inttypes.h> |
| Share counters across threads | C11 <stdatomic.h> where supported |
20. Suggested Learning Path
Section titled “20. Suggested Learning Path”- Compile a program using
puts,printf,fgets, and checked return values. - Practice C strings and distinguish null-terminated text from arbitrary byte buffers.
- Parse lines using
strtol/strtodrather than depending on unchecked conversion. - Write file-copy and line-processing programs with correct EOF/error handling.
- Master allocation ownership, checked growth, and sanitizer-assisted testing.
- Use
qsort, callbacks, math, and time formatting in small utilities. - Separate ISO C from POSIX or Windows features when designing portable code.
- Learn atomics, thread synchronization, locale/encoding choices, and build hardening for production programs.
A Useful Default Build
Section titled “A Useful Default Build”cc -std=c17 -Wall -Wextra -Wpedantic -Wconversion -g program.c -o programWhen the program uses <math.h> functions on Unix-like systems:
cc -std=c17 -Wall -Wextra -Wpedantic -g program.c -lm -o programThe best libc habit is simple: know each function’s contract, pass it valid buffers and types, and check every result that can tell you work failed.