Proven C Book←↑→

부록 F — 표준 라이브러리 요람

이 부록은 찾는 글이다. 본문이 「왜」를 다뤘다면 여기는 「무엇을·어떻게」를 한자리에 모은다. C23(N3220)의 부속서 B 를 기준으로 헤더마다 함수·매크로·타입을 빠짐없이 싣는 것을 목표로 한다.

★ 여기 실린 것이 전부다. C23(N3220) 부속서 B 의 표준 헤더 서른하나를 모두 실었다 — 함수 · 매크로 · 타입이 하나도 빠지지 않았음을 기계가 매 빌드마다 대조한다.

읽는 법은 이렇다.

열무엇을 적었나
이름함수 이름. f·l 변형이 있으면 한 칸에 묶었다
꼴표준이 적은 선언. QChar·QVoid 는 한정자를 보존하는 반환을 뜻하는 표준의 표기다(C23) — 넘긴 포인터가 const 면 돌려주는 것도 const 다
인자와 반환각 매개변수가 무엇이고, 무엇을 돌려주며, 실패를 어떻게 알리는가
하는 일 · ★함정한 줄 요약. ★ 표시는 이 책이 본문에서 다룬 함정이다

표 105.1 — 요람의 열이 뜻하는 것

세 가지를 미리 밝혀 둔다.

첫째, 부속서 K 함수(_s 로 끝나는 것)도 실었다. 표준에 있으나 선택 사항이고 널리 구현되지 않았다 — 그 사정은 83장에 있다. 요람에서는 ★K 로 표시한다.

둘째, 「함정」 칸이 비어 있으면 「없음」이라고 적는다. 비워 두면 검토하지 않은 것과 구별되지 않기 때문이다.

셋째, 이 부록은 표준을 옮겨 적은 것이 아니다. 선언은 사실이라 그대로 두었지만 설명은 이 책의 문장이며, 값과 동작 가운데 잴 수 있는 것은 재서 적었다.

<string.h> — 문자열과 기억#

본문: 70장(정독), 43장(문자열의 정체), 44장(안전한 입력).

이 헤더는 크기를 받지 않는 함수와 받는 함수가 섞여 있다. 그 갈림이 사고의 갈림이기도 하다.

기억 블록 다루기 (mem*)#

이름꼴인자와 반환하는 일 · ★함정
memcpyvoid *memcpy(void *restrict s1, const void *restrict s2, size_t n)s1 목적지, s2 출처, n 바이트 수. 반환은 s1n 바이트를 옮긴다. ★ 두 영역이 겹치면 계약 밖이다 — restrict 가 그 약속이다. 겹치면 memmove
memmovevoid *memmove(void *s1, const void *s2, size_t n)같음. 반환은 s1겹쳐도 옳게 옮긴다. 함정 없음 — 겹칠지 모르면 이것을 쓴다
memcmpint memcmp(const void *s1, const void *s2, size_t n)두 영역과 길이. 반환은 음수·0·양수바이트를 사전순으로 견준다. ★ 구조체를 통째로 견주면 안 된다 — 패딩이 값에 낀다(48장)
memchrQVoid *memchr(QVoid *s, int c, size_t n)c 는 unsigned char 로 변환되어 찾는 값. 못 찾으면 널앞에서부터 n 바이트 안에서 찾는다. 문자열과 달리 NUL 에서 멈추지 않는다
memsetvoid *memset(void *s, int c, size_t n)c 는 unsigned char 로 변환. 반환은 sn 바이트를 같은 값으로 채운다. ★ 포인터를 널로 만드는 수단이 아니다 — 비트 0 과 널 표현은 별개다(37장)
memset_explicitvoid *memset_explicit(void *s, int c, size_t n)memset 과 같다C23. 최적화가 지우지 못하는 지우기 — 비밀번호를 지운 뒤 곧 해제할 때 쓴다
memccpyvoid *memccpy(void *restrict s1, const void *restrict s2, int c, size_t n)c 를 만나면 거기까지 복사하고 그 다음 자리를 돌려준다. 못 만나면 널C23 에 들어왔다(오래된 POSIX 함수). 「구분자까지 복사」를 한 번에 한다
memcpy_serrno_t memcpy_s(void *restrict s1, rsize_t s1max, const void *restrict s2, rsize_t n)목적지 크기 s1max 를 함께 받는다. 성공은 0★K 부속서 K. 넘치면 목적지를 0 으로 채우고 오류를 낸다. 널리 구현되지 않았다(83장)
memmove_serrno_t memmove_s(void *s1, rsize_t s1max, const void *s2, rsize_t n)같음★K 같은 사정
memset_serrno_t memset_s(void *s, rsize_t smax, int c, rsize_t n)같음★K 같은 사정. memset_explicit 이 C23 의 답이다

표 105.2 — <string.h> 요람 — 기억 블록 (mem*)

복사와 이어 붙이기 (strcpy·strcat 계열)#

이름꼴인자와 반환하는 일 · ★함정
strcpychar *strcpy(char *restrict s1, const char *restrict s2)반환은 s1NUL 까지 통째로 옮긴다. ★ 목적지 크기를 모른다 — 넘침의 고전
strncpychar *strncpy(char *restrict s1, const char *restrict s2, size_t n)n 은 쓸 바이트 수이지 목적지 크기가 아니다★★ 이름과 달리 안전 함수가 아니다. 딱 맞으면 NUL 을 붙이지 않고, 짧으면 남은 자리를 전부 0 으로 채운다(느리다). 70장
strcatchar *strcat(char *restrict s1, const char *restrict s2)반환은 s1뒤에 이어 붙인다. ★ 목적지 크기를 모르고, 매번 끝을 다시 찾아 이어 붙이므로 반복하면 제곱이 된다
strncatchar *strncat(char *restrict s1, const char *restrict s2, size_t n)n 은 더 붙일 최대 바이트 수. NUL 은 그 위에 하나 더 쓴다★ 그래서 필요한 그릇은 n + 1 이다 — strncpy 와 셈법이 다르다
strdupchar *strdup(const char *s)실패하면 널C23 에 들어왔다. free 해야 한다 — 소유가 넘어온다(94장)
strndupchar *strndup(const char *s, size_t n)최대 n 바이트까지. 실패하면 널C23. 끝을 못 찾아도 n 에서 멈춘다
strcpy_serrno_t strcpy_s(char *restrict s1, rsize_t s1max, const char *restrict s2)성공은 0★K 83장
strncpy_serrno_t strncpy_s(char *restrict s1, rsize_t s1max, const char *restrict s2, rsize_t n)성공은 0★K 83장
strcat_serrno_t strcat_s(char *restrict s1, rsize_t s1max, const char *restrict s2)성공은 0★K 83장
strncat_serrno_t strncat_s(char *restrict s1, rsize_t s1max, const char *restrict s2, rsize_t n)성공은 0★K 83장

표 105.3 — <string.h> 요람

견주기와 길이#

이름꼴인자와 반환하는 일 · ★함정
strlensize_t strlen(const char *s)NUL 을 세지 않은 바이트 수★ 글자 수가 아니라 바이트 수다(43·77장). 그리고 O(n) 이므로 루프 조건에 두지 않는다(42장)
strcmpint strcmp(const char *s1, const char *s2)음수·0·양수★ 「사전순」이 아니라 unsigned char 값 순이다. 사람이 쓰는 정렬은 strcoll
strncmpint strncmp(const char *s1, const char *s2, size_t n)같음앞 n 바이트만 견준다. 접두어 검사에 쓴다
strcollint strcoll(const char *s1, const char *s2)같음로케일이 정한 순서로 견준다(74장). 느리다 — 여러 번 견줄 것이면 strxfrm
strxfrmsize_t strxfrm(char *restrict s1, const char *restrict s2, size_t n)필요한 길이를 돌려준다(NUL 제외). n 이상이면 s1 의 내용은 미정로케일 비교를 «키»로 굳힌다. 키끼리는 strcmp 로 견줄 수 있다
strnlen_ssize_t strnlen_s(const char *s, size_t maxsize)최대 maxsize. s 가 널이면 0★K 그러나 이것만은 널리 쓸 만하다 — 끝이 없을지 모르는 버퍼의 길이를 잰다

표 105.4 — <string.h> 요람 — 견주기와 길이

찾기와 쪼개기#

이름꼴인자와 반환하는 일 · ★함정
strchrQChar *strchr(QChar *s, int c)못 찾으면 널. c 가 '\0' 이면 끝의 NUL 을 가리킨다앞에서부터 한 바이트를 찾는다. ★ 멀티바이트 인코딩에서 글자 속을 짚을 수 있다(77장)
strrchrQChar *strrchr(QChar *s, int c)같음뒤에서부터 찾는다. 경로에서 마지막 구분자를 찾는 데 쓰는데 — ★ 위와 같은 위험
strstrQChar *strstr(QChar *s1, const char *s2)못 찾으면 널. s2 가 빈 문자열이면 s1부분 문자열을 찾는다. 표준은 알고리즘을 정하지 않는다 — 최악 O(nm) 일 수 있다
strspnsize_t strspn(const char *s1, const char *s2)길이를 돌려준다s2 의 글자들로만 이루어진 앞부분의 길이. 건너뛰기에 쓴다
strcspnsize_t strcspn(const char *s1, const char *s2)길이를 돌려준다반대 — s2 의 글자를 만나기 전까지의 길이
strpbrkQChar *strpbrk(QChar *s1, const char *s2)못 찾으면 널s2 의 글자 아무거나 처음 나오는 자리
strtokchar *strtok(char *restrict s1, const char *restrict s2)첫 호출에 문자열, 다음부터 널. 더 없으면 널★★ 원본을 부순다(구분자 자리에 NUL 을 쓴다). 그리고 상태를 함수 안에 숨긴다 — 재진입 불가, 스레드 안전하지 않다. 70장
strtok_schar *strtok_s(char *restrict s1, rsize_t *restrict s1max, const char *restrict s2, char **restrict ptr)상태를 ptr 로 밖에 둔다★K 상태를 밖으로 뺀 것은 옳은 방향이다. POSIX 의 strtok_r 이 같은 생각

표 105.5 — <string.h> 요람 — 찾기와 쪼개기

오류 문자열#

이름꼴인자와 반환하는 일 · ★함정
strerrorchar *strerror(int errnum)오류 번호에 대응하는 문자열★ 정적 버퍼를 돌려줄 수 있다 — 다음 호출이 덮어쓸 수 있고 스레드 안전이 보장되지 않는다(80장)
strerrorlen_ssize_t strerrorlen_s(errno_t errnum)필요한 길이★K strerror_s 와 짝을 이룬다

표 105.6 — <string.h> 요람 — 오류 문자열

매크로·타입무엇인가주의
NULL널 포인터 상수C23 부터는 nullptr 가 있다(37·87장)
size_t크기·개수의 부호 없는 타입<stddef.h> 가 본가다(36장)
rsize_t★K 부속서 K 의 크기 타입RSIZE_MAX 로 상한을 검사한다
errno_t★K 부속서 K 의 오류 타입사실상 int

표 105.7 — 매크로와 타입

<ctype.h> — 한 바이트의 갈래#

본문: 72장(정독), 8장(문자 집합의 역사).

