Proven C Book한국어 GitHub

Appendix F — The standard library, entry by entry

This appendix is something to look things up in. Where the body dealt with “why”, this gathers “what and how” in one place. It follows Annex B of C23 (N3220), aiming to list every function, macro and type of every header without omission.

How to read it.

ColumnWhat is in it
NameThe function’s name. Where f/l variants exist they share one cell
FormThe declaration as the standard writes it. QChar and QVoid are the standard’s notation (C23) for a return that preserves qualifiers — hand in a const pointer and you get one back
Arguments and returnWhat each parameter is, what comes back, and how failure is reported
What it does · ★trapsA one-line summary. ★ marks a trap this book treats in the body

Table 99.1

Three things to say in advance.

First, the Annex K functions (those ending in _s) are included. They are in the standard but optional, and were never widely implemented — that story is in chapter 78. They are marked ★K here.

Second, an empty “trap” cell is written as “none”. Left blank, it could not be told apart from not yet examined.

Third, this appendix is not a transcription of the standard. The declarations are facts and stand as they are, but the explanations are this book’s own, and whatever could be measured was measured.

<string.h> — strings and memory

Body: chapter 65 (close reading), chapter 42 (what a string is), chapter 43 (safe input).

This header mixes functions that take a size with functions that do not. That division is also the division between accidents.

Working on blocks of memory (mem*)

NameFormArguments and returnWhat it does · ★traps
memcpyvoid *memcpy(void *restrict s1, const void *restrict s2, size_t n)s1 destination, s2 source, n bytes. Returns s1Moves n bytes. ★ If the regions overlap it is outside the contract — that is what restrict promises. If they may overlap, use memmove
memmovevoid *memmove(void *s1, const void *s2, size_t n)The same. Returns s1Moves correctly even when overlapping. No trap — if in doubt, use this
memcmpint memcmp(const void *s1, const void *s2, size_t n)Two regions and a length. Returns negative, 0 or positiveCompares bytes in order. ★ Do not compare whole structs — padding gets into the value (chapter 47)
memchrQVoid *memchr(QVoid *s, int c, size_t n)c is converted to unsigned char. Null if not foundSearches forward within n bytes. Unlike the string functions it does not stop at a NUL
memsetvoid *memset(void *s, int c, size_t n)c converted to unsigned char. Returns sFills n bytes with one value. ★ Not a way to make pointers null — all-bits-zero and the null representation are separate things (chapter 36)
memset_explicitvoid *memset_explicit(void *s, int c, size_t n)As memsetC23. A wipe the optimizer may not remove — for clearing a password just before freeing
memccpyvoid *memccpy(void *restrict s1, const void *restrict s2, int c, size_t n)Copies up to and including c and returns the address after it; null if c was not metArrived in C23 (an old POSIX function). Does “copy up to the delimiter” in one step
memcpy_serrno_t memcpy_s(void *restrict s1, rsize_t s1max, const void *restrict s2, rsize_t n)Also takes the destination size s1max. 0 on success★K Annex K. On overflow it zeroes the destination and reports an error. Not widely implemented (chapter 78)
memmove_serrno_t memmove_s(void *s1, rsize_t s1max, const void *s2, rsize_t n)The same★K The same story
memset_serrno_t memset_s(void *s, rsize_t smax, int c, rsize_t n)The same★K The same story. memset_explicit is C23′s answer

Table 99.2

Copying and joining (strcpy and strcat families)

NameFormArguments and returnWhat it does · ★traps
strcpychar *strcpy(char *restrict s1, const char *restrict s2)Returns s1Copies through the NUL. ★ It does not know the destination’s size — the classic overflow
strncpychar *strncpy(char *restrict s1, const char *restrict s2, size_t n)n is how many bytes to write, not the destination’s size★★ Despite the name it is not a safe function. On an exact fit it writes no NUL; when short it fills the remainder with zeros (slow). Chapter 65
strcatchar *strcat(char *restrict s1, const char *restrict s2)Returns s1Appends. ★ It does not know the size, and it finds the end again every time — repeated in a loop it becomes quadratic
strncatchar *strncat(char *restrict s1, const char *restrict s2, size_t n)n is at most how many bytes to append; the NUL goes on top of that★ So the buffer must hold n + 1 — the arithmetic differs from strncpy
strdupchar *strdup(const char *s)Null on failureArrived in C23. You must free it — ownership comes to you (chapter 89)
strndupchar *strndup(const char *s, size_t n)At most n bytes. Null on failureC23. Stops at n even if no end is found
strcpy_serrno_t strcpy_s(char *restrict s1, rsize_t s1max, const char *restrict s2)0 on success★K Chapter 78
strncpy_serrno_t strncpy_s(char *restrict s1, rsize_t s1max, const char *restrict s2, rsize_t n)0 on success★K Chapter 78
strcat_serrno_t strcat_s(char *restrict s1, rsize_t s1max, const char *restrict s2)0 on success★K Chapter 78
strncat_serrno_t strncat_s(char *restrict s1, rsize_t s1max, const char *restrict s2, rsize_t n)0 on success★K Chapter 78

Table 99.3

Comparing, and length

