부록 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*)#
| 이름 | 꼴 | 인자와 반환 | 하는 일 · ★함정 |
|---|---|---|---|
memcpy | void *memcpy(void *restrict s1, const void *restrict s2, size_t n) | s1 목적지, s2 출처, n 바이트 수. 반환은 s1 | n 바이트를 옮긴다. ★ 두 영역이 겹치면 계약 밖이다 — restrict 가 그 약속이다. 겹치면 memmove |
memmove | void *memmove(void *s1, const void *s2, size_t n) | 같음. 반환은 s1 | 겹쳐도 옳게 옮긴다. 함정 없음 — 겹칠지 모르면 이것을 쓴다 |
memcmp | int memcmp(const void *s1, const void *s2, size_t n) | 두 영역과 길이. 반환은 음수·0·양수 | 바이트를 사전순으로 견준다. ★ 구조체를 통째로 견주면 안 된다 — 패딩이 값에 낀다(48장) |
memchr | QVoid *memchr(QVoid *s, int c, size_t n) | c 는 unsigned char 로 변환되어 찾는 값. 못 찾으면 널 | 앞에서부터 n 바이트 안에서 찾는다. 문자열과 달리 NUL 에서 멈추지 않는다 |
memset | void *memset(void *s, int c, size_t n) | c 는 unsigned char 로 변환. 반환은 s | n 바이트를 같은 값으로 채운다. ★ 포인터를 널로 만드는 수단이 아니다 — 비트 0 과 널 표현은 별개다(37장) |
memset_explicit | void *memset_explicit(void *s, int c, size_t n) | memset 과 같다 | C23. 최적화가 지우지 못하는 지우기 — 비밀번호를 지운 뒤 곧 해제할 때 쓴다 |
memccpy | void *memccpy(void *restrict s1, const void *restrict s2, int c, size_t n) | c 를 만나면 거기까지 복사하고 그 다음 자리를 돌려준다. 못 만나면 널 | C23 에 들어왔다(오래된 POSIX 함수). 「구분자까지 복사」를 한 번에 한다 |
memcpy_s | errno_t memcpy_s(void *restrict s1, rsize_t s1max, const void *restrict s2, rsize_t n) | 목적지 크기 s1max 를 함께 받는다. 성공은 0 | ★K 부속서 K. 넘치면 목적지를 0 으로 채우고 오류를 낸다. 널리 구현되지 않았다(83장) |
memmove_s | errno_t memmove_s(void *s1, rsize_t s1max, const void *s2, rsize_t n) | 같음 | ★K 같은 사정 |
memset_s | errno_t memset_s(void *s, rsize_t smax, int c, rsize_t n) | 같음 | ★K 같은 사정. memset_explicit 이 C23 의 답이다 |
표 105.2 — <string.h> 요람 — 기억 블록 (mem*)
복사와 이어 붙이기 (strcpy·strcat 계열)#
| 이름 | 꼴 | 인자와 반환 | 하는 일 · ★함정 |
|---|---|---|---|
strcpy | char *strcpy(char *restrict s1, const char *restrict s2) | 반환은 s1 | NUL 까지 통째로 옮긴다. ★ 목적지 크기를 모른다 — 넘침의 고전 |
strncpy | char *strncpy(char *restrict s1, const char *restrict s2, size_t n) | n 은 쓸 바이트 수이지 목적지 크기가 아니다 | ★★ 이름과 달리 안전 함수가 아니다. 딱 맞으면 NUL 을 붙이지 않고, 짧으면 남은 자리를 전부 0 으로 채운다(느리다). 70장 |
strcat | char *strcat(char *restrict s1, const char *restrict s2) | 반환은 s1 | 뒤에 이어 붙인다. ★ 목적지 크기를 모르고, 매번 끝을 다시 찾아 이어 붙이므로 반복하면 제곱이 된다 |
strncat | char *strncat(char *restrict s1, const char *restrict s2, size_t n) | n 은 더 붙일 최대 바이트 수. NUL 은 그 위에 하나 더 쓴다 | ★ 그래서 필요한 그릇은 n + 1 이다 — strncpy 와 셈법이 다르다 |
strdup | char *strdup(const char *s) | 실패하면 널 | C23 에 들어왔다. free 해야 한다 — 소유가 넘어온다(94장) |
strndup | char *strndup(const char *s, size_t n) | 최대 n 바이트까지. 실패하면 널 | C23. 끝을 못 찾아도 n 에서 멈춘다 |
strcpy_s | errno_t strcpy_s(char *restrict s1, rsize_t s1max, const char *restrict s2) | 성공은 0 | ★K 83장 |
strncpy_s | errno_t strncpy_s(char *restrict s1, rsize_t s1max, const char *restrict s2, rsize_t n) | 성공은 0 | ★K 83장 |
strcat_s | errno_t strcat_s(char *restrict s1, rsize_t s1max, const char *restrict s2) | 성공은 0 | ★K 83장 |
strncat_s | errno_t strncat_s(char *restrict s1, rsize_t s1max, const char *restrict s2, rsize_t n) | 성공은 0 | ★K 83장 |
표 105.3 — <string.h> 요람
견주기와 길이#
| 이름 | 꼴 | 인자와 반환 | 하는 일 · ★함정 |
|---|---|---|---|
strlen | size_t strlen(const char *s) | NUL 을 세지 않은 바이트 수 | ★ 글자 수가 아니라 바이트 수다(43·77장). 그리고 O(n) 이므로 루프 조건에 두지 않는다(42장) |
strcmp | int strcmp(const char *s1, const char *s2) | 음수·0·양수 | ★ 「사전순」이 아니라 unsigned char 값 순이다. 사람이 쓰는 정렬은 strcoll |
strncmp | int strncmp(const char *s1, const char *s2, size_t n) | 같음 | 앞 n 바이트만 견준다. 접두어 검사에 쓴다 |
strcoll | int strcoll(const char *s1, const char *s2) | 같음 | 로케일이 정한 순서로 견준다(74장). 느리다 — 여러 번 견줄 것이면 strxfrm |
strxfrm | size_t strxfrm(char *restrict s1, const char *restrict s2, size_t n) | 필요한 길이를 돌려준다(NUL 제외). n 이상이면 s1 의 내용은 미정 | 로케일 비교를 «키»로 굳힌다. 키끼리는 strcmp 로 견줄 수 있다 |
strnlen_s | size_t strnlen_s(const char *s, size_t maxsize) | 최대 maxsize. s 가 널이면 0 | ★K 그러나 이것만은 널리 쓸 만하다 — 끝이 없을지 모르는 버퍼의 길이를 잰다 |
표 105.4 — <string.h> 요람 — 견주기와 길이
찾기와 쪼개기#
| 이름 | 꼴 | 인자와 반환 | 하는 일 · ★함정 |
|---|---|---|---|
strchr | QChar *strchr(QChar *s, int c) | 못 찾으면 널. c 가 '\0' 이면 끝의 NUL 을 가리킨다 | 앞에서부터 한 바이트를 찾는다. ★ 멀티바이트 인코딩에서 글자 속을 짚을 수 있다(77장) |
strrchr | QChar *strrchr(QChar *s, int c) | 같음 | 뒤에서부터 찾는다. 경로에서 마지막 구분자를 찾는 데 쓰는데 — ★ 위와 같은 위험 |
strstr | QChar *strstr(QChar *s1, const char *s2) | 못 찾으면 널. s2 가 빈 문자열이면 s1 | 부분 문자열을 찾는다. 표준은 알고리즘을 정하지 않는다 — 최악 O(nm) 일 수 있다 |
strspn | size_t strspn(const char *s1, const char *s2) | 길이를 돌려준다 | s2 의 글자들로만 이루어진 앞부분의 길이. 건너뛰기에 쓴다 |
strcspn | size_t strcspn(const char *s1, const char *s2) | 길이를 돌려준다 | 반대 — s2 의 글자를 만나기 전까지의 길이 |
strpbrk | QChar *strpbrk(QChar *s1, const char *s2) | 못 찾으면 널 | s2 의 글자 아무거나 처음 나오는 자리 |
strtok | char *strtok(char *restrict s1, const char *restrict s2) | 첫 호출에 문자열, 다음부터 널. 더 없으면 널 | ★★ 원본을 부순다(구분자 자리에 NUL 을 쓴다). 그리고 상태를 함수 안에 숨긴다 — 재진입 불가, 스레드 안전하지 않다. 70장 |
strtok_s | char *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> 요람 — 찾기와 쪼개기
오류 문자열#
| 이름 | 꼴 | 인자와 반환 | 하는 일 · ★함정 |
|---|---|---|---|
strerror | char *strerror(int errnum) | 오류 번호에 대응하는 문자열 | ★ 정적 버퍼를 돌려줄 수 있다 — 다음 호출이 덮어쓸 수 있고 스레드 안전이 보장되지 않는다(80장) |
strerrorlen_s | size_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 | 제어 문자 | 화면에 그려지지 않는 것 |
isdigit | 0~9 | ★ 로케일과 무관하다 — 표준이 이 열 글자로 못박았다 |
isgraph | 공백을 뺀, 그려지는 글자 | isprint 에서 공백을 뺀 것 |
islower | 소문자 | 로케일이 정한다 |
isprint | 공백을 포함해 그려지는 글자 | |
ispunct | 글자도 숫자도 공백도 아닌, 그려지는 글자 | |
isspace | 공백류 — 공백·\\n·\\t·\\v·\\f·\\r | 입력을 건너뛸 때 쓴다 |
isupper | 대문자 | 로케일이 정한다 |
isxdigit | 16진 숫자 | ★ 로케일과 무관하다 |
tolower | 대문자면 대응하는 소문자, 아니면 그대로 | ★ 한 글자 대 한 글자다. 터키어 I, 독일어 ß 처럼 그 가정이 깨지는 언어가 있다(77장) |
toupper | 소문자면 대응하는 대문자, 아니면 그대로 | ★ 같음 |
표 105.9 — <ctype.h> 요람
<stdckdint.h> — 넘침을 묻는 산술#
본문: 86장(정독), 28장(정수의 유한함), 54장(정의되지 않은 동작).
C23 이 들인 헤더다. 셋 다 타입 제네릭 매크로이지 함수가 아니다 — 그래서 꼴에 type1·type2 라고 적혀 있다.
| 이름 | 꼴 | 인자와 반환 | 하는 일 · ★함정 |
|---|---|---|---|
ckd_add | bool ckd_add(type1 *result, type2 a, type3 b) | result 에 결과를 쓴다. 넘쳤으면 참, 아니면 거짓을 돌려준다 | ★ 반환값의 방향에 주의 — 참이 「성공」이 아니라 「넘쳤다」이다 |
ckd_sub | bool ckd_sub(type1 *result, type2 a, type3 b) | 같음 | 뺄셈. 부호 없는 타입에서 0 아래로 내려가는 것도 잡는다 |
ckd_mul | bool 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장(정의되지 않은 동작).
헤더 전체가 매크로 하나와 그 매크로를 끄는 이름 하나다. 함수가 아니라 매크로인 것이 요점이다 — 그래야 실패한 자리의 파일 이름과 줄 번호를 찍을 수 있다.
| 이름 | 꼴 | 인자와 반환 | 하는 일 · ★함정 |
|---|---|---|---|
assert | void assert(scalar expression) | 수식 하나를 받는다. 돌려주는 값은 없다 | 거짓이면 진단을 찍고 abort 한다. ★ NDEBUG 가 정의되면 통째로 사라진다 — 그래서 부수효과를 안에 넣으면 안 된다(assert(f()) 는 배포판에서 f 를 부르지 않는다) |
NDEBUG | 매크로 | 값이 아니라 정의 여부만 본다 | 정의되어 있으면 assert 가 아무 일도 하지 않는다. ★ <assert.h> 를 포함하는 그 시점의 정의 여부를 본다 — 포함할 때마다 다시 판정된다 |
표 105.12 — <assert.h> 요람
<errno.h> — 실패의 까닭을 남기는 자리#
본문: 80장(정독), 53장(오류를 값으로).
여기에는 함수가 없다. 정수 하나(errno)와 표준이 못박은 값 셋, 그리고 부속서 K 의 타입 하나뿐이다. ★ 표준이 정한 값은 셋뿐이고, ENOENT 처럼 흔히 쓰는 이름들은 POSIX 가 더한 것이다.
| 이름 | 꼴 | 인자와 반환 | 하는 일 · ★함정 |
|---|---|---|---|
errno | int 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 는 돌아오지 않는다.
| 이름 | 꼴 | 인자와 반환 | 하는 일 · ★함정 |
|---|---|---|---|
setjmp | int setjmp(jmp_buf env) | 현재 자리를 env 에 적는다. 직접 부른 그때는 0, longjmp 로 돌아왔을 때는 그것이 넘긴 값 | ★ 매크로다. 그리고 쓸 수 있는 자리가 표준으로 좁게 정해져 있다 — 조건문의 전체 수식이거나 그 자체가 한 문장인 자리 같은 것. x = setjmp(env); 는 허용되지 않는다 |
longjmp | void 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_start | void va_start(va_list ap, ...) | ap 를 쓸 수 있게 준비한다 | ★ C23 부터 마지막 이름 있는 매개변수를 적지 않아도 된다. 그래서 이름 있는 매개변수가 하나도 없는 가변 인자 함수도 쓸 수 있게 되었다 |
va_arg | type va_arg(va_list ap, type) | 다음 인자를 type 으로 꺼낸다 | ★ 실제로 넘어온 타입과 다르면 정의되지 않은 동작이다. 기본 인자 승격을 거친 뒤의 타입으로 적어야 한다 — float 은 double 로, char·short 는 int 로 온다 |
va_copy | void va_copy(va_list dest, va_list src) | src 의 현재 상태를 dest 에 복사한다 | C99. 같은 목록을 두 번 훑어야 할 때 쓴다(vsnprintf 로 길이를 재고 다시 찍는 관용구). ★ 복사한 것도 각각 va_end 해야 한다 |
va_end | void 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_t | size_t 와 같다 | 부속서 K 가 쓰는 이름 | ★K 선택 사항이므로 없을 수 있다(83장) |
NULL | 널 포인터 상수로 펼쳐지는 매크로 | 「가리키는 데가 없음」 | ★ 0 이라는 보장은 없다 — 표현이 0 이 아닌 기계가 있었다(37장) |
offsetof | size_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 이기 때문이다.
| 타입 | 최소 | 최대 | 폭 · ★함정 |
|---|---|---|---|
char | CHAR_MIN | CHAR_MAX | CHAR_WIDTH. ★ 부호가 있는지 없는지 구현이 정한다 — 그래서 CHAR_MIN 이 0 일 수도 있다 |
signed char | SCHAR_MIN | SCHAR_MAX | SCHAR_WIDTH |
unsigned char | 0 | UCHAR_MAX | UCHAR_WIDTH |
short | SHRT_MIN | SHRT_MAX | SHRT_WIDTH |
unsigned short | 0 | USHRT_MAX | USHRT_WIDTH |
int | INT_MIN | INT_MAX | INT_WIDTH |
unsigned int | 0 | UINT_MAX | UINT_WIDTH |
long | LONG_MIN | LONG_MAX | LONG_WIDTH. ★ 리눅스 64비트에서 8바이트, 윈도우에서 4바이트다 |
unsigned long | 0 | ULONG_MAX | ULONG_WIDTH |
long long | LLONG_MIN | LLONG_MAX | LLONG_WIDTH |
unsigned long long | 0 | ULLONG_MAX | ULLONG_WIDTH |
bool | 0 | BOOL_MAX | BOOL_WIDTH. C23 이 들였다 — 각각 1 이다 |
표 105.20 — <limits.h> 요람
| 이름 | 무엇 | ★함정 |
|---|---|---|
CHAR_BIT | 한 바이트의 비트 수 | ★ 8 이라는 보장이 표준에는 없다. 다만 8 이 아닌 기계는 오늘날 사실상 없다(6장) |
MB_LEN_MAX | 어느 로케일에서든 멀티바이트 한 글자의 최대 바이트 수 | MB_CUR_MAX(로케일에 따라 달라지는 현재 값)와 다르다(75장) |
BITINT_MAXWIDTH | _BitInt(N) 이 허용하는 최대 N | C23. 최소 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_DIG | double 은 보통 15 |
_DECIMAL_DIG | 그 타입을 십진으로 적었다가 되읽어도 같아지는 자릿수 | FLT_DECIMAL_DIG·DBL_DECIMAL_DIG·LDBL_DECIMAL_DIG | ★ _DIG 와 다르다 — 이쪽이 크다(double 은 17) |
_EPSILON | 1 과, 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_EXP | FLT_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_SUBNORM | 1 · 0 · (모름) 중 하나 |
_IS_IEC_60559 | IEC 60559(IEEE 754)를 따르는가 | FLT_IS_IEC_60559·DBL_IS_IEC_60559·LDBL_IS_IEC_60559 | C23 이 들였다 |
_SNAN | 시그널링 NaN | FLT_SNAN·DBL_SNAN·LDBL_SNAN | C23. 있으면 그 타입의 시그널링 NaN 이다 |
표 105.22 — 접미사가 뜻하는 것 — 세 접두에 모두 붙는다
| 이름 | 무엇 | ★함정 |
|---|---|---|
FLT_RADIX | 지수의 밑 | ★ 이름에 FLT_ 가 붙었지만 세 타입 모두에 해당한다. 오늘날 사실상 2 다 |
FLT_ROUNDS | 지금의 반올림 방향 | ★ 상수가 아니라 실행 중에 바뀔 수 있다(fesetround, 78장) |
FLT_EVAL_METHOD | 중간 계산을 어떤 폭으로 하는가 | ★ float 끼리의 계산이 double 로 이뤄질 수 있다는 뜻이다 |
DECIMAL_DIG | 가장 넓은 타입의 _DECIMAL_DIG | C23 에서 밀려난 이름이다 — LDBL_DECIMAL_DIG 를 쓴다 |
CR_DECIMAL_DIG | 정확히 반올림되는 십진 변환의 자릿수 | C23 |
INFINITY | float 무한대 | 있는 경우에만 정의된다 |
NAN | float 조용한 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_t | INTN_MIN·INTN_MAX·INTN_WIDTH·UINTN_MAX·UINTN_WIDTH | N 에 8·16·32·64 가 들어간다 |
| 적어도 N 비트 | int_leastN_t·uint_leastN_t | INT_LEASTN_MIN·INT_LEASTN_MAX·INT_LEASTN_WIDTH·UINT_LEASTN_MAX·UINT_LEASTN_WIDTH | |
| 빠른 것 | int_fastN_t·uint_fastN_t | INT_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_WIDTH | size_t | 할당 크기를 셈할 때의 상한이다 |
PTRDIFF_MIN·PTRDIFF_MAX | ptrdiff_t | |
WCHAR_MIN·WCHAR_MAX·WCHAR_WIDTH | wchar_t | (75장) |
WINT_MIN·WINT_MAX·WINT_WIDTH | wint_t | WEOF 를 담을 수 있어야 한다 |
SIG_ATOMIC_MIN·SIG_ATOMIC_MAX·SIG_ATOMIC_WIDTH | sig_atomic_t | 신호 처리기가 만질 수 있는 유일한 타입(80장) |
RSIZE_MAX | 부속서 K 가 「너무 크다」고 볼 경계 | ★K 선택 사항(83장) |
표 105.26 — 다른 헤더의 타입에 딸린 한계
<inttypes.h> — 그 정수를 찍고 읽는 법#
본문: 69장(형식 문자열), 71장(문자열에서 수로), 6장(정수의 표현).
<stdint.h> 를 포함하면서, printf 에 무엇을 적을 것인가 를 함께 준다. int64_t 를 찍을 때 %ld 라 적으면 어느 기계에서는 맞고 어느 기계에서는 틀린다 — 그 문제를 없애는 헤더다.
| 이름 | 꼴 | 인자와 반환 | 하는 일 · ★함정 |
|---|---|---|---|
imaxabs | intmax_t imaxabs(intmax_t j) | 절댓값 | ★ 가장 작은 음수의 절댓값은 표현할 수 없다 — 정의되지 않은 동작이다 |
imaxdiv | imaxdiv_t imaxdiv(intmax_t numer, intmax_t denom) | 몫과 나머지를 함께 | imaxdiv_t 는 quot 와 rem 을 담은 구조체다 |
strtoimax | intmax_t strtoimax(const char * restrict nptr, char ** restrict endptr, int base) | 읽은 값. 실패는 endptr 과 errno 로 | ★ 규약은 strtol 과 같다 — 부르기 전에 errno 를 0 으로(71장) |
strtoumax | uintmax_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·PRIBFASTN | least·fast 타입을 찍는 형식 | PRIdLEAST16·PRIuFAST32 |
PRIBMAX·PRIBPTR | intmax_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_ones | 0 인 비트 수 · 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장(문자 집합).
함수는 둘뿐이지만 그 둘이 프로그램 전체의 동작을 바꾼다. 로케일은 전역 상태다 — 이 헤더의 함정은 거의 다 거기서 나온다.
| 이름 | 꼴 | 인자와 반환 | 하는 일 · ★함정 |
|---|---|---|---|
setlocale | char *setlocale(int category, const char *locale) | 갈래와 이름을 받아 지금의 로케일 이름을 돌려준다. 실패하면 널 | locale 이 널이면 바꾸지 않고 묻기만 한다. ★ 돌려준 문자열은 다음 호출이 덮을 수 있다 — 쓰려면 복사한다. ★ 시작할 때는 언제나 "C" 다. ★ 여러 스레드에서 부르는 것은 안전하지 않다 |
localeconv | struct 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 | 제어 문자 | |
iswdigit | 0~9 | ★ 로케일과 무관하다 |
iswgraph | 공백을 뺀, 그려지는 글자 | |
iswlower | 소문자 | |
iswprint | 그려지는 글자 | |
iswpunct | 문장 부호류 | |
iswspace | 공백류 | |
iswupper | 대문자 | |
iswxdigit | 16진 숫자 | ★ 로케일과 무관하다 |
towlower·towupper | 대응하는 소문자·대문자로, 없으면 그대로 | ★ 한 글자 대 한 글자라는 한계가 그대로 있다 |
wctype | 갈래 이름("alpha" 같은 것)을 wctype_t 로 바꾼다 | 없는 이름이면 0 |
iswctype | wctype 이 준 갈래에 드는가 | 로케일이 정의한 갈래도 쓸 수 있다 |
wctrans | 바꿈 이름("tolower" 같은 것)을 wctrans_t 로 | 없는 이름이면 0 |
towctrans | wctrans 가 준 바꿈을 적용한다 | |
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장)에 대한 표준의 답이다. 폭을 이름에 박았고, 변환 함수는 상태를 들고 한 글자씩 오간다.
| 이름 | 꼴 | 인자와 반환 | 하는 일 · ★함정 |
|---|---|---|---|
mbrtoc8 | size_t mbrtoc8(char8_t * restrict pc8, const char * restrict s, size_t n, mbstate_t * restrict ps) | 읽은 바이트 수. 특별한 값으로 상황을 알린다 | C23. ★ 한 번에 UTF-8 한 바이트를 내놓을 수 있어 여러 번 불러야 한다 |
c8rtomb | size_t c8rtomb(char *s, char8_t c8, mbstate_t *ps) | 쓴 바이트 수 | C23. 반대 방향 |
mbrtoc16 | size_t mbrtoc16(char16_t * restrict pc16, const char * restrict s, size_t n, mbstate_t * restrict ps) | 같음 | ★ 서로게이트 쌍 때문에 한 글자가 두 번에 나올 수 있다 |
c16rtomb | size_t c16rtomb(char *s, char16_t c16, mbstate_t *ps) | 쓴 바이트 수 | 반대 방향 |
mbrtoc32 | size_t mbrtoc32(char32_t * restrict pc32, const char * restrict s, size_t n, mbstate_t * restrict ps) | 같음 | 코드 포인트 하나가 한 번에 나온다 — 다루기 가장 쉽다 |
c32rtomb | size_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장(끼어듦).
함수는 둘뿐인데, 이 헤더의 무게는 함수가 아니라 처리기 안에서 무엇을 해도 되는가 라는 계약에 있다. 표준이 허락하는 것이 놀랄 만큼 적다.
| 이름 | 꼴 | 인자와 반환 | 하는 일 · ★함정 |
|---|---|---|---|
signal | void (*signal(int sig, void (*func)(int)))(int) | 신호에 붙일 처리기를 정한다. 이전 처리기를 돌려주고, 실패하면 SIG_ERR | ★ 꼴이 어려워 보이지만 「int 를 받는 함수 포인터를 받아 같은 것을 돌려준다」가 전부다. ★ 처리기가 불린 뒤 기본으로 되돌아갈 수 있다 — 표준이 어느 쪽인지 정하지 않았다 |
raise | int raise(int sig) | 스스로에게 신호를 보낸다. 성공하면 0 | abort 가 SIGABRT 로 하는 일이 이것이다 |
sig_atomic_t | 정수 타입 | 처리기와 본체가 나눠 쓰는 유일한 타입 | ★ volatile sig_atomic_t 로 적는다. 그것 말고는 처리기가 만질 수 있는 것이 거의 없다 |
SIG_DFL·SIG_IGN | signal 에 넘기는 값 | 기본 동작 · 무시 | |
SIG_ERR | signal 의 실패 반환값 | ★ 널이 아니다 — 따로 비교해야 한다 | |
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_UNDERFLOW | 0 으로 나눔 · 정확하지 않음 · 잘못된 연산 · 넘침 · 아래넘침 |
| 전부 | 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> 를 포함하고, 그 함수들을 같은 이름의 타입 제네릭 매크로로 덮는다.
| 인자의 타입 | 고르는 것 | 보기 |
|---|---|---|
float | f 접미가 붙은 판 | sqrt(x) → sqrtf |
double·정수 | 접미 없는 판 | sqrt(x) → sqrt — ★ 정수는 double 로 올라간다 |
long double | l 접미가 붙은 판 | 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_explicit | key 자리에 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 · wcsxfrm | wcscoll·wcsxfrm 은 로케일 순서를 따른다(74장) |
| 찾기 | wcschr · wcsrchr · wcsstr · wcspbrk · wcsspn · wcscspn · wmemchr | |
| 자르기 | wcstok | ★ 상태를 밖에서 들고 있어야 한다 — strtok 와 달리 셋째 인자가 있다 |
| 채우기 | wmemset | |
| 시각 서식 | wcsftime | strftime 의 와이드 판 |
표 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 | |
| 부속서 K | wcscpy_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).
| 이름 | 꼴 | 인자와 반환 | 하는 일 · ★함정 |
|---|---|---|---|
time | time_t time(time_t *timer) | 지금의 달력 시각. 알 수 없으면 (time_t)-1 | ★ time_t 의 뜻과 정밀도를 표준이 정하지 않았다 — 유닉스 초라는 보장이 없다 |
difftime | double difftime(time_t time1, time_t time0) | 두 시각의 차를 초로 | ★ 빼기로 계산하면 안 된다 — time_t 가 초라는 보장이 없기 때문이다 |
clock | clock_t clock(void) | 프로세서 시간. 알 수 없으면 (clock_t)-1 | ★ 벽시계가 아니다. 초로 바꾸려면 CLOCKS_PER_SEC 으로 나눈다. ★ 감아 돌 수 있다 |
mktime | time_t mktime(struct tm *timeptr) | 쪼갠 시각을 달력 시각으로 | ★ 인자를 고쳐 준다 — 범위를 벗어난 값을 정규화하고 tm_wday·tm_yday 를 채운다. 그래서 「석 달 뒤」를 셀 때 쓴다 |
localtime·gmtime | struct tm *localtime(const time_t *timer) · struct tm *gmtime(const time_t *timer) | 달력 시각을 지역 시각 · UTC 로 쪼갠다 | ★ 정적 저장소를 돌려준다 — 다음 호출이 덮고, 스레드 안전이 아니다 |
localtime_r·gmtime_r | struct 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장) |
timegm | time_t timegm(struct tm *timeptr) | 쪼갠 시각을 UTC 로 보고 달력 시각으로 | C23. mktime 의 UTC 짝이다 |
asctime·ctime | char *asctime(const struct tm *timeptr) · char *ctime(const time_t *timer) | 정해진 꼴의 문자열 | ★ 쓰지 않는 편이 낫다 — 정적 버퍼를 돌려주고, 꼴이 고정이라 로케일도 못 따른다 |
asctime_s·ctime_s | 부속서 K 판 | 그릇과 크기를 받는다 | ★K |
strftime | size_t strftime(char * restrict s, size_t maxsize, const char * restrict format, const struct tm * restrict timeptr) | 쓴 글자 수. 자리가 모자라면 0 | ★ 0 이 실패다 — 그런데 빈 문자열을 만들었을 때도 0 이다. 서식은 LC_TIME 을 따른다(74장) |
timespec_get | int timespec_get(struct timespec *ts, int base) | base 를 돌려준다. 실패하면 0 | C11. TIME_UTC 를 준다. C23 이 TIME_MONOTONIC·TIME_ACTIVE·TIME_THREAD_ACTIVE 를 더했다 |
timespec_getres | int 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_SEC | clock_t 를 초로 바꾸는 나눗수 | ★ 1,000,000 이라는 보장은 없다 |
TIME_UTC·TIME_MONOTONIC·TIME_ACTIVE·TIME_THREAD_ACTIVE | timespec_get 이 고르는 시계 | 뒤의 셋은 C23 |
NULL·size_t·errno_t·rsize_t | 다른 헤더에서 온 이름들 | 뒤의 둘은 ★K |
표 105.50 — 타입과 매크로
<stdlib.h> — 잡동사니 서랍#
본문: 71장(정독), 46장(동적 기억), 89장(할당기).
이름 그대로 서랍이다 — 기억 할당 · 프로그램 종료 · 문자열에서 수로 · 정렬과 찾기 · 난수 · 멀티바이트 변환이 한 헤더에 들어 있다. 무리별로 갈라 본다.
| 이름 | 꼴 | 인자와 반환 | 하는 일 · ★함정 |
|---|---|---|---|
malloc | void *malloc(size_t size) | 자리를 잡아 준다. 실패하면 널 | ★ 내용은 불확정이다. ★ size 가 0 이면 널이거나 유일한 자리인데, 어느 쪽인지 정해져 있지 않다 |
calloc | void *calloc(size_t nmemb, size_t size) | 같지만 0 으로 채운다 | ★ 곱셈의 넘침을 함수가 검사해 준다 — 그래서 배열에는 이쪽이 안전하다 |
realloc | void *realloc(void *ptr, size_t size) | 크기를 바꾼다. 실패하면 널 | ★★ 실패해도 원본은 살아 있다 — p = realloc(p, n) 은 실패하면 원본을 잃는다. ★ C23 에서 size 가 0 이면 정의되지 않은 동작이 되었다 |
aligned_alloc | void *aligned_alloc(size_t alignment, size_t size) | 정렬을 맞춰 잡아 준다 | ★ size 가 alignment 의 배수여야 한다 |
free | void free(void *ptr) | 돌려준다 | ★ 널을 넘기는 것은 안전하다. ★ 두 번 부르면 정의되지 않은 동작이다 |
free_sized·free_aligned_sized | void free_sized(void *ptr, size_t size) · void free_aligned_sized(void *ptr, size_t alignment, size_t size) | 크기를 함께 알려 주며 돌려준다 | C23. 할당기가 크기를 따로 안 적어 두어도 되게 한다 — ★ 틀린 크기를 주면 정의되지 않은 동작이다 |
memalignment | size_t memalignment(const void *p) | 그 주소가 만족하는 정렬 | C23 |
표 105.51 — <stdlib.h> 요람 — 기억 할당
| 이름 | 꼴 | 인자와 반환 | 하는 일 · ★함정 |
|---|---|---|---|
exit | void exit(int status) | 정상 종료. 돌아오지 않는다 | atexit 로 등록한 것을 부르고 스트림을 흘려보낸다. ★ 신호 처리기에서 부르면 안 된다 |
quick_exit | void quick_exit(int status) | 빠른 종료 | at_quick_exit 로 등록한 것만 부른다 — 스트림을 흘려보내지 않는다 |
_Exit | void _Exit(int status) | 즉시 종료 | ★ 등록한 것을 하나도 부르지 않는다. 신호 처리기에서 안전한 유일한 종료 |
abort | void abort(void) | 비정상 종료 | SIGABRT 를 낸다. ★ 스트림을 흘려보낸다는 보장이 없다 |
atexit·at_quick_exit | int atexit(void (*func)(void)) · int at_quick_exit(void (*func)(void)) | 끝날 때 부를 함수를 등록한다. 성공하면 0 | ★ 등록의 역순으로 불린다. 적어도 32 개는 등록할 수 있다 |
system | int system(const char *string) | 명령 처리기에 넘긴다 | ★ 널을 주면 처리기가 있는지 묻는 뜻이다. ★★ 사용자 입력을 그대로 넘기면 명령 주입이 된다 |
getenv | char *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·atof | int atoi(const char *nptr) 등 | 읽은 값 | ★★ 실패를 알릴 방법이 없다. 「0」과 「읽지 못했다」가 구별되지 않는다 — 새 코드에서는 쓰지 않는다 |
strtol·strtoul | long 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·strfroml | int strfromd(char * restrict s, size_t n, const char * restrict format, double fp) 등 | 반대 방향 — 수를 문자열로. snprintf 처럼 쓰려 한 길이를 돌려준다 | C23. ★ 로케일의 영향을 받지 않는다 — 그래서 파일 형식에 알맞다 |
strfromd32·strfromd64·strfromd128 | 십진 판 | 같음 | 선택 사항 |
표 105.53 — <stdlib.h> 요람 — 문자열과 수 사이
| 이름 | 꼴 | 인자와 반환 | 하는 일 · ★함정 |
|---|---|---|---|
abs·labs·llabs | int abs(int j) 등 | 절댓값 | ★ 가장 작은 음수는 표현할 수 없다 — 정의되지 않은 동작 |
div·ldiv·lldiv | div_t div(int numer, int denom) 등 | 몫과 나머지를 한 번에 | div_t·ldiv_t·lldiv_t 는 quot·rem 을 담는다 |
qsort | void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)) | 자리에서 정렬한다 | ★ 안정 정렬이 아니다. ★ 비교 함수가 일관되지 않으면 정의되지 않은 동작이다 — 뺄셈으로 비교하면 넘칠 수 있다 |
bsearch | void *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·srand | int rand(void) · void srand(unsigned int seed) | 0 부터 RAND_MAX 까지 | ★★ 암호에 쓰면 안 된다. ★ 품질이 구현마다 다르고, 스레드 안전이 아니다 |
mblen·mbtowc·wctomb | int 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장(스트림의 기원).
모든 것이 스트림이라는 하나의 그림 위에 있다. 열고 · 읽고 쓰고 · 자리를 옮기고 · 닫는다. 그리고 그 사이에 버퍼가 있다는 사실이 이 헤더 함정의 절반이다.
| 이름 | 꼴 | 인자와 반환 | 하는 일 · ★함정 |
|---|---|---|---|
fopen | FILE *fopen(const char * restrict filename, const char * restrict mode) | 스트림. 실패하면 널 | ★ "b" 를 붙이지 않으면 텍스트 모드라 줄 끝이 바뀔 수 있다. ★ 실패의 까닭은 errno 에 있다 |
freopen | FILE *freopen(const char * restrict filename, const char * restrict mode, FILE * restrict stream) | 이미 있는 스트림을 다른 파일로 다시 연다 | stdin·stdout 을 파일로 돌릴 때 쓴다 |
fclose | int fclose(FILE *stream) | 닫는다. 성공하면 0, 실패하면 EOF | ★★ 반환값을 봐야 한다 — 흘려보내다 실패한 쓰기가 여기서 처음 드러난다 |
fflush | int fflush(FILE *stream) | 버퍼를 흘려보낸다 | ★ 널을 주면 출력 스트림 전부를 흘려보낸다. ★ 입력 스트림에 부르는 것은 정의되지 않은 동작이다 |
setbuf·setvbuf | void 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·rename | int remove(const char *filename) · int rename(const char *old, const char *new) | 지우고 이름을 바꾼다. 성공하면 0 | ★ 열려 있는 파일에 대한 동작은 정해져 있지 않다. ★ rename 이 이미 있는 이름을 덮을지도 정해져 있지 않다 |
tmpfile·tmpnam | FILE *tmpfile(void) · char *tmpnam(char *s) | 임시 파일 · 임시 이름 | ★★ tmpnam 은 이름만 준다 — 그 이름을 쓰기까지의 틈이 보안 구멍이다. tmpfile 을 쓴다. ★ TMP_MAX 가 만들 수 있는 이름의 수 |
표 105.56 — <stdio.h> 요람 — 열고 닫고 버퍼
| 이름 | 꼴 | 인자와 반환 | 하는 일 · ★함정 |
|---|---|---|---|
fgetc·getc·getchar | int fgetc(FILE *stream) 등 | 한 글자. 끝이나 오류면 EOF | ★★ int 로 받아야 한다 — char 로 받으면 EOF 와 0xFF 를 구별할 수 없다. ★ getc 는 매크로일 수 있어 인자를 여러 번 평가할 수 있다 |
fputc·putc·putchar | int fputc(int c, FILE *stream) 등 | 쓴 글자, 실패하면 EOF | ★ putc 도 매크로일 수 있다 |
ungetc | int ungetc(int c, FILE *stream) | 한 글자를 되돌려 놓는다 | ★ 한 글자만 보장된다 |
fgets | char *fgets(char * restrict s, int n, FILE * restrict stream) | 읽은 문자열, 끝이면 널 | ★ 줄 끝 문자를 함께 담는다. 줄이 잘렸는지 알려면 그것이 있는지 본다 |
fputs·puts | int fputs(const char * restrict s, FILE * restrict stream) · int puts(const char *s) | 쓴다 | ★ puts 는 줄 끝을 붙이고 fputs 는 붙이지 않는다 |
fread·fwrite | size_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 · vsnprintf | va_list 를 받는 판 | (63장) |
scanf 무리 | scanf · fscanf · sscanf | 읽어 채운 항목의 수 | ★★ %s 에 폭을 적지 않으면 넘친다. ★ 반환값을 확인하지 않으면 안 채워진 변수를 쓰게 된다 |
vscanf 무리 | vscanf · vfscanf · vsscanf | va_list 를 받는 판 |
표 105.57 — <stdio.h> 요람 — 읽고 쓰기
| 이름 | 꼴 | 인자와 반환 | 하는 일 · ★함정 |
|---|---|---|---|
fseek·ftell | int fseek(FILE *stream, long offset, int whence) · long ftell(FILE *stream) | 자리를 옮기고 묻는다 | whence 는 SEEK_SET·SEEK_CUR·SEEK_END. ★ 텍스트 모드에서는 ftell 이 준 값으로만 되돌아갈 수 있다. ★ long 이라 큰 파일에서 모자란다 |
fgetpos·fsetpos | int fgetpos(FILE * restrict stream, fpos_t * restrict pos) · int fsetpos(FILE *stream, const fpos_t *pos) | fpos_t 로 자리를 담고 되돌린다 | ★ 큰 파일에는 이쪽을 쓴다 — long 의 한계가 없다 |
rewind | void rewind(FILE *stream) | 처음으로 되돌리고 오류 표시를 지운다 | ★ 반환값이 없다 — 실패를 알 수 없다 |
feof·ferror·clearerr | int feof(FILE *stream) 등 | 끝 표시 · 오류 표시를 묻고 지운다 | ★★ feof 는 읽기가 실패한 뒤에야 참이 된다 — while (!feof(f)) 는 마지막 줄을 두 번 처리하는 흔한 사고다 |
perror | void 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 은 속을 들여다보지 않는다 |
| 부속서 K | fopen_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·l | float 판 · long double 판 | sqrtf · sqrtl |
<tgmath.h> | 접미를 골라 주는 매크로 | sqrt(x) 한 이름으로 셋 다 |
| 십진 이름 | d32·d64·d128 이 들어간 것은 십진 부동소수점용 | 선택 사항이라 없는 구현이 많다 |
표 105.60 — 이름을 읽는 규칙
| 이름 | 무엇을 묻는가 | ★함정 |
|---|---|---|
fpclassify | FP_NAN·FP_INFINITE·FP_ZERO·FP_SUBNORMAL·FP_NORMAL 중 하나 | |
isnan·isinf·isfinite·isnormal | NaN · 무한 · 유한 · 정규 | ★ x != x 대신 isnan 을 쓴다 |
issubnormal·iszero·iscanonical·issignaling | 비정규 · 0 · 정규 표현 · 시그널링 NaN | C23 |
signbit | 부호 비트가 서 있는가 | ★ -0.0 에서 참이다 — x < 0 과 다르다 |
isgreater·isgreaterequal·isless·islessequal·islessgreater·isunordered | NaN 이 있어도 예외를 내지 않고 견준다 | ★ 보통 >·< 는 시그널링 NaN 에서 예외를 낸다 |
iseqsig | 같은가를 묻되 NaN 이면 예외를 낸다 | C23 |
totalorder·totalordermag | IEEE 754 의 전순서로 견준다 | C23. -0.0 과 +0.0 도 가른다 |
표 105.61 — 값이 무엇인지 묻기 — 매크로다
| 무리 | 이름 | 비고 · ★함정 |
|---|---|---|
| 삼각 | sin · cos · tan · asin · acos · atan · atan2 | ★ atan2 는 사분면을 가려 준다 — 인자 순서는 (y, x) 다 |
| 배수 삼각 | sinpi · cospi · tanpi · asinpi · acospi · atanpi · atan2pi | C23. 인자를 의 배수로 본다 — 정밀도가 낫다 |
| 쌍곡 | sinh · cosh · tanh · asinh · acosh · atanh | |
| 지수 | exp · exp2 · exp10 · expm1 · exp2m1 · exp10m1 | ★ expm1 은 을 작은 x 에서 정확히 셈한다 |
| 로그 | log · log2 · log10 · log1p · logp1 · log2p1 · log10p1 · logb · ilogb · llogb | ★ log1p 는 의 정확한 판 |
| 거듭제곱 | 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 · ufromfpx | C23. 방향과 폭을 골라 정수로 만든다 |
| 나머지 | 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 · canonicalize | C23 이 NaN 의 짐(payload)을 다룰 길을 열었다 |
표 105.63 — 반올림 · 나머지 · 자리 옮기기
| 무리 | 이름 | 비고 |
|---|---|---|
| 곱하고 더하기 | fma | ★ 곱셈과 덧셈을 한 번만 반올림한다 — 정밀도의 핵심 |
float 결과 | fadd · fsub · fmul · fdiv · fsqrt · ffma | C23. 넓은 타입에서 셈하고 float 로 한 번만 반올림 |
double 결과 | daddl · dsubl · dmull · ddivl · dsqrtl · dfmal | long 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> 에서 온다.