열네 함수가 전부 같은 꼴이다 — int 를 받아 int 를 돌려준다. 그 한 줄에 ★ 이 헤더 전체를 지배하는 함정이 있다.

모든 함수에 공통내용
인자unsigned char 로 표현할 수 있는 값 또는 EOF. ★ 그 밖의 값은 계약 밖이다
★ 가장 흔한 사고char 를 그대로 넘기는 것. 부호 있는 char 인 기계에서 128 이상의 바이트가 음수가 되어 계약을 벗어난다. 반드시 isalpha((unsigned char)c)
반환판정 함수는 «참이면 0 이 아닌 값», 거짓이면 0. 1 이라고 가정하지 않는다
로케일isdigit·isxdigit 을 뺀 나머지는 LC_CTYPE 이 바꿀 수 있다(73장)

표 105.8 — 모든 함수에 공통인 것

이름무엇이 참인가비고 · ★함정
isalnum글자 또는 숫자isalpha 이거나 isdigit
isalpha글자★ 「A~Z, a~z」가 아니다 — 로케일과 문자 집합이 정한다(8장의 EBCDIC)
isblank칸 띄우개 — 공백과 수평 탭C99. 「줄 안의 빈칸」을 뜻한다
iscntrl제어 문자화면에 그려지지 않는 것
isdigit0~9★ 로케일과 무관하다 — 표준이 이 열 글자로 못박았다
isgraph공백을 뺀, 그려지는 글자isprint 에서 공백을 뺀 것
islower소문자로케일이 정한다
isprint공백을 포함해 그려지는 글자
ispunct글자도 숫자도 공백도 아닌, 그려지는 글자
isspace공백류 — 공백·\\n·\\t·\\v·\\f·\\r입력을 건너뛸 때 쓴다
isupper대문자로케일이 정한다
isxdigit16진 숫자★ 로케일과 무관하다
tolower대문자면 대응하는 소문자, 아니면 그대로★ 한 글자 대 한 글자다. 터키어 I, 독일어 ß 처럼 그 가정이 깨지는 언어가 있다(77장)
toupper소문자면 대응하는 대문자, 아니면 그대로★ 같음

표 105.9 — <ctype.h> 요람

<stdckdint.h> — 넘침을 묻는 산술#

본문: 86장(정독), 28장(정수의 유한함), 54장(정의되지 않은 동작).

C23 이 들인 헤더다. 셋 다 타입 제네릭 매크로이지 함수가 아니다 — 그래서 꼴에 type1·type2 라고 적혀 있다.

이름꼴인자와 반환하는 일 · ★함정
ckd_addbool ckd_add(type1 *result, type2 a, type3 b)result 에 결과를 쓴다. 넘쳤으면 참, 아니면 거짓을 돌려준다★ 반환값의 방향에 주의 — 참이 「성공」이 아니라 「넘쳤다」이다
ckd_subbool ckd_sub(type1 *result, type2 a, type3 b)같음뺄셈. 부호 없는 타입에서 0 아래로 내려가는 것도 잡는다
ckd_mulbool ckd_mul(type1 *result, type2 a, type3 b)같음곱셈. ★ 할당 크기 계산에 가장 쓸모 있다 — n * sizeof *p 의 넘침이 고전적인 보안 사고다

표 105.10 — 그 밖의 헤더 요람

공통 규약내용비고
결과의 정확성넘치지 않았다면 수학적으로 옳은 값「감아 돈 값」이 아니다
넘쳤을 때result 에는 감아 돈 값이 들어간다미정의 동작이 아니다 — 값이 정해져 있다
피연산자bool 과 비트 정밀 정수를 뺀 정수 타입char 도 허용된다
매크로 검사__STDC_VERSION_STDCKDINT_H__있는지 보고 갈라 쓴다

표 105.11 — 요람의 공통 규약

<assert.h> — 계약을 실행 중에 확인한다#

본문: 80장(정독), 53장(계약), 54장(정의되지 않은 동작).

헤더 전체가 매크로 하나와 그 매크로를 끄는 이름 하나다. 함수가 아니라 매크로인 것이 요점이다 — 그래야 실패한 자리의 파일 이름과 줄 번호를 찍을 수 있다.

이름꼴인자와 반환하는 일 · ★함정
assertvoid assert(scalar expression)수식 하나를 받는다. 돌려주는 값은 없다거짓이면 진단을 찍고 abort 한다. ★ NDEBUG 가 정의되면 통째로 사라진다 — 그래서 부수효과를 안에 넣으면 안 된다(assert(f()) 는 배포판에서 f 를 부르지 않는다)
NDEBUG매크로값이 아니라 정의 여부만 본다정의되어 있으면 assert 가 아무 일도 하지 않는다. ★ <assert.h> 를 포함하는 그 시점의 정의 여부를 본다 — 포함할 때마다 다시 판정된다

표 105.12 — <assert.h> 요람

<errno.h> — 실패의 까닭을 남기는 자리#

본문: 80장(정독), 53장(오류를 값으로).

여기에는 함수가 없다. 정수 하나(errno)와 표준이 못박은 값 셋, 그리고 부속서 K 의 타입 하나뿐이다. ★ 표준이 정한 값은 셋뿐이고, ENOENT 처럼 흔히 쓰는 이름들은 POSIX 가 더한 것이다.

이름꼴인자와 반환하는 일 · ★함정
errnoint errno 로 쓰는 수정 가능한 좌변값읽고 쓸 수 있다★ 변수라는 보장은 없다 — 스레드마다 따로인 자리를 매크로로 준다. ★ 성공한 호출이 errno 를 지우지 않는다. 그래서 부르기 전에 0 으로 두고, 실패를 확인한 뒤에만 읽는다
EDOM정수 상수 수식0 이 아닌 서로 다른 값정의역 오류 — sqrt(-1.0) 처럼 인자가 수학적으로 허용되지 않을 때(78장)
ERANGE정수 상수 수식같음값이 표현 범위를 벗어남 — strtol 의 넘침(71장)·exp 의 발산
EILSEQ정수 상수 수식같음바이트 열이 그 인코딩의 올바른 글자가 아님 — 멀티바이트 변환(75장)
errno_t타입int 와 같다★K 부속서 K 의 이름이다. 선택 사항이므로 없을 수 있다(83장)

표 105.13 — <errno.h> 요람

<setjmp.h> — 되돌아갈 자리를 적어 둔다#

본문: 82장(정독), 80장(진단과 제어), 5장(기억의 구역들).

둘뿐인데 둘 다 보통 함수처럼 굴지 않는다. setjmp 는 두 번 돌아오고, longjmp 는 돌아오지 않는다.

이름꼴인자와 반환하는 일 · ★함정
setjmpint setjmp(jmp_buf env)현재 자리를 env 에 적는다. 직접 부른 그때는 0, longjmp 로 돌아왔을 때는 그것이 넘긴 값★ 매크로다. 그리고 쓸 수 있는 자리가 표준으로 좁게 정해져 있다 — 조건문의 전체 수식이거나 그 자체가 한 문장인 자리 같은 것. x = setjmp(env); 는 허용되지 않는다
longjmpvoid longjmp(jmp_buf env, int val)env 가 가리키는 자리로 되돌아간다. 돌아오지 않는다★ val 이 0 이면 1 로 바뀐다 — 「처음 지나감」과 구별해야 하기 때문이다. ★ setjmp 를 부른 함수가 이미 끝났다면 정의되지 않은 동작이다. ★ 자동 변수 중 volatile 이 아니고 그 사이에 바뀐 것의 값은 불확정이다
jmp_buf배열 타입setjmp·longjmp 가 주고받는 그릇★ 통째로 대입해 옮길 수 있다고 보장하지 않는다 — 그 자리에 두고 쓴다

표 105.14 — <setjmp.h> 요람

<stdarg.h> — 개수를 모르는 인자를 훑는다#

본문: 63장(정독), 69장(형식 문자열).

넷 다 매크로다. va_start 로 열고 va_arg 로 하나씩 꺼내며 va_end 로 닫는 것이 전부인데, 타입을 스스로 알아내지 못한다는 점이 이 헤더의 모든 함정의 뿌리다.

이름꼴인자와 반환하는 일 · ★함정
va_startvoid va_start(va_list ap, ...)ap 를 쓸 수 있게 준비한다★ C23 부터 마지막 이름 있는 매개변수를 적지 않아도 된다. 그래서 이름 있는 매개변수가 하나도 없는 가변 인자 함수도 쓸 수 있게 되었다
va_argtype va_arg(va_list ap, type)다음 인자를 type 으로 꺼낸다★ 실제로 넘어온 타입과 다르면 정의되지 않은 동작이다. 기본 인자 승격을 거친 뒤의 타입으로 적어야 한다 — float 은 double 로, char·short 는 int 로 온다
va_copyvoid va_copy(va_list dest, va_list src)src 의 현재 상태를 dest 에 복사한다C99. 같은 목록을 두 번 훑어야 할 때 쓴다(vsnprintf 로 길이를 재고 다시 찍는 관용구). ★ 복사한 것도 각각 va_end 해야 한다
va_endvoid va_end(va_list ap)ap 를 닫는다★ 열었으면 반드시 닫는다 — 빠뜨려도 대개 아무 일이 없어 보이지만 표준은 정의되지 않은 동작이라 한다
va_list타입훑는 상태를 담는 그릇★ 한 번 훑고 나면 다시 쓸 수 없다 — 다시 훑으려면 va_copy 로 떠 둔다

표 105.15 — <stdarg.h> 요람

<stddef.h> — 이름 없는 것들에 이름을 준다#

본문: 37장(널 삼형제), 27장(타입의 갈래), 4장(정렬).

함수가 없다. 대신 다른 헤더들이 기대는 이름이 여기 모여 있다 — 크기를 재는 타입, 두 주소의 거리, 그리고 널.

이름꼴무엇인가하는 일 · ★함정
size_t부호 없는 정수 타입sizeof 가 돌려주는 타입크기와 개수를 담는다. ★ 부호가 없다 — 빼서 음수가 되면 감아 돌아 거대한 수가 된다(6장)
ptrdiff_t부호 있는 정수 타입두 포인터를 뺀 결과의 타입★ 같은 배열 안의 두 자리끼리만 빼는 것이 계약이다(38장)
wchar_t정수 타입와이드 문자를 담는 타입★ 크기도 인코딩도 표준이 정하지 않았다 — 리눅스 4바이트, 윈도우 2바이트(75장)
max_align_t객체 타입기본 정렬 중 가장 엄한 것을 요구하는 타입malloc 이 돌려주는 자리가 만족시키는 정렬의 기준이다(4장)
nullptr_t타입nullptr 의 타입C23. ★ 값이 하나뿐인 타입이다 — nullptr 그 자체(87장)
rsize_tsize_t 와 같다부속서 K 가 쓰는 이름★K 선택 사항이므로 없을 수 있다(83장)
NULL널 포인터 상수로 펼쳐지는 매크로「가리키는 데가 없음」★ 0 이라는 보장은 없다 — 표현이 0 이 아닌 기계가 있었다(37장)
offsetofsize_t offsetof(type, member-designator)구조체 첫머리에서 멤버까지의 거리★ 비트 필드에는 쓸 수 없다. 직렬화와 컨테이너 관용구의 바탕이다

