Appendix A: C Standard Library Quick Reference
This appendix covers the most important C standard library functions for systems programming, organized by header. Each entry lists the signature, purpose, and common pitfalls.
stdio.h -- Standard I/O
| Function | Signature | Purpose |
|---|---|---|
printf | int printf(const char *fmt, ...) | Print formatted output to stdout |
fprintf | int fprintf(FILE *stream, const char *fmt, ...) | Print to any stream |
snprintf | int snprintf(char *buf, size_t n, const char *fmt, ...) | Safe formatted string |
scanf | int scanf(const char *fmt, ...) | Read formatted input from stdin |
fopen | FILE *fopen(const char *path, const char *mode) | Open a file stream |
fclose | int fclose(FILE *stream) | Close a file stream |
fread | size_t fread(void *ptr, size_t size, size_t n, FILE *s) | Binary read |
fwrite | size_t fwrite(const void *ptr, size_t size, size_t n, FILE *s) | Binary write |
fgets | char *fgets(char *s, int n, FILE *stream) | Read a line (safe) |
fputs | int fputs(const char *s, FILE *stream) | Write a string |
fseek | int fseek(FILE *stream, long offset, int whence) | Seek in stream |
ftell | long ftell(FILE *stream) | Current position |
fflush | int fflush(FILE *stream) | Flush output buffer |
perror | void perror(const char *s) | Print errno message |
feof | int feof(FILE *stream) | Check end-of-file |
ferror | int ferror(FILE *stream) | Check stream error |
Pitfalls:
sprintfhas no bounds checking. Always usesnprintf.getsis removed from C11. Never use it. Usefgets.scanf("%s", buf)has no bounds checking. Usescanf("%63s", buf)with a width specifier.feofreturns true only after a read attempt fails. Don't use it as a loop condition before reading.fcloseon an already-closedFILE*is undefined behavior.
stdlib.h -- General Utilities
| Function | Signature | Purpose |
|---|---|---|
malloc | void *malloc(size_t size) | Allocate memory |
calloc | void *calloc(size_t n, size_t size) | Allocate zeroed memory |
realloc | void *realloc(void *ptr, size_t size) | Resize allocation |
free | void free(void *ptr) | Free memory |
exit | void exit(int status) | Terminate process |
atexit | int atexit(void (*fn)(void)) | Register exit handler |
atoi | int atoi(const char *s) | String to int (unsafe) |
strtol | long strtol(const char *s, char **end, int base) | String to long (safe) |
strtoul | unsigned long strtoul(const char *s, char **end, int base) | String to unsigned long |
strtod | double strtod(const char *s, char **end) | String to double |
abs | int abs(int n) | Absolute value |
rand | int rand(void) | Pseudo-random integer |
srand | void srand(unsigned seed) | Seed random generator |
qsort | void qsort(void *base, size_t n, size_t size, int(*cmp)(...)) | Sort array |
bsearch | void *bsearch(const void *key, const void *base, ...) | Binary search |
getenv | char *getenv(const char *name) | Get environment variable |
system | int system(const char *cmd) | Run shell command |
Pitfalls:
atoireturns 0 on error, which is indistinguishable from converting "0". Usestrtolinstead.realloc(ptr, 0)behavior is implementation-defined. Don't rely on it.reallocon failure returns NULL but doesn't free the original. Save the original pointer:tmp = realloc(ptr, n); if (tmp) ptr = tmp;rand()is not cryptographically secure. Usegetrandom()for security.system()is a shell injection risk. Avoid in production code.
string.h -- String and Memory Operations
| Function | Signature | Purpose |
|---|---|---|
strlen | size_t strlen(const char *s) | String length |
strcpy | char *strcpy(char *dst, const char *src) | Copy string (unsafe) |
strncpy | char *strncpy(char *dst, const char *src, size_t n) | Copy with limit |
strcat | char *strcat(char *dst, const char *src) | Concatenate (unsafe) |
strncat | char *strncat(char *dst, const char *src, size_t n) | Concatenate with limit |
strcmp | int strcmp(const char *a, const char *b) | Compare strings |
strncmp | int strncmp(const char *a, const char *b, size_t n) | Compare n bytes |
strchr | char *strchr(const char *s, int c) | Find char in string |
strrchr | char *strrchr(const char *s, int c) | Find char (from end) |
strstr | char *strstr(const char *haystack, const char *needle) | Find substring |
strtok | char *strtok(char *s, const char *delim) | Tokenize (modifies string) |
memcpy | void *memcpy(void *dst, const void *src, size_t n) | Copy memory (no overlap) |
memmove | void *memmove(void *dst, const void *src, size_t n) | Copy memory (overlap ok) |
memset | void *memset(void *s, int c, size_t n) | Fill memory |
memcmp | int memcmp(const void *a, const void *b, size_t n) | Compare memory |
strerror | char *strerror(int errnum) | Error number to string |
Pitfalls:
strncpydoes NOT null-terminate ifsrcis longer thann. Always:strncpy(dst, src, n-1); dst[n-1] = '\0';memcpywith overlapping regions is undefined behavior. Usememmove.strtokuses a static buffer and is not thread-safe. Usestrtok_r.strcmpreturns 0 for equal strings (not 1). The return value is the difference, not a boolean.
math.h -- Mathematics
| Function | Signature | Purpose |
|---|---|---|
sqrt | double sqrt(double x) | Square root |
pow | double pow(double base, double exp) | Power |
fabs | double fabs(double x) | Absolute value |
ceil | double ceil(double x) | Round up |
floor | double floor(double x) | Round down |
round | double round(double x) | Round to nearest |
log | double log(double x) | Natural logarithm |
log10 | double log10(double x) | Base-10 logarithm |
sin/cos/tan | double sin(double x) | Trig functions (radians) |
Pitfalls:
- Link with
-lmon Linux. Math functions are inlibm, notlibc. sqrt(-1)returns NaN, not an error. Checkisnan().
ctype.h -- Character Classification
| Function | Purpose | Example |
|---|---|---|
isalpha(c) | Letter? | isalpha('A') = true |
isdigit(c) | Digit? | isdigit('5') = true |
isalnum(c) | Letter or digit? | |
isspace(c) | Whitespace? | isspace(' ') = true |
isupper(c) / islower(c) | Case check | |
toupper(c) / tolower(c) | Case conversion |
Pitfalls:
- Argument must be
unsigned charorEOF. Passing a signedcharwith negative value is undefined behavior:isalpha((unsigned char)c).
errno.h -- Error Numbers
Common errno values:
| Value | Name | Meaning |
|---|---|---|
| 1 | EPERM | Operation not permitted |
| 2 | ENOENT | No such file or directory |
| 5 | EIO | I/O error |
| 9 | EBADF | Bad file descriptor |
| 11 | EAGAIN | Try again (non-blocking) |
| 12 | ENOMEM | Out of memory |
| 13 | EACCES | Permission denied |
| 17 | EEXIST | File exists |
| 22 | EINVAL | Invalid argument |
| 28 | ENOSPC | No space left on device |
| 32 | EPIPE | Broken pipe |
Pitfalls:
errnois only valid immediately after a failed call. Successful calls may or may not reset it.errnois thread-local in glibc (actually a macro that expands to(*__errno_location())).
signal.h -- Signal Handling
| Function | Signature | Purpose |
|---|---|---|
signal | void (*signal(int sig, void (*handler)(int)))(int) | Set signal handler |
raise | int raise(int sig) | Send signal to self |
kill | int kill(pid_t pid, int sig) | Send signal to process |
sigaction | int sigaction(int sig, const struct sigaction *act, ...) | Set handler (preferred) |
sigprocmask | int sigprocmask(int how, const sigset_t *set, ...) | Block/unblock signals |
Pitfalls:
signal()behavior varies across systems. Usesigaction()instead.- Signal handlers must only call async-signal-safe functions. No
printf, nomalloc.
time.h -- Time Functions
| Function | Signature | Purpose |
|---|---|---|
time | time_t time(time_t *tloc) | Current time (seconds) |
clock | clock_t clock(void) | CPU time used |
difftime | double difftime(time_t t1, time_t t0) | Time difference |
localtime | struct tm *localtime(const time_t *t) | Convert to local time |
gmtime | struct tm *gmtime(const time_t *t) | Convert to UTC |
strftime | size_t strftime(char *s, size_t max, const char *fmt, ...) | Format time string |
clock_gettime | int clock_gettime(clockid_t id, struct timespec *tp) | High-resolution time |
Pitfalls:
localtimereturns a pointer to a static buffer (not thread-safe). Uselocaltime_r.clock()measures CPU time, not wall time. Useclock_gettime(CLOCK_MONOTONIC)for benchmarks.
unistd.h -- POSIX System Interface
| Function | Signature | Purpose |
|---|---|---|
read | ssize_t read(int fd, void *buf, size_t count) | Read from fd |
write | ssize_t write(int fd, const void *buf, size_t count) | Write to fd |
close | int close(int fd) | Close fd |
lseek | off_t lseek(int fd, off_t offset, int whence) | Seek in fd |
fork | pid_t fork(void) | Create child process |
exec* | int execvp(const char *file, char *const argv[]) | Replace process |
pipe | int pipe(int pipefd[2]) | Create pipe |
dup2 | int dup2(int oldfd, int newfd) | Duplicate fd |
getpid | pid_t getpid(void) | Current PID |
getcwd | char *getcwd(char *buf, size_t size) | Current directory |
sleep | unsigned sleep(unsigned seconds) | Sleep |
usleep | int usleep(useconds_t usec) | Sleep (microseconds) |
unlink | int unlink(const char *path) | Delete file |
access | int access(const char *path, int mode) | Check file access |
Pitfalls:
readandwritemay transfer fewer bytes than requested (short reads/ writes). Always loop.closecan fail. Check the return value, especially for files opened withO_SYNC.fork+execis POSIX, not C standard. Useposix_spawnwhen possible.
sys/types.h -- System Data Types
| Type | Purpose |
|---|---|
pid_t | Process ID |
uid_t / gid_t | User/group ID |
off_t | File offset |
size_t | Unsigned size |
ssize_t | Signed size (for error returns) |
mode_t | File permissions |
dev_t | Device number |
ino_t | Inode number |
time_t | Time in seconds |
Pitfalls:
size_tis unsigned. Subtracting twosize_tvalues can wrap to a huge number. Usessize_tor cast carefully.off_tis 32-bit by default on 32-bit systems. Use_FILE_OFFSET_BITS=64for large file support.