NameFormArguments and returnWhat it does · ★traps
strlensize_t strlen(const char *s)Bytes, not counting the NULBytes, not characters (chapters 42, 72). And it is O(n), so it does not belong in a loop condition (chapter 41)
strcmpint strcmp(const char *s1, const char *s2)Negative, 0 or positiveNot “dictionary order” but order by unsigned char value. Human ordering is strcoll
strncmpint strncmp(const char *s1, const char *s2, size_t n)The sameCompares the first n bytes. Used for prefix tests
strcollint strcoll(const char *s1, const char *s2)The sameCompares in the order the locale prescribes (chapter 69). Slow — for repeated comparison use strxfrm
strxfrmsize_t strxfrm(char *restrict s1, const char *restrict s2, size_t n)Returns the length needed (excluding the NUL). If that is n or more, the contents of s1 are unspecifiedFreezes a locale comparison into a “key”. Keys can then be compared with strcmp
strnlen_ssize_t strnlen_s(const char *s, size_t maxsize)At most maxsize; 0 if s is null★K Yet this one is broadly useful — it measures a buffer that may have no end

Table 99.4

Searching and splitting

NameFormArguments and returnWhat it does · ★traps
strchrQChar *strchr(QChar *s, int c)Null if not found. If c is '\0' it points at the terminating NULFinds one byte, searching forward. ★ In a multibyte encoding it can land inside a character (chapter 72)
strrchrQChar *strrchr(QChar *s, int c)The sameSearches backward. Used to find the last separator in a path — ★ with the same danger
strstrQChar *strstr(QChar *s1, const char *s2)Null if not found; s1 if s2 is emptyFinds a substring. The standard prescribes no algorithm — it may be O(nm) in the worst case
strspnsize_t strspn(const char *s1, const char *s2)A lengthHow long the prefix made only of characters from s2 is. Used for skipping
strcspnsize_t strcspn(const char *s1, const char *s2)A lengthThe reverse — how far until a character from s2 appears
strpbrkQChar *strpbrk(QChar *s1, const char *s2)Null if not foundThe first place any character of s2 occurs
strtokchar *strtok(char *restrict s1, const char *restrict s2)The string on the first call, null thereafter. Null when there are no more★★ It destroys the original (writing NULs over the delimiters), and it hides state inside the function — not reentrant, not thread-safe. Chapter 65
strtok_schar *strtok_s(char *restrict s1, rsize_t *restrict s1max, const char *restrict s2, char **restrict ptr)Keeps the state outside, in ptr★K Moving the state out is the right direction. POSIX’s strtok_r had the same idea

Table 99.5

Error strings

NameFormArguments and returnWhat it does · ★traps
strerrorchar *strerror(int errnum)The string for an error numberMay return a static buffer — a later call can overwrite it, and thread safety is not guaranteed (chapter 75)
strerrorlen_ssize_t strerrorlen_s(errno_t errnum)The length needed★K Pairs with strerror_s

Table 99.6

Macro or typeWhat it isNote
NULLThe null pointer constantFrom C23 there is nullptr (chapters 36, 82)
size_tThe unsigned type of sizes and countsIts home is <stddef.h> (chapter 35)
rsize_t★K Annex K’s size typeBounded by RSIZE_MAX
errno_t★K Annex K’s error typeEffectively int

Table 99.7

<ctype.h> — the kinds of a single byte

Body: chapter 67 (close reading), chapter 9 (the history of character sets).

All fourteen functions have the same shape — they take an int and return an int. ★ In that one line lies the trap that governs the whole header.

Common to every functionWhat it is
ArgumentA value representable as unsigned char, or EOF. ★ Anything else is outside the contract
The commonest accidentPassing a char directly. On a machine where char is signed, a byte of 128 or more becomes negative and leaves the contract. Always isalpha((unsigned char)c)
ReturnThe classifying functions return “nonzero if true”, 0 if false. Do not assume 1
LocaleAll but isdigit and isxdigit can be changed by LC_CTYPE (chapter 68)

Table 99.8

NameTrue forNote · ★traps
isalnumA letter or a digitisalpha or isdigit
isalphaA letterNot “A–Z, a–z” — the locale and the character set decide (chapter 9′s EBCDIC)
isblankA word separator — space and horizontal tabC99. Means “blank within a line”
iscntrlA control characterNot drawn on screen
isdigit09Independent of the locale — the standard fixes these ten
isgraphA printing character other than spaceisprint minus the space
islowerA lowercase letterThe locale decides
isprintA printing character, space included
ispunctA printing character that is neither letter, digit nor space
isspaceWhitespace — space, \\n, \\t, \\v, \\f, \\rUsed for skipping input
isupperAn uppercase letterThe locale decides
isxdigitA hexadecimal digit★ Independent of the locale
tolowerReturns the lowercase if it is an uppercase letter, otherwise the argumentOne character to one character. Turkish I and German ß break that assumption (chapter 72)
toupperThe reverse★ The same

Table 99.9

<stdckdint.h> — arithmetic that answers about overflow

Body: chapter 81 (close reading), chapter 27 (integers are finite), chapter 52 (undefined behaviour).

A header C23 brought in. All three are type-generic macros, not functions — which is why the forms say type1 and type2.

NameFormArguments and returnWhat it does · ★traps
ckd_addbool ckd_add(type1 *result, type2 a, type3 b)Writes the result into result. Returns true if it overflowed, false otherwise★ Mind the direction of the return — true is not “success” but “it overflowed”
ckd_subbool ckd_sub(type1 *result, type2 a, type3 b)The sameSubtraction. Also catches going below 0 in an unsigned type
ckd_mulbool ckd_mul(type1 *result, type2 a, type3 b)The sameMultiplication. ★ The most useful of the three for allocation arithmetic — overflow in n * sizeof *p is a classic security bug

Table 99.10

Common contractWhat it saysNote
Accuracy of the resultIf it did not overflow, the mathematically correct valueNot “the wrapped value”
When it overflowedresult receives the wrapped valueNot undefined behaviour — the value is settled
OperandsInteger types other than bool and the bit-precise integerschar is allowed too
Feature test__STDC_VERSION_STDCKDINT_H__Check for it and branch

Table 99.11