표 105.16 — <stddef.h> 요람

<stdbool.h>·<stdalign.h> — 비어 버린 두 헤더#

본문: 87장(매크로에서 키워드로), 31장(불리언), 4장(정렬).

두 헤더 다 지금은 실을 것이 없다. 그것이 이 항목의 내용이다 — C99·C11 이 매크로로 빌려주던 이름들이 C23 에서 언어의 낱말이 되었기 때문이다. 헤더는 옛 코드가 깨지지 않도록 남아 있을 뿐이다.

헤더예전에 주던 것지금
<stdbool.h>bool · true · false 를 매크로로셋 다 키워드다. 포함해도 되고 안 해도 된다
<stdalign.h>alignas · alignof 를 매크로로둘 다 키워드다. 같은 사정

표 105.17 — 빈 헤더가 된 사정

★ 그래서 새로 쓰는 코드에 이 둘을 포함할 까닭은 없다. 다만 포함해도 아무 해가 없게 표준이 남겨 두었다 — 옛 코드를 고치지 않아도 되게 하려는 배려다.

<iso646.h>·<stdnoreturn.h> — 흉터 하나와 물러난 이름 하나#

본문: 8장(ISO 646 과 삼중자), 25장(_Noreturn), 87장(매크로에서 키워드로).

둘 다 함수가 하나도 없는 헤더라 요람의 표 꼴에는 맞지 않는다. 그래도 표준 헤더이므로 여기에 둔다.

<iso646.h> 는 연산자를 낱말로 적게 하는 매크로 열한 개를 준다. 8장에서 본 ISO 646 국가 변형 — &·|·^·~ 같은 자리에 다른 글자가 들어앉은 문자 집합 — 에서도 C 를 적을 수 있게 하려는 것이다. 삼중자가 같은 문제에 대한 문자 수준의 답이었다면 이 헤더는 낱말 수준의 답이다.

매크로펼친 것매크로펼친 것
and&&not_eq!=
and_eq&=or||
bitand&or_eq|=
bitor|xor^
compl~xor_eq^=
not!

표 105.18 — <iso646.h> 가 주는 열한 이름

★ C 에서는 이것들이 매크로라서 헤더를 포함해야 뜻이 생긴다. C++ 에서는 같은 이름이 처음부터 언어의 대체 표기다. 그래서 and 라는 이름의 변수는 C 에서는 멀쩡하고 C++ 컴파일러에 넣으면 깨진다.

<stdnoreturn.h> 는 매크로 하나, noreturn 을 준다. 펼치면 _Noreturn 이다. C23 은 _Noreturn 함수 지정자와 함께 이 매크로와 헤더를 폐기 예정으로 표시했다(N3220 §7.25). 새 코드는 [[noreturn]] 속성을 쓴다(25장).

★ 이 헤더를 포함한 파일에서는 noreturn 이 매크로라서 [[noreturn]] 이 전처리 뒤에 [[_Noreturn]] 이 된다. 표준은 그 때문에 [[_Noreturn]] 을 같은 뜻의 속성으로 따로 인정해 두었다(§6.7.13.7 의 각주) — 옛 헤더와 새 속성이 한 파일에서 만나도 깨지지 않게 하려는 배려다.

<limits.h> — 정수가 담을 수 있는 끝#

본문: 6장(정수의 표현), 27장(타입의 갈래), 28장(정수의 유한함).

함수가 없다. 타입마다 가장 작은 값 · 가장 큰 값 · 비트 폭 셋이 짝을 이룬다. 규칙만 알면 이름 서른셋을 외울 일이 없다.

꼬리무엇보기
_MIN그 타입이 담는 가장 작은 값INT_MIN
_MAX가장 큰 값INT_MAX
_WIDTH부호 비트를 포함한 비트 폭INT_WIDTH — C23 이 들였다

표 105.19 — 이름을 읽는 규칙

★ 부호 없는 타입에는 _MIN 이 없다. 0 이기 때문이다.

타입최소최대폭 · ★함정
charCHAR_MINCHAR_MAXCHAR_WIDTH. ★ 부호가 있는지 없는지 구현이 정한다 — 그래서 CHAR_MIN 이 0 일 수도 있다
signed charSCHAR_MINSCHAR_MAXSCHAR_WIDTH
unsigned char0UCHAR_MAXUCHAR_WIDTH
shortSHRT_MINSHRT_MAXSHRT_WIDTH
unsigned short0USHRT_MAXUSHRT_WIDTH
intINT_MININT_MAXINT_WIDTH
unsigned int0UINT_MAXUINT_WIDTH
longLONG_MINLONG_MAXLONG_WIDTH. ★ 리눅스 64비트에서 8바이트, 윈도우에서 4바이트다
unsigned long0ULONG_MAXULONG_WIDTH
long longLLONG_MINLLONG_MAXLLONG_WIDTH
unsigned long long0ULLONG_MAXULLONG_WIDTH
bool0BOOL_MAXBOOL_WIDTH. C23 이 들였다 — 각각 1 이다

표 105.20 — <limits.h> 요람

이름무엇★함정
CHAR_BIT한 바이트의 비트 수★ 8 이라는 보장이 표준에는 없다. 다만 8 이 아닌 기계는 오늘날 사실상 없다(6장)
MB_LEN_MAX어느 로케일에서든 멀티바이트 한 글자의 최대 바이트 수MB_CUR_MAX(로케일에 따라 달라지는 현재 값)와 다르다(75장)
BITINT_MAXWIDTH_BitInt(N) 이 허용하는 최대 NC23. 최소 ULLONG_WIDTH 이상이 보장된다

표 105.21 — 타입에 딸리지 않은 셋

<float.h> — 실수가 담을 수 있는 끝#

본문: 7장(수의 표현), 52장(근사의 수학), 78장(수 다루기).

함수가 없다. 이름이 예순 남짓이라 많아 보이지만, 실은 접두 셋 × 접미 열다섯 에 타입에 딸리지 않은 몇이 붙은 것이다. 접두는 FLT_(float) · DBL_(double) · LDBL_(long double)이다.

접미사무엇보기★함정
_MANT_DIG가수의 자릿수 — FLT_RADIX 진법으로FLT_MANT_DIG·DBL_MANT_DIG·LDBL_MANT_DIG정밀도의 근원이다
_DIG십진으로 왕복해도 안전한 자릿수FLT_DIG·DBL_DIG·LDBL_DIGdouble 은 보통 15
_DECIMAL_DIG그 타입을 십진으로 적었다가 되읽어도 같아지는 자릿수FLT_DECIMAL_DIG·DBL_DECIMAL_DIG·LDBL_DECIMAL_DIG★ _DIG 와 다르다 — 이쪽이 크다(double 은 17)
_EPSILON1 과, 1 보다 큰 가장 작은 수의 차FLT_EPSILON·DBL_EPSILON·LDBL_EPSILON★ 「충분히 작은 수」가 아니다 — 큰 값끼리 비교할 때 그대로 쓰면 틀린다
_MIN정규화된 가장 작은 양수FLT_MIN·DBL_MIN·LDBL_MIN★ 가장 작은 양수가 아니다 — 그것은 _TRUE_MIN 이다
_TRUE_MIN비정규를 포함한 가장 작은 양수FLT_TRUE_MIN·DBL_TRUE_MIN·LDBL_TRUE_MIN비정규가 없으면 _MIN 과 같다
_MAX가장 큰 유한값FLT_MAX·DBL_MAX·LDBL_MAX넘으면 무한대가 된다
_NORM_MAX가장 큰 정규화된 유한값FLT_NORM_MAX·DBL_NORM_MAX·LDBL_NORM_MAX이진 형식에서는 _MAX 와 같다
_MIN_EXP·_MAX_EXPFLT_RADIX 를 밑으로 한 지수의 범위FLT_MIN_EXP·DBL_MIN_EXP·LDBL_MIN_EXP / FLT_MAX_EXP·DBL_MAX_EXP·LDBL_MAX_EXP
_MIN_10_EXP·_MAX_10_EXP십진 지수의 범위FLT_MIN_10_EXP·DBL_MIN_10_EXP·LDBL_MIN_10_EXP / FLT_MAX_10_EXP·DBL_MAX_10_EXP·LDBL_MAX_10_EXP
_HAS_SUBNORM비정규가 있는가FLT_HAS_SUBNORM·DBL_HAS_SUBNORM·LDBL_HAS_SUBNORM1 · 0 · −1(모름) 중 하나
_IS_IEC_60559IEC 60559(IEEE 754)를 따르는가FLT_IS_IEC_60559·DBL_IS_IEC_60559·LDBL_IS_IEC_60559C23 이 들였다
_SNAN시그널링 NaNFLT_SNAN·DBL_SNAN·LDBL_SNANC23. 있으면 그 타입의 시그널링 NaN 이다

표 105.22 — 접미사가 뜻하는 것 — 세 접두에 모두 붙는다

이름무엇★함정
FLT_RADIX지수의 밑★ 이름에 FLT_ 가 붙었지만 세 타입 모두에 해당한다. 오늘날 사실상 2 다
FLT_ROUNDS지금의 반올림 방향★ 상수가 아니라 실행 중에 바뀔 수 있다(fesetround, 78장)
FLT_EVAL_METHOD중간 계산을 어떤 폭으로 하는가★ float 끼리의 계산이 double 로 이뤄질 수 있다는 뜻이다
DECIMAL_DIG가장 넓은 타입의 _DECIMAL_DIGC23 에서 밀려난 이름이다 — LDBL_DECIMAL_DIG 를 쓴다
CR_DECIMAL_DIG정확히 반올림되는 십진 변환의 자릿수C23
INFINITYfloat 무한대있는 경우에만 정의된다
NANfloat 조용한 NaN★ NAN == NAN 은 거짓이다(52장)

표 105.23 — 타입에 딸리지 않은 이름들

★ 십진 부동소수점도 같은 규칙을 따른다. N 자리에 32 · 64 · 128 이 들어간다 — DECN_MANT_DIG · DECN_EPSILON · DECN_MAX · DECN_MIN · DECN_MAX_EXP · DECN_MIN_EXP · DECN_TRUE_MIN · DECN_SNAN 이고, 여기에 DEC_EVAL_METHOD · DEC_INFINITY · DEC_NAN 이 딸린다. 선택 사항이라 없는 구현이 많다.

<stdint.h> — 폭을 못박은 정수#

본문: 6장(정수의 표현), 27장(타입의 갈래), 51장(비트 다루기).

int 의 폭이 기계마다 다르다는 사정을 정면으로 다루는 헤더다. 함수는 없고 타입과 매크로뿐인데, 이름에 들어간 N 은 8 · 16 · 32 · 64 같은 실제 폭이 들어갈 자리다.

꼴약속★함정
intN_t·uintN_t정확히 N 비트, 채움 비트 없음, 2의 보수★ 있다는 보장이 없다 — 그 폭이 없는 기계에서는 정의되지 않는다
int_leastN_t·uint_leastN_t적어도 N 비트인 것 중 가장 작은 것★ 이쪽은 언제나 있다. 이식성이 가장 높다
int_fastN_t·uint_fastN_t적어도 N 비트이면서 그 기계에서 빠른 것★ 「빠르다」의 판단은 구현이 한다 — 크기가 예상보다 클 수 있다

