언어로서의 C는 아주 작습니다. 타입, 연산자, 제어 흐름, 함수, 포인터가 전부입니다. C는 출력할 수도, 파일을 읽을 수도, 문자열을 비교할 수도, 메모리를 할당할 수도 없습니다. 그 모든 것이 표준 라이브러리입니다. 규격을 따르는 모든 구현이 함께 제공하는 헤더 모음이죠.
이 페이지는 지도입니다. 각 절은 헤더가 무엇을 위한 것인지 말하고, 짧게 동작하는 예제 하나를 보여주고, 더 자세한 페이지가 있다면 그곳을 가리킵니다.
stdio.h - 입력과 출력
거의 모든 프로그램에서 가장 먼저 포함하는 헤더입니다. printf와 scanf, FILE 타입과 파일 함수들, 그리고 더 안전한 줄 단위 입력 함수들이 들어 있습니다.
핵심은 이렇습니다. 출력에는 printf, fprintf, sprintf, snprintf. 입력에는 scanf, fscanf, sscanf, fgets, getchar. 파일에는 fopen, fclose, fread, fwrite, fseek. 더 깊은 내용은 printf, scanf, 파일 처리에 있습니다.
기억해둘 규칙 하나: 절대 gets()를 쓰지 마세요. 버퍼의 크기를 알 방법이 없으며 C11에서 표준에서 제거되었습니다. fgets(buf, sizeof buf, stdin)가 그 대체입니다.
stdlib.h - 일반 유틸리티
무엇이든 담는 헤더입니다. 메모리, 변환, 난수, 프로세스 제어, 정렬.
주요 항목: malloc, calloc, realloc, free(동적 메모리 참고). 변환에는 atoi, atof, strtol, strtod - atoi가 조용히 0을 돌려주는 자리에서 실패를 알려주는 strto* 계열을 선호하세요. 그리고 rand와 srand(난수 참고), exit, abort, getenv, system, qsort/bsearch가 있습니다.
string.h - 문자열과 메모리 블록
C의 문자열은 '\0'으로 끝나는 char 배열이며, 문자열에 대한 모든 연산은 함수 호출입니다.
이 헤더가 버퍼 오버플로의 원천이 되지 않게 하는 두 가지 습관이 있습니다. 항상 목적지의 크기를 알고 있을 것, 그리고 strcmp가 같을 때 0을 돌려준다는 사실을 기억할 것 - if (strcmp(a, b))는 문자열이 다를 때 참입니다. 더 안전한 strncpy/snprintf 패턴은 문자열 함수에서 자세히 다룹니다.
math.h - 수학
sqrt, pow, fabs, floor, ceil, round, fmod, 삼각함수와 로그 계열, 그리고 INFINITY와 NAN이 있습니다.
링커 플래그가 필요할 수 있는 유일한 헤더입니다. 리눅스에서는 컴파일 명령 끝에 -lm을 붙이지 않으면 링커가 "undefined reference to sqrt"를 보고합니다. 전체 내용은 수학 함수에 있습니다.
time.h - 시계와 날짜
struct tm의 두 가지 오프셋은 누구나 한 번씩 걸립니다. tm_year는 1900년부터 세고, tm_mon은 0부터 시작합니다. time(NULL)은 srand의 흔한 시드이기도 합니다.
ctype.h - 문자 분류
작지만 보기보다 쓸모가 많습니다. 각 함수는 문자 하나를 받아 예/아니오 질문에 답하거나 대소문자를 변환합니다.
전체 목록은 isalpha, isdigit, isalnum, isspace, isupper, islower, ispunct, isxdigit, isprint, 그리고 toupper와 tolower입니다.
(unsigned char) 캐스트에 주목하세요. 이 함수들은 unsigned char로 표현 가능한 값과 EOF에 대해서만 정의되어 있습니다. 대부분의 시스템에서 그냥 char는 부호가 있으므로, 127을 넘는 바이트는 음수로 전달되고 호출은 미정의 동작이 됩니다. 캐스트는 비용이 전혀 없으면서 이 부류의 문제를 통째로 없앱니다.
그리고 직접 만든 검사보다 이들을 선호하세요. c >= '0' && c <= '9'도 우연히 동작하긴 하지만, isdigit(c)는 의도를 말해주고 어디서나 올바르게 동작합니다.
limits.h와 float.h - 타입의 경계
이 기계에서 int는 얼마나 커질 수 있을까요? 이 헤더들이 답해주며, 이식 가능한 코드가 가정하지 않고 물어보는 이유가 바로 그 답에 있습니다.
INT_MAX는 오버플로가 일어나기 전에 확인하는 방법입니다. 부호 있는 오버플로는 나중에 검사할 수 있는 순환이 아니라 미정의 동작이기 때문에 이 점이 중요합니다.
stdbool.h와 stdint.h - 더 나은 타입
C에는 C99 이전까지 불리언 타입이 없었습니다. <stdbool.h>가 하나를 제공합니다.
bool, true, false는 각각 _Bool, 1, 0에 대한 매크로일 뿐이지만 의도를 분명하게 만들어 줍니다. (C23에서는 키워드가 되어 이 헤더가 더 이상 필요하지 않습니다.)
<stdint.h>는 폭이 보장된 타입을 제공합니다. 파일 형식, 네트워크 프로토콜, 그리고 "int는 아마 32비트겠지"로는 충분하지 않은 임베디드 영역에서 필수입니다.
<inttypes.h>의 PRId32 같은 매크로는 현재 플랫폼에서 각 고정 폭 타입에 맞는 printf 지정자로 확장됩니다. %d는 어떤 시스템에서는 int32_t에 맞고 다른 시스템에서는 틀리기 때문에, 이 매크로가 이식 가능하게 출력하는 방법입니다.
assert.h - 가정 검사하기
assert(expr)는 식이 참이면 아무것도 하지 않고, 거짓이면 메시지를 내고 프로그램을 중단시킵니다.
-DNDEBUG로 컴파일하면 모든 단언문이 제거되므로 릴리스 빌드에서는 비용이 0입니다. 그것이 동시에 경고이기도 합니다. 단언문 안에 부수 효과를 절대 넣지 마세요. assert(read_next() > 0)는 릴리스 빌드에서 읽기 자체를 통째로 멈추게 하고, 그 결과로 생긴 버그는 프로덕션에서만 나타납니다.
단언문은 여러분의 실수를 위한 것입니다. 코드가 올바르다면 불가능해야 하는 조건 말이죠. 잘못된 사용자 입력은 단언문이 아닙니다. 그건 if와 오류 메시지의 몫입니다.
나머지, 간단히
<stddef.h>-size_t,ptrdiff_t,NULL,offsetof<stdarg.h>- 가변 인자 함수,printf뒤의 장치<errno.h>-errno와 함께, 호출이 왜 실패했는지 보고하는perror와strerror<signal.h>- Ctrl-C 같은 시그널 처리<setjmp.h>- 비지역 점프. 정답인 경우가 드뭅니다<locale.h>,<wchar.h>,<wctype.h>- 로케일과 와이드 문자<stdnoreturn.h>,<stdalign.h>,<threads.h>,<stdatomic.h>- C11에서 추가된 것들
두 가지 습관이 이 라이브러리를 쓰기 좋게 만듭니다. 시그니처를 추측하기 전에 매뉴얼 페이지를 읽으세요 - macOS와 리눅스의 man 3 strtol은 정확하고 빠릅니다. 그리고 반환값을 확인하세요. malloc, fopen, strtol은 모두 실패를 알려주며, 이를 무시하는 프로그램은 훨씬 나중에, 훨씬 알아보기 어렵게 실패합니다.
자주 묻는 질문
C 표준 라이브러리란 무엇인가요?
규격을 따르는 모든 C 구현이 제공하는 함수, 타입, 매크로의 집합으로, 약 30개의 헤더로 묶여 있습니다. 입출력(<stdio.h>), 메모리와 변환(<stdlib.h>), 문자열(<string.h>), 수학(<math.h>), 시간, 문자 분류, 숫자 타입의 한계까지 다룹니다. C 언어 자체에는 입출력도 문자열 처리도 없습니다. 전부 여기서 옵니다.
stdio.h와 stdlib.h의 차이는 무엇인가요?
<stdio.h>는 입출력입니다. printf, scanf, fopen, fgets, FILE이 들어 있습니다. <stdlib.h>는 일반 유틸리티입니다. malloc과 free, atoi와 strtol, rand와 srand, exit, qsort, bsearch가 있습니다. 보통의 프로그램은 둘 다 포함합니다.
ctype.h는 무엇을 하나요?
문자 하나를 분류하고 변환합니다. isdigit, isalpha, isalnum, isspace, isupper, islower, ispunct에 더해 toupper와 tolower가 있습니다. c >= '0' && c <= '9' 같은 직접 작성한 비교 대신 이들을 쓰면 의도가 분명해지고 모든 로케일과 문자 집합에서 올바르게 동작합니다.
C 표준 라이브러리를 쓰려면 별도로 링크해야 하나요?
아니요, 다만 수학 함수 하나는 예외입니다. 표준 C 라이브러리는 자동으로 링크되지만, 리눅스에서는 <math.h>의 구현이 별도 라이브러리에 있어서 sqrt 같은 함수에는 컴파일 명령 끝에 -lm이 필요합니다. 나머지는 올바른 #include만 있으면 됩니다.