표 105.24 — 세 갈래 — 무엇을 약속하는가

이름무엇짝★함정
intmax_t·uintmax_t가장 넓은 정수 타입INTMAX_MIN·INTMAX_MAX·INTMAX_WIDTH·UINTMAX_MAX·UINTMAX_WIDTH★ _BitInt 은 여기에 들어가지 않는다
intptr_t·uintptr_t포인터를 담았다 되돌릴 수 있는 정수INTPTR_MIN·INTPTR_MAX·INTPTR_WIDTH·UINTPTR_MAX·UINTPTR_WIDTH★ 선택 사항이다. 그리고 되돌린 포인터의 유효성은 프로버넌스가 정한다(38장)
폭이 정확한 것intN_t·uintN_tINTN_MIN·INTN_MAX·INTN_WIDTH·UINTN_MAX·UINTN_WIDTHN 에 8·16·32·64 가 들어간다
적어도 N 비트int_leastN_t·uint_leastN_tINT_LEASTN_MIN·INT_LEASTN_MAX·INT_LEASTN_WIDTH·UINT_LEASTN_MAX·UINT_LEASTN_WIDTH
빠른 것int_fastN_t·uint_fastN_tINT_FASTN_MIN·INT_FASTN_MAX·INT_FASTN_WIDTH·UINT_FASTN_MAX·UINT_FASTN_WIDTH
상수를 적는 매크로그 타입의 상수를 만든다INTN_C·UINTN_C·INTMAX_C·UINTMAX_C★ 값이 아니라 리터럴에 접미사를 붙이는 매크로다

표 105.25 — <stdint.h> 요람

이름무엇의 한계인가비고
SIZE_MAX·SIZE_WIDTHsize_t할당 크기를 셈할 때의 상한이다
PTRDIFF_MIN·PTRDIFF_MAXptrdiff_t
WCHAR_MIN·WCHAR_MAX·WCHAR_WIDTHwchar_t(75장)
WINT_MIN·WINT_MAX·WINT_WIDTHwint_tWEOF 를 담을 수 있어야 한다
SIG_ATOMIC_MIN·SIG_ATOMIC_MAX·SIG_ATOMIC_WIDTHsig_atomic_t신호 처리기가 만질 수 있는 유일한 타입(80장)
RSIZE_MAX부속서 K 가 「너무 크다」고 볼 경계★K 선택 사항(83장)

표 105.26 — 다른 헤더의 타입에 딸린 한계

<inttypes.h> — 그 정수를 찍고 읽는 법#

본문: 69장(형식 문자열), 71장(문자열에서 수로), 6장(정수의 표현).

<stdint.h> 를 포함하면서, printf 에 무엇을 적을 것인가 를 함께 준다. int64_t 를 찍을 때 %ld 라 적으면 어느 기계에서는 맞고 어느 기계에서는 틀린다 — 그 문제를 없애는 헤더다.

이름꼴인자와 반환하는 일 · ★함정
imaxabsintmax_t imaxabs(intmax_t j)절댓값★ 가장 작은 음수의 절댓값은 표현할 수 없다 — 정의되지 않은 동작이다
imaxdivimaxdiv_t imaxdiv(intmax_t numer, intmax_t denom)몫과 나머지를 함께imaxdiv_t 는 quot 와 rem 을 담은 구조체다
strtoimaxintmax_t strtoimax(const char * restrict nptr, char ** restrict endptr, int base)읽은 값. 실패는 endptr 과 errno 로★ 규약은 strtol 과 같다 — 부르기 전에 errno 를 0 으로(71장)
strtoumaxuintmax_t strtoumax(const char * restrict nptr, char ** restrict endptr, int base)같음★ 부호 없는데도 - 를 받아들여 감아 돈 값을 준다
wcstoimax·wcstoumax와이드 문자열 판같음(75장)
intmax_t·uintmax_t·wchar_t타입<stdint.h>·<stddef.h> 의 것을 다시 보인다

표 105.27 — <inttypes.h> 요람

꼴무엇보기
PRIBN폭이 정확한 타입을 찍는 형식 — B 자리에 d·i·o·u·x 가 온다PRId32·PRIu64
PRIBLEASTN·PRIBFASTNleast·fast 타입을 찍는 형식PRIdLEAST16·PRIuFAST32
PRIBMAX·PRIBPTRintmax_t·intptr_t 를 찍는 형식PRIdMAX·PRIdPTR
PRIXN·PRIXLEASTN·PRIXFASTN·PRIXMAX·PRIXPTR같은 것의 대문자 16진 판PRIX64

표 105.28 — 형식 매크로를 읽는 규칙

★ 쓰는 법은 문자열을 잇는 것이다 — printf("%" PRId64 "\n", v); 처럼. 읽는 쪽에는 같은 규칙의 SCN… 짝이 있다.

<stdbit.h> — 비트를 세는 표준 방법#

본문: 51장(정독), 29장(정수의 연산).

C23 이 들인 헤더다. 그동안 컴파일러 확장(__builtin_popcount 같은 것)으로만 있던 비트 셈이 표준이 되었다. 이름은 규칙 하나로 읽는다 — stdc_ 로 시작하고, 타입마다 _uc·_us·_ui·_ul·_ull 접미가 붙은 판이 따로 있으며, 접미 없는 이름은 타입 제네릭 매크로다.

이름무엇을 세는가★함정 · 쓸모
stdc_leading_zeros맨 앞의 0 비트 수폭에 기대므로 타입이 중요하다
stdc_leading_ones맨 앞의 1 비트 수
stdc_trailing_zeros맨 뒤의 0 비트 수2 의 몇 승으로 나뉘는지를 뜻한다
stdc_trailing_ones맨 뒤의 1 비트 수
stdc_first_leading_zero·stdc_first_leading_one앞에서부터 처음 나오는 자리(1부터 셈)★ 없으면 0 이다 — 0 이 「첫 자리」가 아니다
stdc_first_trailing_zero·stdc_first_trailing_one뒤에서부터 처음 나오는 자리같음
stdc_count_zeros·stdc_count_ones0 인 비트 수 · 1 인 비트 수stdc_count_ones 가 인구수(popcount)다
stdc_has_single_bit정확히 한 비트만 1 인가2 의 거듭제곱 판정 — ★ 0 은 거짓이다
stdc_bit_width값을 담는 데 필요한 최소 비트 수stdc_bit_width(0) 은 0
stdc_bit_floor·stdc_bit_ceil그 값 이하 · 이상의 가장 가까운 2 의 거듭제곱★ stdc_bit_ceil 이 넘치면 정의되지 않은 동작이다

표 105.29 — <stdbit.h> 요람 — 타입 제네릭 이름으로 적는다

★ __STDC_VERSION_STDBIT_H__ 로 있는지 본다. 그리고 엔디안을 묻는 이름 __STDC_ENDIAN_LITTLE__ · __STDC_ENDIAN_BIG__ · __STDC_ENDIAN_NATIVE__ 도 이 헤더가 준다(6장).

<locale.h> — 「어디의 관습인가」를 바꾼다#

본문: 73장(정독), 74장(숫자·통화·시간·정렬), 8장(문자 집합).

함수는 둘뿐이지만 그 둘이 프로그램 전체의 동작을 바꾼다. 로케일은 전역 상태다 — 이 헤더의 함정은 거의 다 거기서 나온다.

이름꼴인자와 반환하는 일 · ★함정
setlocalechar *setlocale(int category, const char *locale)갈래와 이름을 받아 지금의 로케일 이름을 돌려준다. 실패하면 널locale 이 널이면 바꾸지 않고 묻기만 한다. ★ 돌려준 문자열은 다음 호출이 덮을 수 있다 — 쓰려면 복사한다. ★ 시작할 때는 언제나 "C" 다. ★ 여러 스레드에서 부르는 것은 안전하지 않다
localeconvstruct lconv *localeconv(void)지금 로케일의 숫자·통화 서식을 담은 구조체★ 돌려준 것을 고치면 안 되고, setlocale 이 그 내용을 바꿀 수 있다 — 값이 필요하면 그때 읽는다
LC_ALL갈래전부★ "C" 아닌 값을 통째로 켜면 printf 의 소수점까지 바뀐다 — 파일 형식이 깨지는 흔한 사고다
LC_COLLATE갈래정렬 순서strcoll·strxfrm 이 이것을 본다
LC_CTYPE갈래글자의 갈래와 대소문자<ctype.h> 와 멀티바이트 변환이 본다
LC_MONETARY갈래통화 서식localeconv 로 읽어 직접 찍는다
LC_NUMERIC갈래소수점과 자릿수 묶음★ printf·strtod 가 이것을 본다
LC_TIME갈래날짜와 시각 서식strftime 이 본다
NULL매크로<stddef.h> 의 것

표 105.30 — <locale.h> 요람

<wctype.h> — 와이드 문자의 갈래#

본문: 75장(와이드 문자), 73장(로케일), 8장(문자 집합).

<ctype.h> 의 와이드 판이다. 이름 앞에 isw·tow 가 붙는 것만 다르다고 보면 되는데, 이름으로 갈래를 지정하는 두 쌍(wctype·iswctype, wctrans·towctrans)이 더 있다.

이름무엇이 참인가 · 무엇을 하는가비고 · ★함정
iswalnum글자 또는 숫자<ctype.h> 의 짝과 뜻이 같다
iswalpha글자로케일이 정한다
iswblank칸 띄우개C99
iswcntrl제어 문자
iswdigit0~9★ 로케일과 무관하다
iswgraph공백을 뺀, 그려지는 글자
iswlower소문자
iswprint그려지는 글자
iswpunct문장 부호류
iswspace공백류
iswupper대문자
iswxdigit16진 숫자★ 로케일과 무관하다
towlower·towupper대응하는 소문자·대문자로, 없으면 그대로★ 한 글자 대 한 글자라는 한계가 그대로 있다
wctype갈래 이름("alpha" 같은 것)을 wctype_t 로 바꾼다없는 이름이면 0
iswctypewctype 이 준 갈래에 드는가로케일이 정의한 갈래도 쓸 수 있다
wctrans바꿈 이름("tolower" 같은 것)을 wctrans_t 로없는 이름이면 0
towctranswctrans 가 준 바꿈을 적용한다
wint_t와이드 문자 또는 WEOF 를 담는 타입★ wchar_t 가 아니다 — WEOF 가 들어가야 하기 때문
WEOF입력의 끝을 뜻하는 wint_t 값★ -1 이라는 보장이 없다
wctype_t·wctrans_t갈래와 바꿈을 가리키는 타입

표 105.31 — <wctype.h> 요람

<uchar.h> — 유니코드 폭을 못박은 문자#

본문: 77장(유니코드), 75장(멀티바이트 변환), 8장(문자 집합).

wchar_t 의 크기가 기계마다 다른 문제(75장)에 대한 표준의 답이다. 폭을 이름에 박았고, 변환 함수는 상태를 들고 한 글자씩 오간다.

이름꼴인자와 반환하는 일 · ★함정
mbrtoc8size_t mbrtoc8(char8_t * restrict pc8, const char * restrict s, size_t n, mbstate_t * restrict ps)읽은 바이트 수. 특별한 값으로 상황을 알린다C23. ★ 한 번에 UTF-8 한 바이트를 내놓을 수 있어 여러 번 불러야 한다
c8rtombsize_t c8rtomb(char *s, char8_t c8, mbstate_t *ps)쓴 바이트 수C23. 반대 방향
mbrtoc16size_t mbrtoc16(char16_t * restrict pc16, const char * restrict s, size_t n, mbstate_t * restrict ps)같음★ 서로게이트 쌍 때문에 한 글자가 두 번에 나올 수 있다
c16rtombsize_t c16rtomb(char *s, char16_t c16, mbstate_t *ps)쓴 바이트 수반대 방향
mbrtoc32size_t mbrtoc32(char32_t * restrict pc32, const char * restrict s, size_t n, mbstate_t * restrict ps)같음코드 포인트 하나가 한 번에 나온다 — 다루기 가장 쉽다
c32rtombsize_t c32rtomb(char *s, char32_t c32, mbstate_t *ps)쓴 바이트 수반대 방향
char8_t·char16_t·char32_t타입UTF-8 · UTF-16 · UTF-32 의 코드 단위★ 폭은 못박혔지만, char8_t 는 C23 에서 왔다
mbstate_t타입변환이 들고 다니는 상태★ 0 으로 초기화해 두고 같은 것을 계속 넘긴다 — 문자열마다 새로 둔다
size_t타입<stddef.h> 의 것

표 105.32 — <uchar.h> 요람

★ 돌려주는 size_t 의 특별한 값들이 규약의 핵심이다 — 0(널 문자), (size_t)-1 (부호화 오류, errno 는 EILSEQ), (size_t)-2(바이트가 모자라 아직 판단 못 함), (size_t)-3(내놓을 것이 상태에 남아 있어 입력을 더 읽지 않았음).

<signal.h> — 밖에서 들어오는 끼어듦#

본문: 81장(정독), 80장(진단과 제어), 11장(끼어듦).

함수는 둘뿐인데, 이 헤더의 무게는 함수가 아니라 처리기 안에서 무엇을 해도 되는가 라는 계약에 있다. 표준이 허락하는 것이 놀랄 만큼 적다.

이름꼴인자와 반환하는 일 · ★함정
signalvoid (*signal(int sig, void (*func)(int)))(int)신호에 붙일 처리기를 정한다. 이전 처리기를 돌려주고, 실패하면 SIG_ERR★ 꼴이 어려워 보이지만 「int 를 받는 함수 포인터를 받아 같은 것을 돌려준다」가 전부다. ★ 처리기가 불린 뒤 기본으로 되돌아갈 수 있다 — 표준이 어느 쪽인지 정하지 않았다
raiseint raise(int sig)스스로에게 신호를 보낸다. 성공하면 0abort 가 SIGABRT 로 하는 일이 이것이다
sig_atomic_t정수 타입처리기와 본체가 나눠 쓰는 유일한 타입★ volatile sig_atomic_t 로 적는다. 그것 말고는 처리기가 만질 수 있는 것이 거의 없다
SIG_DFL·SIG_IGNsignal 에 넘기는 값기본 동작 · 무시
SIG_ERRsignal 의 실패 반환값★ 널이 아니다 — 따로 비교해야 한다
SIGABRT비정상 종료abort 가 낸다
SIGFPE잘못된 산술0 으로 나누기 등★ 이름과 달리 정수 나눗셈에서도 난다
SIGILL잘못된 명령
SIGINT사용자의 끼어듦 요청보통 Ctrl-C
SIGSEGV잘못된 기억 접근★ 여기서 「복구」하려는 시도는 거의 언제나 잘못이다
SIGTERM끝내 달라는 요청보내는 쪽의 정중한 요청

표 105.33 — <signal.h> 요람

★ 처리기 안에서 표준이 확실히 허락하는 것은 셋뿐이다 — volatile sig_atomic_t 에 쓰기, signal 을 다시 부르기, abort·_Exit·quick_exit 로 끝내기. printf 도 malloc 도 안 된다(81장).

<fenv.h> — 부동소수점 환경을 만진다#

본문: 78장(수 다루기), 52장(근사의 수학), 7장(수의 표현).

반올림 방향과 예외 깃발을 직접 다루는 헤더다. ★ 이것을 쓰려면 먼저 #pragma STDC FENV_ACCESS ON 을 켜야 한다 — 켜지 않으면 컴파일러가 「이 프로그램은 환경을 만지지 않는다」고 가정하고 계산을 옮겨 버린다.

무리이름뜻
예외 깃발FE_DIVBYZERO·FE_INEXACT·FE_INVALID·FE_OVERFLOW·FE_UNDERFLOW0 으로 나눔 · 정확하지 않음 · 잘못된 연산 · 넘침 · 아래넘침
전부FE_ALL_EXCEPT위의 모든 깃발을 묶은 것
반올림 방향FE_TONEAREST·FE_TOWARDZERO·FE_UPWARD·FE_DOWNWARD·FE_TONEARESTFROMZERO가장 가까운 쪽(기본) · 0 쪽 · 위 · 아래 · 반은 0 에서 먼 쪽
기본 환경·모드FE_DFL_ENV·FE_DFL_MODE·FE_DYNAMIC시작할 때의 환경 · 기본 모드 · 실행 중에 정해짐
십진FE_DEC_TONEAREST·FE_DEC_TOWARDZERO·FE_DEC_UPWARD·FE_DEC_DOWNWARD·FE_DEC_TONEARESTFROMZERO십진 부동소수점의 같은 것들
프라그마FENV_ACCESS·FENV_ROUND·FENV_DEC_ROUND·STDC#pragma STDC … 로 쓴다
기타FE_SNANS_ALWAYS_SIGNAL시그널링 NaN 이 언제나 신호를 내는가

표 105.34 — 예외 깃발과 반올림 방향

이름하는 일비고 · ★함정
feclearexcept깃발을 지운다★ 재기 전에 지우는 것이 규약이다
fetestexcept올라 있는 깃발을 묻는다묻는 깃발들의 논리합을 돌려준다
feraiseexcept·fesetexcept깃발을 올린다 — 신호를 내며 · 조용히fesetexcept 는 C23
fegetexceptflag·fesetexceptflag깃발 상태를 fexcept_t 에 담고 되돌린다
fetestexceptflag담아 둔 상태에서 묻는다C23
fegetround·fesetround반올림 방향을 읽고 정한다FLT_ROUNDS 가 이것을 비춘다
fegetmode·fesetmode모드 전체를 femode_t 로 읽고 정한다C23
fegetenv·fesetenv환경 전체를 fenv_t 로 읽고 정한다
feholdexcept환경을 저장하고 깃발을 지운 뒤 조용한 모드로계산을 감싸는 앞쪽
feupdateenv저장한 환경을 되돌리며 그 사이 생긴 깃발을 다시 올린다감싸는 뒤쪽
fe_dec_getround·fe_dec_setround십진 부동소수점의 반올림 방향C23
fenv_t·fexcept_t·femode_t환경 · 깃발 · 모드를 담는 타입

표 105.35 — <fenv.h> 요람

<tgmath.h> — 타입에 맞는 것을 골라 준다#

본문: 78장(정독), 52장(근사의 수학).

실을 새 이름이 없는 헤더다. <math.h> 와 <complex.h> 를 포함하고, 그 함수들을 같은 이름의 타입 제네릭 매크로로 덮는다.

인자의 타입고르는 것보기
floatf 접미가 붙은 판sqrt(x) → sqrtf
double·정수접미 없는 판sqrt(x) → sqrt — ★ 정수는 double 로 올라간다
long doublel 접미가 붙은 판sqrt(x) → sqrtl
복소수c 접두가 붙은 판sqrt(z) → csqrt

표 105.36 — 무엇을 골라 주는가

★ 그래서 <tgmath.h> 를 포함하면 sqrtf·sqrtl 을 손으로 고를 일이 없다. 다만 매크로이므로 함수 포인터로 넘길 수 없고, 인자를 두 번 평가하지 않는다는 보장을 표준이 따로 두었다.

<complex.h> — 복소수#

본문: 78장(수 다루기), 52장(근사의 수학), 27장(타입의 갈래).

선택 사항인 헤더다 — __STDC_NO_COMPLEX__ 가 정의되어 있으면 없다. 이름은 규칙 하나로 읽는다: <math.h> 의 함수 앞에 c 를 붙인 것이고, float·long double 판은 뒤에 f·l 이 붙는다(csqrtf·csqrtl).

이름무엇을 돌려주는가★함정
creal실수부
cimag허수부
carg편각 — 복소평면에서의 각범위는 −𝜋 에서 𝜋 까지
cabs절댓값 — 원점까지의 거리★ 중간 계산이 넘치지 않도록 만들어져 있다
conj켤레복소수허수부의 부호를 뒤집는다
cproj리만 구면으로의 사영무한대를 하나로 모은다

표 105.37 — 값을 뜯어 보는 넷

무리이름비고
지수와 로그cexp · clog · cpow · csqrt★ clog 와 csqrt 는 가지 자름(branch cut)이 있어, 음의 실수축에서 부호에 따라 답이 갈린다
삼각csin · ccos · ctan
역삼각casin · cacos · catan가지 자름이 있다
쌍곡csinh · ccosh · ctanh
역쌍곡casinh · cacosh · catanh가지 자름이 있다

표 105.38 — <math.h> 와 짝을 이루는 것들

이름무엇★함정
CMPLX·CMPLXF·CMPLXL실수부와 허수부로 복소수를 만든다★ x + y*I 로 만들면 안 되는 경우가 있다 — y 가 무한대이거나 NaN 이면 곱셈이 답을 망친다. C11 이 이 매크로를 들인 까닭이다
CX_LIMITED_RANGE#pragma STDC CX_LIMITED_RANGE ON 으로 켠다곱셈·나눗셈에서 넘침을 피하는 느린 셈법을 꺼도 좋다고 알리는 것
STDC프라그마의 첫 낱말

표 105.39 — 값을 만들고 계산을 고르는 것들

★ 허수 단위는 I 이고, _Complex_I·_Imaginary_I 가 그 바탕이다. 순허수 타입은 선택 사항이라 __STDC_IEC_559_COMPLEX__ 와 함께 살펴야 한다.

<threads.h> — 표준이 정한 스레드#

본문: 84장(정독), 85장(원자적 연산), 11장(끼어듦).

선택 사항이다 — __STDC_NO_THREADS__ 가 정의되어 있으면 없다(실제로 없는 구현이 많아, 실무에서는 POSIX 스레드나 윈도우 API 를 쓰는 일이 흔하다). 이름은 접두로 갈린다: thrd_(스레드) · mtx_(자물쇠) · cnd_(조건 변수) · tss_(스레드마다 따로인 저장소).

이름하는 일비고 · ★함정
thrd_create스레드를 만들어 함수를 돌린다함수 꼴은 thrd_start_t — int (*)(void *) 다
thrd_join끝나기를 기다리고 반환값을 받는다★ join 하거나 detach 하거나, 둘 중 하나는 해야 한다
thrd_detach끝나면 스스로 치우게 한다★ detach 한 것은 join 할 수 없다
thrd_exit지금 스레드를 끝낸다tss 소멸자가 불린다
thrd_current지금 스레드의 손잡이
thrd_equal두 손잡이가 같은 스레드인가★ == 로 견주면 안 된다 — thrd_t 의 속은 정해져 있지 않다
thrd_sleep정해진 시간만큼 쉰다남은 시간을 돌려받을 수 있다
thrd_yield차례를 넘긴다

표 105.40 — 스레드 그 자체 — thrd_

이름하는 일비고 · ★함정
mtx_init·mtx_destroy자물쇠를 만들고 없앤다mtx_plain·mtx_timed·mtx_recursive 중에 고른다
mtx_lock·mtx_unlock잠그고 푼다★ 잠근 스레드가 푼다
mtx_trylock잠기지 않으면 잠그고, 잠겼으면 바로 돌아온다
mtx_timedlock정해진 시각까지만 기다린다mtx_timed 로 만든 것에만
cnd_init·cnd_destroy조건 변수를 만들고 없앤다
cnd_wait·cnd_timedwait자물쇠를 놓고 기다렸다가 깨면 다시 잡는다★ 깨어난 까닭을 다시 확인해야 한다 — 헛깨움이 있다. while 로 감싼다
cnd_signal·cnd_broadcast하나를 · 모두를 깨운다
call_once여러 스레드가 불러도 딱 한 번만 돌린다once_flag 를 ONCE_FLAG_INIT 로 두고 쓴다

표 105.41 — 맞춰 쓰기 — mtx_ · cnd_ · call_once

이름무엇비고
tss_create·tss_delete열쇠를 만들고 없앤다소멸자를 함께 준다 — tss_dtor_t
tss_get·tss_set이 스레드 몫의 값을 읽고 쓴다★ 소멸자가 도는 횟수의 상한이 TSS_DTOR_ITERATIONS 다
thrd_t·mtx_t·cnd_t·tss_t손잡이 타입들속을 들여다보지 않는다
thrd_start_t·tss_dtor_t함수 포인터 타입

표 105.42 — 스레드마다 따로인 저장소와 타입들

★ 돌려주는 값은 thrd_success · thrd_busy · thrd_error · thrd_nomem · thrd_timedout 이다. 0 이 성공이 아니다 — 다른 C 함수와 규약이 다르다.

<stdatomic.h> — 쪼개지지 않는 연산#

본문: 85장(정독), 84장(스레드), 12장(기억의 사다리).

선택 사항이다 — __STDC_NO_ATOMICS__ 가 정의되어 있으면 없다. 이름은 규칙으로 읽는다: atomic_ 으로 시작하고, _explicit 이 붙은 짝은 메모리 순서를 직접 고르는 판이다. 붙지 않은 것은 가장 강한 순서(memory_order_seq_cst)를 쓴다.

이름하는 일★함정
atomic_init원자적 객체를 처음 값으로 채운다★ 이것 자체는 원자적이 아니다 — 남이 보기 전에 한다
atomic_load·atomic_load_explicit읽는다
atomic_store·atomic_store_explicit쓴다
atomic_exchange·atomic_exchange_explicit바꾸고 옛 값을 돌려준다
atomic_compare_exchange_strong·atomic_compare_exchange_strong_explicit기대한 값이면 바꾼다★ 실패하면 기대값 자리에 지금 값을 써 준다
atomic_compare_exchange_weak·atomic_compare_exchange_weak_explicit같지만 까닭 없이 실패할 수 있다그래서 루프 안에서 쓴다 — 대신 더 빠르다
atomic_fetch_key·atomic_fetch_key_explicitkey 자리에 add·sub·or·xor·and 가 들어간다옛 값을 돌려준다
atomic_flag_test_and_set·atomic_flag_test_and_set_explicit깃발을 세우고 이전 상태를 돌려준다★ atomic_flag 는 언제나 락 없이 도는 유일한 타입이다
atomic_flag_clear·atomic_flag_clear_explicit깃발을 내린다ATOMIC_FLAG_INIT 로 초기화한다
atomic_is_lock_free그 타입이 락 없이 도는가★ 타입마다 ATOMIC_…_LOCK_FREE 매크로로 컴파일 때 물을 수도 있다
atomic_thread_fence·atomic_signal_fence순서의 울타리를 세운다 — 스레드 사이 · 신호 처리기 사이
kill_dependency의존 사슬을 끊는다memory_order_consume 과 함께 쓰는데, 실제로 쓰이는 일이 드물다

표 105.43 — <stdatomic.h> 의 연산

무리이름비고
문자atomic_char8_t · atomic_char16_t · atomic_char32_t · atomic_wchar_t
폭이 정해진 것atomic_int_least8_t · atomic_int_least16_t · atomic_int_least32_t · atomic_int_least64_t 와 부호 없는 짝 atomic_uint_least8_t · atomic_uint_least16_t · atomic_uint_least32_t · atomic_uint_least64_t
빠른 것atomic_int_fast8_t · atomic_int_fast16_t · atomic_int_fast32_t · atomic_int_fast64_t 와 atomic_uint_fast8_t · atomic_uint_fast16_t · atomic_uint_fast32_t · atomic_uint_fast64_t
그 밖atomic_intmax_t · atomic_uintmax_t · atomic_intptr_t · atomic_uintptr_t · atomic_size_t · atomic_ptrdiff_t_Atomic 을 직접 붙여 써도 된다

표 105.44 — 타입 이름 — atomic_ 을 붙인 것들

<wchar.h> — 와이드 문자의 모든 것#

본문: 75장(와이드 문자 ①), 76장(와이드 문자 ②), 69장(읽고 쓰기).

가장 큰 헤더 가운데 하나인데, 새로 배울 것은 거의 없다. <string.h> · <stdio.h> · <stdlib.h> 의 함수들을 와이드 판으로 옮긴 것이기 때문이다. 규칙은 셋이다 — 문자열은 wcs, 기억 블록은 wmem, 입출력은 이름 안에 w.

무리이름비고 · ★함정
길이wcslen★ 글자 수가 아니라 코드 단위 수다 — UTF-16 인 곳에서는 다르다
복사wcscpy · wcsncpy · wmemcpy · wmemmove★ wcsncpy 는 strncpy 와 같은 함정을 그대로 물려받았다(끝을 안 붙일 수 있다)
이어붙이기wcscat · wcsncat
비교wcscmp · wcsncmp · wmemcmp · wcscoll · wcsxfrmwcscoll·wcsxfrm 은 로케일 순서를 따른다(74장)
찾기wcschr · wcsrchr · wcsstr · wcspbrk · wcsspn · wcscspn · wmemchr
자르기wcstok★ 상태를 밖에서 들고 있어야 한다 — strtok 와 달리 셋째 인자가 있다
채우기wmemset
시각 서식wcsftimestrftime 의 와이드 판

표 105.45 — 문자열과 기억 — <string.h> 의 짝

무리이름비고 · ★함정
한 글자btowc · wctob★ 단일 바이트로 표현되는 글자에만 뜻이 있다
상태를 들고mbrtowc · wcrtomb · mbrlen · mbsinit★ mbstate_t 를 0 으로 두고 같은 것을 계속 넘긴다
문자열 통째로mbsrtowcs · wcsrtombs★ 부분 변환이 가능하도록 원본 포인터를 고쳐 준다
수로wcstol · wcstoul · wcstod · wcstof · wcstold규약은 strtol 무리와 같다(71장)
십진 부동소수점wcstod32 · wcstod64 · wcstod128선택 사항

표 105.46 — 변환 — 바이트와 와이드 사이, 그리고 수로

무리이름비고 · ★함정
한 글자fgetwc · getwc · getwchar · fputwc · putwc · putwchar · ungetwc끝은 WEOF 다
한 줄fgetws · fputws
형식 출력wprintf · fwprintf · swprintf · vwprintf · vfwprintf · vswprintf★ swprintf 는 snprintf 처럼 크기를 받는다
형식 입력wscanf · fwscanf · swscanf · vwscanf · vfwscanf · vswscanf
넓이 정하기fwide★ 스트림은 바이트 쪽이나 와이드 쪽 중 하나로 굳는다 — 섞어 쓰면 안 된다(76장)

표 105.47 — 입출력 — <stdio.h> 의 짝

무리이름비고
타입wchar_t · wint_t · mbstate_t · size_t · FILE다른 헤더에서 온 것들을 다시 보인다
매크로WEOF · WCHAR_MIN · WCHAR_MAX · NULL
부속서 Kwcscpy_s · wcsncpy_s · wcscat_s · wcsncat_s · wmemcpy_s · wmemmove_s · wcstok_s · wcsnlen_s · wcrtomb_s · mbsrtowcs_s · wcsrtombs_s · wprintf_s · fwprintf_s · swprintf_s · snwprintf_s · vwprintf_s · vfwprintf_s · vswprintf_s · vsnwprintf_s · wscanf_s · fwscanf_s · swscanf_s · vwscanf_s · vfwscanf_s · vswscanf_s★K 선택 사항이고 널리 구현되지 않았다(83장)

표 105.48 — 타입 · 매크로 · 부속서 K

<time.h> — 시각과 시간#

본문: 79장(정독), 74장(시간 서식), 98장(바깥 세계).

시각을 세는 방법이 셋이라는 것만 알면 나머지는 그 셋 사이를 오가는 함수들이다 — 달력 시각(time_t) · 쪼갠 시각(struct tm) · 프로세서 시간(clock_t).

이름꼴인자와 반환하는 일 · ★함정
timetime_t time(time_t *timer)지금의 달력 시각. 알 수 없으면 (time_t)-1★ time_t 의 뜻과 정밀도를 표준이 정하지 않았다 — 유닉스 초라는 보장이 없다
difftimedouble difftime(time_t time1, time_t time0)두 시각의 차를 초로★ 빼기로 계산하면 안 된다 — time_t 가 초라는 보장이 없기 때문이다
clockclock_t clock(void)프로세서 시간. 알 수 없으면 (clock_t)-1★ 벽시계가 아니다. 초로 바꾸려면 CLOCKS_PER_SEC 으로 나눈다. ★ 감아 돌 수 있다
mktimetime_t mktime(struct tm *timeptr)쪼갠 시각을 달력 시각으로★ 인자를 고쳐 준다 — 범위를 벗어난 값을 정규화하고 tm_wday·tm_yday 를 채운다. 그래서 「석 달 뒤」를 셀 때 쓴다
localtime·gmtimestruct tm *localtime(const time_t *timer) · struct tm *gmtime(const time_t *timer)달력 시각을 지역 시각 · UTC 로 쪼갠다★ 정적 저장소를 돌려준다 — 다음 호출이 덮고, 스레드 안전이 아니다
localtime_r·gmtime_rstruct tm *localtime_r(const time_t *timer, struct tm *buf) · struct tm *gmtime_r(const time_t *timer, struct tm *buf)같지만 받을 그릇을 준다C23 이 표준으로 들였다 — 그전에는 POSIX 의 것이었다. 새 코드는 이쪽
localtime_s·gmtime_s부속서 K 판같음★K (83장)
timegmtime_t timegm(struct tm *timeptr)쪼갠 시각을 UTC 로 보고 달력 시각으로C23. mktime 의 UTC 짝이다
asctime·ctimechar *asctime(const struct tm *timeptr) · char *ctime(const time_t *timer)정해진 꼴의 문자열★ 쓰지 않는 편이 낫다 — 정적 버퍼를 돌려주고, 꼴이 고정이라 로케일도 못 따른다
asctime_s·ctime_s부속서 K 판그릇과 크기를 받는다★K
strftimesize_t strftime(char * restrict s, size_t maxsize, const char * restrict format, const struct tm * restrict timeptr)쓴 글자 수. 자리가 모자라면 0★ 0 이 실패다 — 그런데 빈 문자열을 만들었을 때도 0 이다. 서식은 LC_TIME 을 따른다(74장)
timespec_getint timespec_get(struct timespec *ts, int base)base 를 돌려준다. 실패하면 0C11. TIME_UTC 를 준다. C23 이 TIME_MONOTONIC·TIME_ACTIVE·TIME_THREAD_ACTIVE 를 더했다
timespec_getresint timespec_getres(struct timespec *ts, int base)그 시계의 해상도C23. 「얼마나 잘게 잴 수 있는가」를 묻는다

표 105.49 — <time.h> 요람

이름무엇★함정
time_t달력 시각★ 실수형일 수도 있다. 뜻도 정밀도도 정해져 있지 않다
clock_t프로세서 시간CLOCKS_PER_SEC 으로 나눈다
struct tm쪼갠 시각★ tm_year 는 1900 을 뺀 값, tm_mon 은 0 부터다
struct timespec초와 나노초timespec_get 이 채운다
CLOCKS_PER_SECclock_t 를 초로 바꾸는 나눗수★ 1,000,000 이라는 보장은 없다
TIME_UTC·TIME_MONOTONIC·TIME_ACTIVE·TIME_THREAD_ACTIVEtimespec_get 이 고르는 시계뒤의 셋은 C23
NULL·size_t·errno_t·rsize_t다른 헤더에서 온 이름들뒤의 둘은 ★K

표 105.50 — 타입과 매크로

<stdlib.h> — 잡동사니 서랍#

본문: 71장(정독), 46장(동적 기억), 89장(할당기).

이름 그대로 서랍이다 — 기억 할당 · 프로그램 종료 · 문자열에서 수로 · 정렬과 찾기 · 난수 · 멀티바이트 변환이 한 헤더에 들어 있다. 무리별로 갈라 본다.

이름꼴인자와 반환하는 일 · ★함정
mallocvoid *malloc(size_t size)자리를 잡아 준다. 실패하면 널★ 내용은 불확정이다. ★ size 가 0 이면 널이거나 유일한 자리인데, 어느 쪽인지 정해져 있지 않다
callocvoid *calloc(size_t nmemb, size_t size)같지만 0 으로 채운다★ 곱셈의 넘침을 함수가 검사해 준다 — 그래서 배열에는 이쪽이 안전하다
reallocvoid *realloc(void *ptr, size_t size)크기를 바꾼다. 실패하면 널★★ 실패해도 원본은 살아 있다 — p = realloc(p, n) 은 실패하면 원본을 잃는다. ★ C23 에서 size 가 0 이면 정의되지 않은 동작이 되었다
aligned_allocvoid *aligned_alloc(size_t alignment, size_t size)정렬을 맞춰 잡아 준다★ size 가 alignment 의 배수여야 한다
freevoid free(void *ptr)돌려준다★ 널을 넘기는 것은 안전하다. ★ 두 번 부르면 정의되지 않은 동작이다
free_sized·free_aligned_sizedvoid free_sized(void *ptr, size_t size) · void free_aligned_sized(void *ptr, size_t alignment, size_t size)크기를 함께 알려 주며 돌려준다C23. 할당기가 크기를 따로 안 적어 두어도 되게 한다 — ★ 틀린 크기를 주면 정의되지 않은 동작이다
memalignmentsize_t memalignment(const void *p)그 주소가 만족하는 정렬C23

표 105.51 — <stdlib.h> 요람 — 기억 할당

이름꼴인자와 반환하는 일 · ★함정
exitvoid exit(int status)정상 종료. 돌아오지 않는다atexit 로 등록한 것을 부르고 스트림을 흘려보낸다. ★ 신호 처리기에서 부르면 안 된다
quick_exitvoid quick_exit(int status)빠른 종료at_quick_exit 로 등록한 것만 부른다 — 스트림을 흘려보내지 않는다
_Exitvoid _Exit(int status)즉시 종료★ 등록한 것을 하나도 부르지 않는다. 신호 처리기에서 안전한 유일한 종료
abortvoid abort(void)비정상 종료SIGABRT 를 낸다. ★ 스트림을 흘려보낸다는 보장이 없다
atexit·at_quick_exitint atexit(void (*func)(void)) · int at_quick_exit(void (*func)(void))끝날 때 부를 함수를 등록한다. 성공하면 0★ 등록의 역순으로 불린다. 적어도 32 개는 등록할 수 있다
systemint system(const char *string)명령 처리기에 넘긴다★ 널을 주면 처리기가 있는지 묻는 뜻이다. ★★ 사용자 입력을 그대로 넘기면 명령 주입이 된다
getenvchar *getenv(const char *name)환경 변수의 값. 없으면 널★ 돌려준 문자열을 고치면 안 되고, 다음 호출이 덮을 수 있다
getenv_s부속서 K 판길이도 함께 받는다★K (83장)
EXIT_SUCCESS·EXIT_FAILURE매크로exit 에 넘기는 값★ 성공은 0 과 EXIT_SUCCESS 둘 다 되지만, 실패의 값은 EXIT_FAILURE 뿐이다

표 105.52 — <stdlib.h> 요람 — 프로그램의 끝

이름꼴인자와 반환하는 일 · ★함정
atoi·atol·atofint atoi(const char *nptr) 등읽은 값★★ 실패를 알릴 방법이 없다. 「0」과 「읽지 못했다」가 구별되지 않는다 — 새 코드에서는 쓰지 않는다
strtol·strtoullong strtol(const char * restrict nptr, char ** restrict endptr, int base) 등읽은 값. 어디서 멈췄는지는 endptr 로★ 넘치면 LONG_MAX·LONG_MIN 을 주고 errno 를 ERANGE 로 — 부르기 전에 errno 를 0 으로 두어야 한다. ★ base 가 0 이면 접두(0x·0)로 진법을 짐작한다
strtod·strtof·strtold실수 판같음★ 소수점이 LC_NUMERIC 을 따른다 — 파일 형식을 읽을 때 사고가 난다(73장)
strtod32·strtod64·strtod128십진 부동소수점 판같음선택 사항
strfromd·strfromf·strfromlint strfromd(char * restrict s, size_t n, const char * restrict format, double fp) 등반대 방향 — 수를 문자열로. snprintf 처럼 쓰려 한 길이를 돌려준다C23. ★ 로케일의 영향을 받지 않는다 — 그래서 파일 형식에 알맞다
strfromd32·strfromd64·strfromd128십진 판같음선택 사항

표 105.53 — <stdlib.h> 요람 — 문자열과 수 사이

이름꼴인자와 반환하는 일 · ★함정
abs·labs·llabsint abs(int j) 등절댓값★ 가장 작은 음수는 표현할 수 없다 — 정의되지 않은 동작
div·ldiv·lldivdiv_t div(int numer, int denom) 등몫과 나머지를 한 번에div_t·ldiv_t·lldiv_t 는 quot·rem 을 담는다
qsortvoid qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *))자리에서 정렬한다★ 안정 정렬이 아니다. ★ 비교 함수가 일관되지 않으면 정의되지 않은 동작이다 — 뺄셈으로 비교하면 넘칠 수 있다
bsearchvoid *bsearch(const void *key, const void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *))찾으면 그 자리, 없으면 널★ 이미 정렬되어 있어야 한다 — 아니면 정의되지 않은 동작
qsort_s·bsearch_s부속서 K 판문맥 포인터를 함께 넘긴다★K 비교 함수에 상태를 줄 수 있어 더 낫다
rand·srandint rand(void) · void srand(unsigned int seed)0 부터 RAND_MAX 까지★★ 암호에 쓰면 안 된다. ★ 품질이 구현마다 다르고, 스레드 안전이 아니다
mblen·mbtowc·wctombint mblen(const char *s, size_t n) 등한 글자씩 오간다★ 함수 안에 상태가 숨어 있다 — 스레드 안전이 아니다. 상태를 밖에 두는 <wchar.h> 의 mbrtowc 무리를 쓴다
mbstowcs·wcstombs문자열 통째로바꾼 글자 수★ 끝을 붙인다는 보장이 없다 — 자리가 꼭 맞으면 안 붙는다
mbtowc_s 계열mbstowcs_s·wcstombs_s·wctomb_s부속서 K 판★K
MB_CUR_MAX매크로지금 로케일에서 한 글자의 최대 바이트★ 상수가 아니다 — 로케일에 따라 바뀐다
RAND_MAX매크로rand 의 상한적어도 32767

표 105.54 — <stdlib.h> 요람 — 셈 · 정렬 · 난수 · 멀티바이트

이름무엇비고
set_constraint_handler_s계약이 깨졌을 때 부를 것을 정한다★K constraint_handler_t 꼴
abort_handler_s·ignore_handler_s바로 끝내는 처리기 · 무시하는 처리기★K 미리 마련된 둘
call_once·ONCE_FLAG_INIT<threads.h> 의 것을 다시 보인다
NULL·size_t·wchar_t·errno_t·rsize_t다른 헤더에서 온 이름들뒤의 둘은 ★K

표 105.55 — 부속서 K 의 제약 처리기

<stdio.h> — 스트림 입출력#

본문: 68장(스트림의 실제), 69장(읽고 쓰기의 함정), 9장(스트림의 기원).

모든 것이 스트림이라는 하나의 그림 위에 있다. 열고 · 읽고 쓰고 · 자리를 옮기고 · 닫는다. 그리고 그 사이에 버퍼가 있다는 사실이 이 헤더 함정의 절반이다.

이름꼴인자와 반환하는 일 · ★함정
fopenFILE *fopen(const char * restrict filename, const char * restrict mode)스트림. 실패하면 널★ "b" 를 붙이지 않으면 텍스트 모드라 줄 끝이 바뀔 수 있다. ★ 실패의 까닭은 errno 에 있다
freopenFILE *freopen(const char * restrict filename, const char * restrict mode, FILE * restrict stream)이미 있는 스트림을 다른 파일로 다시 연다stdin·stdout 을 파일로 돌릴 때 쓴다
fcloseint fclose(FILE *stream)닫는다. 성공하면 0, 실패하면 EOF★★ 반환값을 봐야 한다 — 흘려보내다 실패한 쓰기가 여기서 처음 드러난다
fflushint fflush(FILE *stream)버퍼를 흘려보낸다★ 널을 주면 출력 스트림 전부를 흘려보낸다. ★ 입력 스트림에 부르는 것은 정의되지 않은 동작이다
setbuf·setvbufvoid setbuf(FILE * restrict stream, char * restrict buf) · int setvbuf(FILE * restrict stream, char * restrict buf, int mode, size_t size)버퍼를 갈아 끼운다★ 아무것도 읽고 쓰기 전에 불러야 한다. mode 는 _IOFBF(완전) · _IOLBF(줄) · _IONBF(안 함)
remove·renameint remove(const char *filename) · int rename(const char *old, const char *new)지우고 이름을 바꾼다. 성공하면 0★ 열려 있는 파일에 대한 동작은 정해져 있지 않다. ★ rename 이 이미 있는 이름을 덮을지도 정해져 있지 않다
tmpfile·tmpnamFILE *tmpfile(void) · char *tmpnam(char *s)임시 파일 · 임시 이름★★ tmpnam 은 이름만 준다 — 그 이름을 쓰기까지의 틈이 보안 구멍이다. tmpfile 을 쓴다. ★ TMP_MAX 가 만들 수 있는 이름의 수

표 105.56 — <stdio.h> 요람 — 열고 닫고 버퍼

이름꼴인자와 반환하는 일 · ★함정
fgetc·getc·getcharint fgetc(FILE *stream) 등한 글자. 끝이나 오류면 EOF★★ int 로 받아야 한다 — char 로 받으면 EOF 와 0xFF 를 구별할 수 없다. ★ getc 는 매크로일 수 있어 인자를 여러 번 평가할 수 있다
fputc·putc·putcharint fputc(int c, FILE *stream) 등쓴 글자, 실패하면 EOF★ putc 도 매크로일 수 있다
ungetcint ungetc(int c, FILE *stream)한 글자를 되돌려 놓는다★ 한 글자만 보장된다
fgetschar *fgets(char * restrict s, int n, FILE * restrict stream)읽은 문자열, 끝이면 널★ 줄 끝 문자를 함께 담는다. 줄이 잘렸는지 알려면 그것이 있는지 본다
fputs·putsint fputs(const char * restrict s, FILE * restrict stream) · int puts(const char *s)쓴다★ puts 는 줄 끝을 붙이고 fputs 는 붙이지 않는다
fread·fwritesize_t fread(void * restrict ptr, size_t size, size_t nmemb, FILE * restrict stream) 등읽고 쓴 원소의 수★ 바이트 수가 아니다. 적게 돌아왔을 때 끝인지 오류인지는 feof·ferror 로 가른다
printf 무리printf · fprintf · sprintf · snprintf쓴 글자 수★★ sprintf 는 길이를 모른다 — snprintf 를 쓴다. ★ snprintf 는 쓰려 했던 길이를 돌려주므로 잘렸는지 판별할 수 있다
vprintf 무리vprintf · vfprintf · vsprintf · vsnprintfva_list 를 받는 판(63장)
scanf 무리scanf · fscanf · sscanf읽어 채운 항목의 수★★ %s 에 폭을 적지 않으면 넘친다. ★ 반환값을 확인하지 않으면 안 채워진 변수를 쓰게 된다
vscanf 무리vscanf · vfscanf · vsscanfva_list 를 받는 판

표 105.57 — <stdio.h> 요람 — 읽고 쓰기

이름꼴인자와 반환하는 일 · ★함정
fseek·ftellint fseek(FILE *stream, long offset, int whence) · long ftell(FILE *stream)자리를 옮기고 묻는다whence 는 SEEK_SET·SEEK_CUR·SEEK_END. ★ 텍스트 모드에서는 ftell 이 준 값으로만 되돌아갈 수 있다. ★ long 이라 큰 파일에서 모자란다
fgetpos·fsetposint fgetpos(FILE * restrict stream, fpos_t * restrict pos) · int fsetpos(FILE *stream, const fpos_t *pos)fpos_t 로 자리를 담고 되돌린다★ 큰 파일에는 이쪽을 쓴다 — long 의 한계가 없다
rewindvoid rewind(FILE *stream)처음으로 되돌리고 오류 표시를 지운다★ 반환값이 없다 — 실패를 알 수 없다
feof·ferror·clearerrint feof(FILE *stream) 등끝 표시 · 오류 표시를 묻고 지운다★★ feof 는 읽기가 실패한 뒤에야 참이 된다 — while (!feof(f)) 는 마지막 줄을 두 번 처리하는 흔한 사고다
perrorvoid perror(const char *s)errno 에 맞는 설명을 표준 오류로s 를 앞에 붙여 찍는다

표 105.58 — <stdio.h> 요람 — 자리와 상태

무리이름비고
스트림stdin · stdout · stderr★ stderr 는 버퍼링되지 않거나 줄 단위다
매크로EOF · BUFSIZ · FILENAME_MAX · FOPEN_MAX · TMP_MAX · SEEK_SET · SEEK_CUR · SEEK_END · NULL · _IOFBF · _IOLBF · _IONBF★ EOF 는 음수라는 것만 보장된다
타입FILE · fpos_t · size_t★ FILE 은 속을 들여다보지 않는다
부속서 Kfopen_s · freopen_s · tmpfile_s · tmpnam_s · gets_s · printf_s · fprintf_s · sprintf_s · snprintf_s · vprintf_s · vfprintf_s · vsprintf_s · vsnprintf_s · scanf_s · fscanf_s · sscanf_s · vscanf_s · vfscanf_s · vsscanf_s · TMP_MAX_S · errno_t · rsize_t★K gets 는 C11 에서 없어졌고 gets_s 가 그 자리를 대신한다(83장)

표 105.59 — 매크로 · 타입 · 부속서 K

<math.h> — 수학 함수#

본문: 78장(정독), 52장(근사의 수학), 7장(수의 표현).

이 헤더의 이름이 백예순을 넘지만, 외울 것은 그렇게 많지 않다. 규칙이 셋이다.

규칙뜻보기
접미 f·lfloat 판 · long double 판sqrtf · sqrtl
<tgmath.h>접미를 골라 주는 매크로sqrt(x) 한 이름으로 셋 다
십진 이름d32·d64·d128 이 들어간 것은 십진 부동소수점용선택 사항이라 없는 구현이 많다

표 105.60 — 이름을 읽는 규칙

이름무엇을 묻는가★함정
fpclassifyFP_NAN·FP_INFINITE·FP_ZERO·FP_SUBNORMAL·FP_NORMAL 중 하나
isnan·isinf·isfinite·isnormalNaN · 무한 · 유한 · 정규★ x != x 대신 isnan 을 쓴다
issubnormal·iszero·iscanonical·issignaling비정규 · 0 · 정규 표현 · 시그널링 NaNC23
signbit부호 비트가 서 있는가★ -0.0 에서 참이다 — x < 0 과 다르다
isgreater·isgreaterequal·isless·islessequal·islessgreater·isunorderedNaN 이 있어도 예외를 내지 않고 견준다★ 보통 >·< 는 시그널링 NaN 에서 예외를 낸다
iseqsig같은가를 묻되 NaN 이면 예외를 낸다C23
totalorder·totalordermagIEEE 754 의 전순서로 견준다C23. -0.0 과 +0.0 도 가른다

표 105.61 — 값이 무엇인지 묻기 — 매크로다

무리이름비고 · ★함정
삼각sin · cos · tan · asin · acos · atan · atan2★ atan2 는 사분면을 가려 준다 — 인자 순서는 (y, x) 다
𝜋 배수 삼각sinpi · cospi · tanpi · asinpi · acospi · atanpi · atan2piC23. 인자를 𝜋 의 배수로 본다 — 정밀도가 낫다
쌍곡sinh · cosh · tanh · asinh · acosh · atanh
지수exp · exp2 · exp10 · expm1 · exp2m1 · exp10m1★ expm1 은 𝑒𝑥−1 을 작은 x 에서 정확히 셈한다
로그log · log2 · log10 · log1p · logp1 · log2p1 · log10p1 · logb · ilogb · llogb★ log1p 는 log(1+𝑥) 의 정확한 판
거듭제곱pow · pown · powr · rootn · compoundn · sqrt · rsqrt · cbrt · hypot★ hypot 는 중간 넘침 없이 빗변을 잰다
오차와 감마erf · erfc · lgamma · tgamma★ lgamma 는 로그 감마다

표 105.62 — 초등 함수

무리이름비고 · ★함정
반올림ceil · floor · trunc · round · roundeven · nearbyint · rint · lrint · llrint · lround · llround★ rint 는 현재 반올림 방향을 따르고 예외를 낼 수 있다. nearbyint 는 안 낸다
정수로fromfp · ufromfp · fromfpx · ufromfpxC23. 방향과 폭을 골라 정수로 만든다
나머지fmod · remainder · remquo★ fmod 와 remainder 는 부호가 다르다
자리 옮기기ldexp · scalbn · scalbln · frexp · modf★ frexp·modf 는 두 번째 인자로 나머지 몫을 돌려준다
최대·최소fmax · fmin · fdim · fmaximum · fminimum · fmaximum_num · fminimum_num · fmaximum_mag · fminimum_mag · fmaximum_mag_num · fminimum_mag_num★ fmax 는 NaN 을 무시하고, C23 의 fmaximum 은 NaN 을 전파한다 — 다른 함수다
부호와 이웃fabs · copysign · nextafter · nexttoward · nextup · nextdown★ nextafter 는 표현 가능한 바로 다음 값을 준다
NaN 만들기nan · getpayload · setpayload · setpayloadsig · canonicalizeC23 이 NaN 의 짐(payload)을 다룰 길을 열었다

표 105.63 — 반올림 · 나머지 · 자리 옮기기

무리이름비고
곱하고 더하기fma★ 곱셈과 덧셈을 한 번만 반올림한다 — 정밀도의 핵심
float 결과fadd · fsub · fmul · fdiv · fsqrt · ffmaC23. 넓은 타입에서 셈하고 float 로 한 번만 반올림
double 결과daddl · dsubl · dmull · ddivl · dsqrtl · dfmallong double 로 셈해 double 로
십진 판d32addd64 · d32addd128 · d64addd128 · d32subd64 · d32subd128 · d64subd128 · d32muld64 · d32muld128 · d64muld128 · d32divd64 · d32divd128 · d64divd128 · d32sqrtd64 · d32sqrtd128 · d64sqrtd128 · d32fmad64 · d32fmad128 · d64fmad128십진 부동소수점의 같은 것들

표 105.64 — 정밀도를 지키는 셈 — 넓은 타입으로 한 번에

무리이름비고
자릿수quantized32 · quantized64 · quantized128 · quantumd32 · quantumd64 · quantumd128 · samequantumd32 · samequantumd64 · samequantumd128 · llquantexpd32 · llquantexpd64 · llquantexpd128십진은 같은 값이라도 자릿수가 다를 수 있다 — 그 자릿수를 다루는 함수들
부호화encodedecd32 · encodedecd64 · encodedecd128 · decodedecd32 · decodedecd64 · decodedecd128 · encodebind32 · encodebind64 · encodebind128 · decodebind32 · decodebind64 · decodebind128십진 표현과 이진 표현 사이를 오간다

표 105.65 — 십진 부동소수점 전용

★ 오류를 알리는 방법이 둘이다 — errno(EDOM·ERANGE)와 부동소수점 예외. 어느 쪽을 쓰는지는 math_errhandling 이 말해 준다(MATH_ERRNO·MATH_ERREXCEPT). HUGE_VAL·HUGE_VALF·HUGE_VALL 은 넘쳤을 때 돌려주는 값이고, INFINITY·NAN 은 <float.h> 에서 온다.