상세 차례
장 아래 절까지 펼친 차례다. 찾는 자리를 바로 짚을 때 쓴다.
제목과 색인 표제어에서 찾는다 --- 본문 전체가 아니다
제1부 — 바탕: 기계와 기억
2.1컴퓨터를 세 부품으로 그리기2.2비트 — 정보의 최소 단위2.2.1수학적으로도 2진법이 정답일까2.2.2실제로 만들어진 3진 컴퓨터 — Setun2.2.3그러면 왜 2진법이 이겼는가2.3세 부품짜리 단순한 기계의 시절, C가 태어났다
5.1네 구역, 하나의 비유5.2정적 구역이 둘로 나뉘는 이유 — bss라는 이상한 이름5.3정적·스택·힙 세 구역의 성격 대조5.4이 밑그림이 통하는 데까지5.4.1표준에는 「스택」이라는 낱말이 없다5.4.2실제로 다른 기계들 — 지금도 있다5.5이 밑그림이 앞으로 쓰이는 곳
제2부 — 기계의 진짜 모습: 표현과 실행
6.1부호 없는 정수 — 시계처럼 도는 수6.2음수를 담는 세 가지 약속6.3세 약속의 경쟁, 그리고 C23의 결단6.4시프트 — 비트를 통째로 밀기6.5부호 확장(sign extension) — 좁은 그릇에서 넓은 그릇으로6.6정수의 비트를 눈으로 확인하기
7.1고정소수점 — 소수점을 약속으로 못박다7.2부동소수점 — 소수점을 데이터에 싣다7.3비트가 실제로 어떻게 나뉘는가7.4근사가 일으키는 세 가지 사건7.5난립, 그리고 IEEE 754라는 계약7.6부동소수점의 근사를 눈으로 확인하기
8.1문자는 번호다8.2ASCII와 EBCDIC — 두 개의 표8.2.1「알파벳이 연속이 아니다」가 코드에 하는 일8.2.2그래서 라이브러리가 있다 — 추상이 하는 일8.3ISO 646 — 국가 변형과 C의 삼중자8.48비트의 백가쟁명, 그리고 한글8.5유니코드와 UTF-8 — 하나의 표, 영리한 담기8.6C가 가르는 두 문자 집합 — 소스와 실행8.6.1소스의 글자는 언제 실행 문자 집합으로 옮겨 실리는가 — 번역 단계 1과 58.6.2C23이 붙인 더 정확한 이름 — 리터럴 인코딩8.6.3인코딩을 못박는 접두어8.7같은 글자, 여러 표현 — 텍스트에 잠복한 보안 지형8.8글자들의 열 — 문자열을 담는 방식들
9.1펀치카드 — 한 장이 한 줄이다9.2라인프린터 — 줄 단위로 찍는 출력9.3프린터 터미널 — 종이 위의 대화, 그리고 tty9.4화면 터미널, 그리고 스트림이라는 유산9.5띠의 중간에는 통이 있다 — 버퍼9.6띠를 갈아 끼우다 — 리다이렉션과 파이프라인
11.1한 코어로 여러 일을 — 번갈아 하기라는 발상11.2언제 끊을 것인가 — 네 가지 후보11.2.1프로그램이 스스로 코어를 내놓는 것만으로는 왜 부족한가11.3멀티태스킹의 갈래11.4전환할 때 무엇을 옮기는가 — 문맥11.5전환할 시점을 누가 알리는가 — 시계 인터럽트11.6장치와 잇는 선 — 줄지어 보내기와 한꺼번에 보내기11.7바깥이 말을 걸어오는 시점11.7.1인터럽트가 여럿이면11.7.2장치에게 「물어본다」는 것이 무슨 뜻인가11.7.3인터럽트를 처리하는 도중에 또 오면 — 코드는 이렇게 견딘다11.8둘은 어떻게 다른가 — 인터럽트와 멀티태스킹11.9한때는 인터럽트가 개인용 컴퓨터의 거의 전부였다11.10인터럽트와 멀티태스킹이 오늘 어디로 이어지는가
12.1고백 — CPU·메모리·클록 그림은 과하게 단순했다12.2레지스터 — 원래부터 있던, CPU의 손안12.3벌어지는 격차 — 레지스터와 메모리 사이12.4캐시 — 격차의 틈에 끼어든 중간층12.5캐시의 분화 — 다층의 사다리12.6캐시를 재어 보기 — 같은 개수, 다른 읽기 순서12.7또 하나의 사다리 — 가상기억12.7.1세그먼트에서 페이징으로12.7.2TLB — 번역에도 캐시가 붙는다12.7.3쪽 부재, 그리고 게으른 기억12.7.4보호 — 왜 널 참조는 대개 즉시 죽는가
13.1파이프라인 — 조립 라인 위의 명령들13.2분기 예측 — 갈림길을 미리 짐작하다13.3분기 예측을 재어 보기 — 맞히기 쉬운 조건과 어려운 조건13.4클록의 한계, 그리고 멀티코어13.5같은 시대, C는 계약서를 썼다 — 표준 이전의 C와 C8913.6계약서에 없는 자리 — 회색지대13.7C 밖의 표준이 대신 보장할 때
14.1편집자의 작업실 — 컴파일러가 하는 일14.2추상 기계 — 계약서 속의 가상 기계14.3눈에 보이는 편집 — 값을 언제 읽고 언제 쓰는가14.4계약의 정밀화 — C99, C1114.5컴파일러가 받는 잘못된 신호 — 엄격한 앨리어싱 위반이 부르는 유령
제3부 — 첫 프로그램
16.1첫 프로그램16.2터미널과 셸 — 프로그램을 실행하는 자리16.2.1어떤 터미널(창)과 어떤 셸(일꾼)이 있는가16.2.2셸이 하는 일 — 읽고, 가르고, 실행한다16.2.3내가 만든 실행 파일을 실행하기16.2.4인자 넘기기 — 프로그램에 값을 쥐여 주는 법16.3명령행에서 컴파일하고 실행하기16.3.1GCC (리눅스·macOS·MinGW)16.3.2Clang (macOS 기본, 리눅스·윈도)16.3.3MSVC (윈도)16.4그래서 첫 프로그램은 이렇게 나온다16.5한 줄씩 읽기16.6그런데, 소스에서 실행까지는 무슨 일이 있었나
17.1겉보기 — 명령 한 줄17.21주자 — 전처리: 텍스트를 짜깁기한다17.32주자 — 컴파일: C를 어셈블리로17.42주자의 안쪽 — 렉서와 파서17.53주자 — 어셈블: 기계 명령으로 찍어 내다17.64주자 — 링크: 조각을 잇고 구멍을 메운다
18.1C 개발에 필요한 것은 셋뿐이다 — 컴파일러, 편집기, 터미널18.2설치가 부담이면 — 웹에서 바로 해 보기18.2.1학습용 옵션 — 웹에서도 그대로18.3경고 — 공짜 검토를 켜 두어라18.4한글이 깨질 때 — 인코딩의 사슬18.4.1컴파일러가 읽는 인코딩(②)과 실행 문자 집합(③)을 옵션으로 못박는 법18.5디버거 — 멈춰 세워 들여다보기18.6디버그 빌드의 옵션 — -g와 -O0은 다른 스위치다18.7디버거의 맹점 — 최적화된 빌드18.8디버그에선 되는데 릴리스에서만 틀린다18.9디버거를 믿기 어려울 때의 도구들18.10새니타이저 — 실행 중에 치는 그물18.11여기까지 갖춘 것 — 첫 프로그램, 컴파일 과정, 도구
19.1세상의 큰 셋 — GCC, Clang/LLVM, MSVC19.2성능을 파는 컴파일러들 — 벤더 컴파일러19.3임베디드 — 뒤에서 따로 볼 세계의 입구19.4곁다리 하나 — 윈도우의 단정한 대안, Pelles C19.4.1윈도 앱을 C로 짠다는 선택19.4.2한 묶음에 들어 있는 것 — 자원 편집기와 대화 상자 디자이너19.5그 밖에, 그리고 은퇴한 이름들19.6컴파일러별 공식 자료가 있는 곳
제4부 — 최소한의 도구 상자
21.1값과 그 표기 — 이 장이 다루는 것21.2리터럴 — 소스에 적는 값21.3상수를 적는 법 — 한눈에 보기21.4정수 상수 (§6.4.4.1)21.5C23 의 자리 구분자 '21.6문자 상수 (§6.4.4.5)21.7부동소수점 상수 (§6.4.4.3)21.8문자열 리터럴 (§6.4.5)21.9상수처럼 보이는 것들21.10constexpr — C23 이 들여온 진짜 상수21.10.1무엇을 적는 것인가21.10.2const 와 무엇이 다른가21.10.3값이 정확히 담겨야 한다21.10.4파일 스코프에 두면 어떻게 되는가21.11수식 — 계산되어 값이 되는 것21.12순서 — 우선순위, 그리고 괄호라는 관행21.13미리 심는 주의 하나 — 계산의 시간 순서는 다를 수 있다
23.1출력이란 무엇인가 — 프로그램이 바깥에 말하는 법23.2printf — 서식이 있는 출력23.3빈칸의 종류 — 값마다 다른 자리23.4자리를 맞추는 법 — 폭과 정밀도23.5출력의 창구는 하나가 아니다23.6짝 맞추기 — 서식과 재료의 계약23.7제4부를 닫으며 — 헬로 월드 재독
제5부 — 선언: 이름을 만드는 법
25.1일꾼을 만든다는 것 — 선언과 정의, 다시25.2정의 — 일꾼을 만드는 문법25.3선언 — 시그니처만 미리25.4선언에 붙는 낱말 하나 — inline25.4.1inline 은 명령이 아니라 부탁이다25.4.2inline 정의도 주소를 얻을 수 있다 — 그리고 그 순간 실물(외부 정의)이 필요해진다25.4.3C 의 inline 모형 — 규칙 한 줄과 그 파장25.4.4그래서 실무에서는 inline 정의를 두 형태로만 쓴다25.4.5인라인 정의에 붙는 두 가지 제한25.5스코프 — 이름이 보이는 범위25.6외상 청산 — int main(void)
26.1입력이란 무엇인가 — 바깥에서 값을 받아 오기26.2두 단계 — 읽고, 해석한다26.3fgets의 계약 — 무엇을 주고, 무엇은 말해 주지 않는가26.3.1fgets 가 읽은 줄이 잘렸는지는 어떻게 아는가26.4입력 버퍼(통)에 무엇이 남는가 — 서식 읽기는 예상하고, 가져가고, 남긴다26.5해석은 실패할 수 있다26.6제5부를 닫으며
제6부 — 값과 제어
27.1타입이 정하는 네 가지27.2첫 갈래 — 객체 타입과 함수 타입27.2.1완전 타입과 불완전 타입27.3기본 타입27.3.1문자 타입은 셋이다27.4정수·실수·산술 — 겹쳐 부르는 이름들27.5파생 타입 — 있는 것에서 만들어 내는 것27.6한정자 — 같은 타입의 한정판27.6.1한정판은 원본과 같은 크기다27.6.2한정자는 순서가 자유롭고, 겹쳐 쓸 수 있다27.6.3_Atomic 만 얼굴이 둘이다27.6.4한정자는 값이 아니라 통로의 성질이다27.6.5한정자는 붙일 수는 있어도 몰래 뗄 수는 없다27.6.6const 가 어디에 붙는지가 뜻을 뒤집는다27.7폭을 이름에 적는 타입들 — <stdint.h>27.7.1<stdint.h> 타입의 세 갈래를 가르는 기준은 「폭에 무엇을 요구하는가」다27.7.2uint8_t 의 함정 셋27.7.3그래서 어느 정수 타입을 쓰는가27.8요약 — 표준의 낱말과 이 책의 장
28.1정수 타입 가족28.2기본 자료형 한눈에 보기 — 최소 범위와 실제 범위28.3정수 상수의 타입 — 같은 값도 표기에 따라 다르다28.4크기가 계약인 자리 — 고정 폭 타입28.5정수 타입의 경계를 눈으로 본다
33.1반복이란 — 같은 일을 여러 번33.2루프 3형제33.2.1do-while 이 정말로 이기는 자리 — 거꾸로 세기33.3불변식(invariant) — 루프를 믿는 방법33.3.1불변식을 세 조각으로 읽는다33.3.2사람들은 「불변식」이라는 낱말을 어떻게 쓰는가33.3.3이진 탐색 — 불변식이 빛을 발하는 자리33.3.4불변식으로 무엇을 얻는가33.4for 의 첫 칸 — 무엇을 선언할 수 있는가33.4.1첫 칸의 이름은 어디까지 보이고 언제까지 사는가33.5조건 칸은 검사가 아니라 코드다33.6끝나지 않는 루프33.7겹쳐 도는 루프 — 이중, 삼중33.8재회 — Duff’s device
34.1값 복사 — 원본은 안전하다34.2“값 호출”과 “참조 호출” — 용어를 못박는다34.3부수효과와 평가 순서 — 씨앗의 회수34.4재귀 — 자신을 부르는 함수34.5제6부의 마지막 연산자 ?:, 그리고 계약이라는 씨앗34.6연산자 한눈에 — 지금까지 만난 것들
35.1대입은 문장이 아니라 수식이다35.2대입이 하는 일은 셋이다35.3좌변도 평가된다35.4복합 대입 — 왼쪽을 한 번만 평가한다35.5어디서부터 계약 밖인가35.6대입에 숨는 변환
제7부 — 기억
36.1먼저 세 낱말 — 객체, 주소, 포인터36.1.1객체 — 값을 담는 기억의 한 구역36.1.2무엇이 객체이고, 무엇이 아닌가36.1.3객체가 가진 것들36.1.4값인가 자리인가 — 좌변값이라는 눈36.1.5주소 — 객체가 놓인 자리36.1.6포인터 — 주소를 값으로 담는 것36.1.7세 낱말을 한 번에 — 시연36.2세 가지 표기 — 선언, &, *36.3sizeof — 그릇의 크기를 묻다36.3.1sizeof 가 내놓는 타입 — size_t36.4주소는 어떤 수인가 — 이름·크기·순서36.4.1주소는 왜 하필 정수였나36.4.2포인터를 둘러싼 추상화는 시간이 갈수록 두꺼워졌다36.5주소를 인쇄하는 법36.5.1%p 의 정확한 계약36.5.2주소를 문자열로 만들어야 할 때 — uintptr_t와 PRIxPTR36.5.3그런데 — 주소를 정말 찍어야 하는가36.6포인터의 크기는 하나가 아니다
37.1널이란 무엇인가 — 「아무것도 가리키지 않음」37.2nullptr — 비어 있음의 이름37.3어디까지 0을 써도 되는가 — 널 포인터 상수의 정확한 정의37.3.1널이 포인터에 담긴 뒤의 비트는 표기와 별개다37.4함정 — 표현을 0으로 가정하는 코드
38.1정렬과 캐스트 — char*의 특권38.1.1void * 와 char * 의 특권을 나란히 놓으면38.2프로버넌스 — 번호가 같아도 출처가 다르면38.2.1프로버넌스 — 표준에 아직 없는 낱말38.2.2빈틈은 어디에 있었나 — 이웃한 두 변수38.2.3「프로버넌스」라는 낱말이 태어난 자리 — DR26038.2.4출처 규칙의 명문화 — 설문 조사에서 기술 명세까지38.2.5출처 모델이 표준에 들어오면 무엇이 달라지는가38.2.6출처를 하드웨어가 들고 다니는 기계
39.1배열이란 무엇인가 — 같은 것이 여럿일 때39.2엄밀한 정의 — 표준은 배열을 이렇게 적는다39.3선언과 초기화39.4배열로 할 수 없는 것 — 통째로 대입·비교·반환39.5원소 수 세기와 순회39.6배열과 포인터 — 무너짐의 진실39.7원소와 원소 사이에는 빈자리가 없다39.8포인터의 덧셈은 보통의 덧셈이 아니다39.8.1포인터 곁의 세 정수 타입 — ptrdiff_t·intptr_t·uintptr_t39.8.2어디까지 허용되는가 — 산술의 계약39.9매개변수의 배열은 배열이 아니다39.10크기가 실행 중에 정해지는 배열 — VLA39.11매개변수의 [static N] — “최소 이만큼은 온다”39.11.1[static 1] — “널이 아니다”를 문법으로 적는 법39.11.2같은 대괄호 안의 한정자 — 그것은 포인터의 한정자다39.12경계 — 생사의 규칙39.13배열에서 나는 사고들 — 보안과 기억의 문제39.13.1① 한 칸 넘침 — <= 하나39.13.2② 쓰기 넘침 — 이웃을 덮는다39.13.3③ 읽기 넘침 — 새어 나가는 기억39.13.4④ 음수 첨자 — 반쪽짜리 검사39.13.5⑤ 초기화하지 않은 배열 — 남이 쓰던 기억39.14방어의 층 — 무엇을 켜고 무엇을 습관으로 삼는가
40.1다차원 배열이란 — 표와 격자를 담는 법40.2배열의 배열 — 실체부터40.3첨자식이 풀리는 과정40.4매개변수 — 왜 int **가 아닌가40.5같은 주소, 다른 눈 — 평탄화의 계약40.6실무의 패턴들40.6.1평탄 + 선행 차원 — 수치 라이브러리의 공용어40.6.2행 포인터 배열 — 흩어진 행을 하나로 묶기40.7순회 순서와 캐시 — 왜 같은 계산이 몇 배씩 갈리는가40.8다차원 배열에서 나는 사고들40.8.1① 행 끝을 넘는 첨자 — 오류가 아니라 다른 칸40.8.2② 평탄화 첨자의 정수 넘침40.8.3③ 큰 지역 배열 — 스택이 모자란다40.8.4④ 크기를 따로 받는 매개변수 — 거짓말을 해도 모른다
41.1왜 이 장이 따로 필요한가41.1.1왜 이 오해는 이렇게 질긴가41.1.2그리고 발각은 늦게 온다41.2무엇이 다른가 — 다섯 축41.3접근의 걸음 수 — 이것이 진짜 차이다41.4파일을 건너뛰는 사고41.5언제 같아지는가 — 세 규칙41.6무너지지 않는 자리41.76[a]가 합법인 이유41.8매개변수 변환은 한 겹만 일어난다41.9실무의 세 패턴 — 눈으로 보기41.9.1① 포인터의 배열 — 길이가 제각각인 것들을 묶는다41.9.2② 포인터의 포인터 — 남의 포인터를 고치는 자리41.9.3③ 자료를 옮기지 않고 순서만 바꾸기41.10크기가 제각각인 다차원 배열 넘기기 — C가 못 하던 일, C99가 반쯤 푼 일41.10.1C99가 바꾼 것41.11무너짐이 낳는 사고들 — 보안과 기억의 문제41.11.1① sizeof 의 착각 — 비밀이 덜 지워진다41.11.2② 바이트 수와 원소 수 — 네 배 넘침41.11.3③ 지역 배열의 주소를 돌려주기41.11.4④ 끝 표시에 기대는 포인터의 배열41.11.5⑤ 파일 경계 — 앞에서 본 그 사고41.12배열과 포인터 골라 쓰기 — 실무의 판단
42.1중첩 루프 — 다차원을 걷는 법42.2다중 루프에서 빠져나오기42.3goto 가 정당한 두 자리42.3.1자리 ① — 여러 겹의 루프를 한 번에 벗기42.3.2자리 ② — 오류 처리의 정리 지점 모으기42.4매크로를 한 문장으로 — do { } while (0)42.5그 밖의 관용구와 함정42.5.1역순 순회 — size_t 로는 i >= 0 을 쓸 수 없다42.5.2나머지 관용구
43.1문자열이란 무엇인가 — 글자의 줄과 끝 표시43.2C 문자열의 정체 — 배열, 그리고 약속43.3문자열 리터럴 — 읽기 전용의 땅43.3.1문자열 리터럴의 타입은 const가 아니다 — C의 이상한 자리43.4리터럴도 배열이다43.5이어 붙이기 — 인접한 리터럴은 하나가 된다43.6접두사 — 어떤 인코딩의 리터럴인가
44.1사고의 해부 — 경계 침범이라는 유행병44.2표준 도구들은 실패를 어떻게 알리는가44.3입력 해석의 실패를 다루는 다섯 규율44.3.1① 실패를 값으로 돌려준다44.3.2② 확인을 잊으면 컴파일러가 말하게 한다44.3.3③ 어디까지 읽었는지 알려준다44.3.4④ 잘림을 성공으로 치지 않는다44.3.5⑤ 실패하면 출력 인자를 건드리지 않는다44.4입력 해석의 다섯 규율을 부품으로 굳히면
45.1표준의 네 축 — 저장 기간, 유효범위, 연결, 저장 클래스45.1.1저장 기간 — 넷45.1.2유효범위 — 넷45.1.3연결 — 셋45.1.4저장 클래스 지정자 일곱 — 한자리에 모아 놓고45.1.5저장 클래스 지정자의 자리는 하나뿐이다 — 그리고 그 예외45.2자동 수명과 정적 수명45.3기억의 배치 — 구역들을 눈으로45.4스택 — 호출의 장부
46.1동적 메모리란 무엇인가 — 실행 중에 빌리는 기억46.2malloc 으로 빌리고, free 로 돌려준다46.3malloc을 어떻게 적을 것인가 — 오래된 논쟁46.3.1대상에서 크기를 가져오는 표기(①)가 권장되는 이유 넷46.3.2타입을 적고 캐스트하는 표기(②)를 권하는 쪽의 근거 — CERT MEM02-C46.3.3절충 표기(③)와 「C++ 호환」이라는 이유 — 정말 그런가46.4돌려받은 주소의 정렬 — 무엇을 담을지 모르므로46.5값싸 보이는 malloc·free 두 줄의 비용 — 할당은 왜 비싼가46.61차원으로 빌려 2차원으로 쓰기46.7소유 — 누가 돌려줄 책임을 지는가46.8제7부를 닫으며
제8부 — 자료의 모양
47.1구조체란 무엇인가 — 한 대상의 여러 속성47.2엄밀한 정의 — 배열과 나란히 놓고 읽기47.3선언, 초기화, 접근47.4sizeof의 첫 놀라움 — 멤버 크기의 합이 아니다47.5통째로 0으로 — { 0 }과 { }47.5.1무엇이 보장되는가47.5.2{0}과 {}의 미세한 차이 — 패딩47.6구조체는 값이다47.6.1대입은 되는데 비교는 안 되는 이유47.7머리와 데이터를 한 덩어리로 — 유연 배열 멤버47.7.1sizeof와 offsetof — 어느 것으로 크기를 셈하는가47.7.2그래서 대입은 되는가 — 유연 배열 멤버와 복사
48.1중첩과 접근 — 점과 화살표를 섞어 읽기48.2임시 구조체 — 복합 리터럴48.3이름 붙은 인자 — 구조체 하나로 넘기기48.4패딩 — 멤버 사이의 빈자리48.4.1꼬리 패딩은 왜 있는가 — 답은 「구조체의 배열」에 있다48.4.2구조체의 배열과 포인터 — 실무의 패턴48.5통째로 저장하거나 보내면 안 되는 이유48.5.1그래서 어떻게 하는가 — 필드별 직렬화48.6패딩을 없애는 법, 정렬을 강제하는 법48.6.1alignas — 선언 앞자리의 다섯째 갈래, 정렬 지정자48.7이름 없는 멤버와 container_of48.7.1익명 구조체·공용체 (C11)48.7.2container_of — 멤버에서 바깥을 되찾기48.8배열을 값으로 넘기기 — 되는가, 그리고 해야 하는가
49.1공용체란 무엇인가 — 한 자리를 번갈아 쓰기49.2공용체 — 겹쳐 놓기49.3표현을 눈으로 — 엔디안과 패딩49.4활성 멤버와 타입 펀닝 — 같은 비트를 다른 눈으로49.4.1값이 아닌 비트열 — 비값 표현49.4.2공용체의 크기와 정렬, 그리고 공통 초기 시퀀스49.5비트 필드 — 한 워드를 쪼개 쓰기49.5.1비트 필드에서 구현이 정하는 것들49.6구조체와 공용체를 섞는 실무 패턴49.6.1실물 하나 — 2바이트 조합형 한글49.7다음 장으로
50.1직렬화란 무엇인가 — 값을 바깥으로 내보내는 일50.1.1바이트열을 받는 상대는 넷이다50.1.2포인터는 바깥으로 나가지 못한다50.2직렬화를 피할 수 있는 자리와 그 대가50.2.1기억 모양을 형식으로 굳히기50.2.2제자리 형식 — 복사하지 않고 읽기50.3기법 — 커서, 끈적한 오류, 고정 폭50.3.1커서 — 남은 자리를 데리고 다니는 구조체50.3.2고정 폭과 시프트50.3.3가변 길이 정수 — 작은 수는 짧게50.3.4태그·길이·값 — 모르는 것을 건너뛰기50.3.5실수를 보내는 법50.4팩과 패딩 — 구조체를 형식에 맞추지 않는다50.5엔디안 변환 — 세 갈래50.6버퍼 구조와 그 최적화50.6.1모아서 한 번에50.6.2자라는 버퍼50.6.3고리 버퍼와 두 벌 버퍼50.6.4다시 쓰는 버퍼의 찌꺼기50.7형식을 설계하는 원칙50.8보안 — 바깥에서 온 바이트는 모두 상대가 쓴 것이다50.8.1실제로 난 사고들50.8.2막는 구조50.9정리
51.1자료 배치란 무엇인가 — 같은 자료, 다른 순서51.1.1왜 순서가 속도인가51.2재어 보기 — 두 가지 일, 세 가지 배치51.2.1뜨거운 필드와 찬 필드51.3언제 무엇이 이기는가51.4SoA 의 비용51.5캐시에 유리한 자료 구조51.5.1포인터를 따라가는 비용51.5.2캐시에 유리한 자료 구조, 갈래별 처방51.5.3행렬의 순회와 타일링51.6캐시에 유리한 알고리즘51.7게임과 서버의 실무51.7.1엔터티·컴포넌트 시스템51.7.2서버51.7.3거짓 공유 — 배치의 문제로 다시51.8보안 — 캐시는 비밀을 흘린다51.8.1타이밍 부채널 — 비밀에 따라 표를 읽으면51.8.2투기 실행 — 범위 검사를 지나간 읽기51.8.3해시 표 범람 — 평균만 믿으면51.8.4배치를 바꾸면 생기는 다른 위험51.9잴 때의 규율51.10정리51.11제8부를 닫으며
제9부 — 깊은 구석들
52.1수학의 수식, C의 수식52.2엄밀한 정의 — 수식, 연산자, 피연산자52.3부수효과 — 값 말고 일어나는 일52.4수식이 가진 네 가지52.5우선순위와 결합52.6우선순위 때문에 자주 미끄러지는 자리들52.7전위와 후위 — 같은 일을 하고 다른 값을 준다52.7.1표준이 정한 계약52.7.2값이 정해지는 때와 메모리가 바뀌는 때52.7.3「전위가 빠르다」는 이야기의 진실52.7.4C++ 에서는 두 가지가 달라진다52.8연산자별 계약52.9후위 연산자52.10단항 연산자52.11산술 연산자52.12시프트 연산자52.13관계·상등 연산자52.14비트·논리 연산자52.15조건·대입·쉼표52.16평가 순서와 시퀀스 포인트52.16.1세 가지 관계 — 먼저, 순서 없음, 어느 쪽인지 모름52.16.2시퀀스 포인트 — 「여기까지는 다 끝났다」52.16.3계약 밖이 되는 규칙 — 한 객체에 순서 없는 두 접근52.17회색지대 총람52.17.1정의되지 않은 동작(UB)52.17.2미지정(unspecified)52.17.3구현 정의(implementation-defined)52.18연산자가 아닌 것들
53.1다섯 가지 기본 동작53.1.1마스크 만들기, 그리고 시프트 폭의 벼랑53.2플래그 집합 — 가장 흔한 쓰임53.3관용구 — 그리고 왜 그렇게 되는가53.4C23 — 관용구가 이름을 얻다53.5비트를 다루는 원칙 일곱53.6비트 연산 언저리에서 잘 나는 사고들53.7손으로 최적화하기 전에53.8실물 — 비트는 어디에 쓰이는가53.9되짚기
54.1타입 선택과 비교54.2특수값 — 무한대와 NaN54.2.1비트를 직접 열어 본다54.30 근처의 바닥 — 비정규수54.3.1정규수만 있다면 생기는 구멍54.3.2비정규수에서는 해석 규칙이 두 가지 바뀐다54.3.3비정규수의 대가 — 바닥에서는 정밀도가 샌다54.4바닥에서 터진 보안 사고들54.4.1사고 1 — 숫자 하나로 서버를 멈추다54.4.2사고 2 — 느린 계산이 비밀을 흘린다54.4.3사고 3 — 남의 라이브러리가 내 계산을 바꾼다54.5IEEE 754 만 있는 것은 아니다
56.1표준의 세 갈래 — UB, 미지정, 구현 정의56.2UB 는 왜 존재하는가56.3“무엇이든 가능함”의 실제 얼굴56.4계산이 시작되기도 전에 — 파일 모양의 UB56.5그 밖의 기묘한 UB들56.6일부러 만드는 UB — unreachable56.7UB 를 피하는 법 — 규율, 도구, 그리고 부품56.8다음 장으로
57.1메모리 사고란 무엇인가 — 네 가지 약속57.2크기를 어긴 것 — 버퍼 오버플로57.2.1무엇이 넘치는가 — 다섯 갈래57.2.2왜 위험한가 — 덮이는 것이 데이터만은 아니다57.2.3방어의 층57.2.4버퍼가 길이를 데리고 다니기57.3수명을 어긴 것57.3.1누수 — 마지막 포인터를 잃는 것57.3.2댕글링 포인터와 해제 후 사용57.3.3이중 해제와 잘못된 해제57.3.4끝난 자동 객체의 주소57.3.5realloc — 옛 포인터와 크기 057.4초기화와 타입을 어긴 것57.4.1불확정 값 — 쓴 적 없는 자리를 읽기57.4.2비값 표현 — 그 타입의 값이 아닌 비트57.4.3엄격한 앨리어싱 — 다시 보기57.4.4계약 안에서 같은 비트를 다른 눈으로57.4.5정렬 위반57.5출처를 어긴 것 — 프로버넌스 다시 보기57.6한눈에 — 메모리 쪽의 UB, 미지정, 구현 정의57.7도구가 잡는 것, 못 잡는 것57.8정리 — 사고마다 한 줄
58.1메모리 관리란 무엇인가 — 누가, 언제58.2C 에서 수명을 다스리는 요령58.2.1소유자는 하나 — 이름과 주석으로58.2.2빌림과 돌려줌의 짝을 맞추고, 정리는 한 출구로58.2.3수명이 같은 것끼리 묶기58.2.4해제 뒤 nullptr 는 왜 반쪽인가58.3댕글링을 검출하는 자료 구조 — 세대 번호를 단 핸들58.4빌린 것을 장부에 적기 — 독 칠하기와 격리58.5참조 계수 — 세어서 돌려주기58.6추적 수집 — 닿을 수 있는가58.6.1표시-쓸기58.6.2복사 수집 — 옮기면서 모은다58.6.3세대별 수집 — 대부분의 객체는 일찍 죽는다58.6.4증분 수집과 동시 수집58.7수집기의 비용58.8컴파일러가 소유를 추적하면58.9C 에 수집기를 붙이면58.10제9부를 닫으며
제10부 — 구성과 연결: 파일에서 선언까지
61.1링크는 한 단계가 아니다 — 세 이름61.2동적 링크의 진짜 목적 — 크기가 아니라 ABI61.3ABI 가 약속하는 것 — 조목조목61.4무엇이 ABI 를 깨뜨리는가61.5깨뜨리지 않고 고치는 법61.6위치 독립 코드(PIC) — 왜 라이브러리는 이렇게 지어야 하는가61.7이름이 겹치면 라이브러리 함수가 갈아치워진다 — interpositioning
62.1스코프란 무엇인가 — 「보이는 곳」이 아니라 「그 이름이 그것을 뜻하는 곳」62.2네 가지 스코프62.3스코프는 어디서 시작하는가 — 등호 앞이다62.4가림 — 안쪽이 바깥쪽을 덮는다62.5전처리기는 스코프를 모른다62.6스코프·수명·연결 — 자주 뒤섞이는 셋
63.1「C에는 이름 공간이 없다」는 말의 진실63.2네 이름 공간63.3세 축 — 스코프·연결·이름 공간63.4태그가 따로 사는 덕분에 생긴 관행63.4.1typedef struct node node;63.4.2그런데 typedef를 쓰지 말라는 규약도 있다63.5열거 상수는 보통 식별자다63.6가림(shadowing)63.7써서는 안 되는 이름 — 표준과 구현이 예약한 이름
64.1충돌은 언제 어디서 터지는가64.2첫째 무기 — 내보내지 않기(static)64.3둘째 무기 — 접두어 규약64.4셋째 무기 — 심볼 가시성64.5넷째 무기 — 이름 자체를 줄이기64.6C++ 는 이름 충돌을 어떻게 풀었는가64.6.1문법64.6.2익명 이름 공간 — C의 static에 해당64.6.3using namespace — 편의와 그 대가64.6.4ADL — 인자를 보고 마당을 찾는다64.6.5이름 맹글링과 extern "C"64.7좋은 습관 — 요약
65.1전처리기란 무엇인가 — 컴파일 앞에서 글을 고치는 단계65.2매크로 — 이름을 토큰 열로 바꾸기65.2.1괄호 — 매크로를 쓰는 사람이 가장 먼저 배우는 것65.3# — 토큰을 문자열로 (stringize)65.4## — 토큰 두 개를 하나로 (token paste)65.5두 겹 우회(double expansion) — 왜 한 겹으로는 안 풀리는가65.5.1먼저 이름부터 — 매크로 치환 규칙 넷65.5.2손으로 따라가는 전개 — STR_RAW(WIDTH) 와 STR(WIDTH)65.5.3표준이 든 예 — glue와 xglue65.6표준이 든 기묘한 예들65.6.1① 자리표시자(placemarker) — 빈 인자를 붙이면65.6.2② 표준이 “미지정”이라고 못박은 전개65.6.3③ __VA_OPT__ — “비었다”를 언제 판정하는가65.7X 매크로 — 목록 하나에서 코드 여러 벌을65.8번역 단계 — 표준이 못박은 여덟 걸음
66.1셋을 갈라 놓는다 — 선언, 정의, 호출식66.1.1호출식은 연산자다66.1.2C23 이 정리한 원형 없는 함수66.2인자는 대입하듯 들어간다66.3인자의 평가 순서 — 표준이 정하지 않은 것66.3.1순서가 있는 자리66.4한 번의 호출, 다섯 걸음66.5레지스터인가 스택인가 — 예산이 있다66.5.1스택을 치우는 쪽이 갈리면 심볼 이름도 갈린다66.6기묘한 사례들 — ARM 과 임베디드66.7구조체를 주고받을 때66.8함수 포인터를 돌려주는 함수66.9함수 호출 언저리에서 잘 나는 사고들66.10되짚기
67.1가변 인자 함수란 무엇인가 — 인자의 개수가 정해지지 않은 함수67.2<stdarg.h> 의 네 도구67.3기본 인자 진급 — ...을 건너는 값은 굵어진다67.4근본적 한계 — 가변 인자 함수는 인자에 대해 아무것도 모른다67.5_Generic — 타입을 컴파일 시간에 붙잡기67.6proven의 PROVEN_ARG — 실물의 구조67.7역사 — varargs에서 stdarg로
68.1함수 포인터란 무엇인가 — 「어느 함수를 부를지」를 값으로68.2이름은 포인터로 무너진다68.3타입이 곧 계약이다68.4void *와 함수 포인터는 다른 세계다68.5함수 포인터를 인쇄하는 법68.5.1왜 함수 포인터를 %p에 넘길 수 없는가68.5.2이식성 있는 길 — 바이트로 떠서 찍기68.5.3가장 좋은 답 — 주소 대신 이름68.6디스패치 표 — switch 대신 배열68.7가상 함수 표 — C로 짓는 객체 지향
69.1두 가지 독법69.2독법 ① 실습 — 영어 어순으로 이어 읽기69.2.1독법 ①에는 이름이 있다 — 우경식(boustrophedon)69.2.2지워 가며 읽기69.3독법 ② — 이름이 없을 때69.4typedef — 층을 나누어 이름을 붙인다69.4.1typedef 를 어디에 쓰고 어디에 쓰지 않을 것인가69.5실물 — 실무에서 실제로 쓰인 선언들69.5.1① 표준이 직접 낳은 괴물 — signal69.5.2② 실제로 더 험한 것 — X11의 오류 처리기69.5.3③ 그리고 정말로 무의미한 선언69.6도구에 맡기기 — cdecl69.7제10부를 닫으며
제11부 — C 로 흉내 내는 객체 지향 프로그래밍
70.1객체 지향이란 무엇인가 — 원론70.2C 에는 없다 — 흉내다70.2.1「잘하면 된다」를 경계한다70.3세 덩어리를 가른다 — 상태, 행동, 표70.3.1표의 빈칸70.4불투명 타입 — 캡슐화의 C 판70.5이름 — 접두어가 곧 이름 공간70.6첫 인자는 객체 — this 를 손으로70.7이 부의 여섯 장70.8정리
71.1다형성 — 얻는 것, 치르는 것, 경계할 것71.2상속을 표로 잇기 — 기반 표를 품은 파생 표71.3오버로딩 — 이름 하나에 몸 여럿71.3.1표에 넣을 함수와 넣지 않을 함수71.3.2C 에서 오버로딩을 하는 두 길71.3.3_Generic 의 장단점과 경계할 것71.4판이 바뀌어도 살아남는 구조체 — 크기를 구조체 자신에 적기71.4.1지켜야 할 규칙71.4.2다른 길과 견주기71.5수명 — RAII 는 없다71.5.1표준 밖의 도움71.6정리
72.1정적 표와 동적 표72.2표를 짓는 다섯 단계72.2.1봉인72.2.2여러 스레드에서 한 번만 짓기72.3표 포인터를 어디에 두는가72.4여러 인터페이스와 찾기72.5이름으로 찾는 디스패치72.6판이 바뀌는 표72.7정리
73.1무엇을 어떻게 재는가73.2첫째, 간접 호출의 비용은 예측에서 나온다73.3둘째, 같은 종류끼리 모은다73.4셋째, 함수를 고르는 코드의 모양73.5탈가상화 — 컴파일러가 간접 호출을 없앨 때73.6인라인 캐시와 다형 인라인 캐시73.7보안 장치의 비용73.7.1Spectre 와 retpoline73.7.2타입을 확인하는 간접 호출 — CFI73.8어떤 고르기를 쓸 것인가73.9정리
74.1C 위에 언어를 얹은 사람들74.2Objective-C 를 조금74.31980년대의 클래스 레코드 — X Toolkit Intrinsics74.4GObject — 가장 큰 모범 사례74.4.1표를 짓는 순서74.5파이썬의 타입 객체 — CPython74.6커널의 표 — 리눅스와 FreeBSD74.7C 에서 쓰는 COM74.8판을 견디는 표 — SQLite 와 OpenSSL74.9다른 생각들 — 라이브러리와 실험74.10디스패치를 잰 논문74.11사례에서 주목할 생각 — 한 표로74.12정리
75.1매크로 — 쓸 만한 것, 대가, 피할 것75.1.1쓸 만한 매크로75.1.2매크로의 대가75.1.3피할 매크로75.2설계 원칙 — SOLID 를 C 로 읽기75.3객체 지향의 한계 — 기억과 캐시75.4객체 지향의 한계 — 프로그램의 흐름75.5요즘의 흐름 — 객체 지향은 만능이 아니다75.5.1표현 문제 — 무엇이 자주 바뀌는가75.5.2C 의 enum-match75.6객체 지향에 몰입하지 말 것75.7처음으로 돌아가기75.8정리75.9제11부를 닫으며
제12부 — 표준 라이브러리 정독
78.1열고, 쓰고, 닫기 — 실패는 세 번 일어날 수 있다78.2파일 끝을 아는 법 — feof의 오용78.3버퍼 크기보다 긴 줄78.4텍스트 모드와 이진 모드78.5파일 위치와 크기
80.1strncpy의 진실80.2그러면 무엇을 쓰는가80.3겹치는 영역 — memcpy와 memmove80.4strtok — 원본을 부수고 상태를 숨긴다80.5나머지 함수들의 함정
81.1변환 — atoi를 버리는 이유81.2동적 할당 — 다섯 함수와 그 계약81.3종료 — 네 가지 방법의 차이81.4정렬과 탐색 — qsort·bsearch81.5난수 — rand의 한계81.6환경과 프로세스
82.1<ctype.h> 함수를 한눈에82.2첫 번째 함정 — char를 그대로 넘기지 않는다82.3두 번째 함정 — EOF가 섞여 들어온다82.4판정은 로케일에 달렸다82.5여러 바이트 문자에는 무의미하다
83.1왜 있는가 — 관습은 나라마다 다르다83.2로케일은 프로그램 전역 상태다83.3여섯 범주 — 무엇이 무엇을 지배하는가83.4setlocale의 정확한 계약83.5로케일 이름의 문법83.5.1문자집합 이름과 정규화83.5.2다른 세계의 이름 — BCP 47과 Windows83.6환경 변수의 우선순위83.7로케일 데이터는 어디에 있는가
84.1창구 하나 — localeconv84.1.1스물네 멤버를 한눈에84.1.2자리 묶음 문자열의 인코딩84.1.3통화 표기는 세 값으로 조립된다84.2LC_NUMERIC — 조용히 데이터를 망가뜨리는 자리84.3LC_TIME — 날짜와 시각의 표기84.4LC_COLLATE — strcmp는 사전순이 아니다84.4.1strxfrm — 왜 이런 함수가 있는가84.5스레드와 로케일84.6실무의 처방
85.1표준이 정한 것과 정하지 않은 것85.2다섯 가지 문자 상수와 문자열85.3MB_CUR_MAX와 MB_LEN_MAX85.4변환 함수의 네 층85.4.1mbstate_t가 왜 필요한가85.5mbrtowc — 한 단계씩 뜯어보기85.5.1문자열 통째로 — mbsrtowcs
86.1두 진영으로 갈린 wchar_t86.2UTF-16의 한계와 서러게이트 쌍86.3Windows — W 함수군과 그 정확한 인코딩86.4리눅스와 glibc — wchar_t 는 4바이트지만 거의 쓰이지 않는다86.5툴킷의 선택 — GTK와 Qt86.6스트림에는 방향이 있다
87.1원칙 — UTF-8을 안에, 변환은 경계에만87.2검증 — 바이트의 꼴이 맞아도 거절해야 하는 것들87.3「글자 수」의 세 층위, 그리고 넷째87.4정규화 — 같은 글자, 다른 바이트87.5대소문자 변환은 언어에 달렸다87.6아직 살아 있는 레거시 — 2바이트 인코딩87.7변환은 무엇으로 하는가
90.1errno — 전역으로 오류를 나르는 대가90.2assert — 계약을 코드로 적는 가장 싼 방법90.3신호 — 다음 장으로90.4비지역 점프(non-local jump) — setjmp/longjmp
91.1어디서 왔는가 — 유닉스의 유산91.2함수 두 개 — 정확한 모양91.2.1signal의 인자와 반환값91.2.2raise의 인자와 반환값91.2.3표준이 정한 신호 여섯 개91.3처리기 안에서 무엇을 할 수 있는가91.3.1sig_atomic_t의 정체91.4처리기와 기억 — 왜 malloc이 목록에 없는가91.5신호가 저장하고 되돌리는 것 — 레지스터 문맥과 errno91.5.1errno는 예외다 — 손으로 챙겨야 한다91.6실무의 패턴 — 깃발을 세우고 즉시 돌아간다91.7POSIX의 sigaction — 표준이 남긴 빈틈을 메운다91.7.1struct sigaction의 속91.7.2siginfo_t — 누가, 왜 보냈는가91.7.3신호를 잠시 막아 두기 — 마스크91.8실제 사용 사례91.8.1정상 종료 — 가장 널리 쓰이는 패턴91.8.2SIGPIPE — 무시하는 것이 정답인 신호91.8.3시스템 호출이 끊기는 문제 — EINTR91.8.4어떤 소프트웨어가, 왜 신호를 쓰는가
92.1왜 있는가 — 예외가 없는 언어의 궁여지책92.2setjmp와 longjmp — 정확한 모양92.2.1setjmp — 두 번 돌아오는 것처럼 보이는 매크로92.2.2setjmp를 쓸 수 있는 자리 — 네 가지뿐92.2.3longjmp — 뛰기 전에 확인할 세 가지92.3jmp_buf의 정체92.3.1jmp_buf 의 여덟 칸을 들여다보면92.3.2레지스터 복원이 곧 volatile 규칙이다92.4돌아왔을 때 값은 어떻게 되는가 — volatile 규칙92.5자원은 아무도 정리해 주지 않는다92.5.1더 조용한 사고 — 포인터가 옛 값으로 되돌아간다92.6실제로 무엇이 이것으로 지어졌는가92.6.1setjmp 를 쓰는 소프트웨어는 기억을 어떻게 감당하는가 — 답이 전부 같다92.7POSIX의 sigsetjmp/siglongjmp92.8오늘날의 자리 — 언제 쓰고, 무엇으로 대신하는가
94.1왜 표준에 스레드가 들어왔나94.2첫 갈래 — 만들고 기다리기94.3그런데 값이 어긋난다94.4상호 배제 — mtx_*94.5기다리기 — cnd_*94.6한 번만 — call_once94.7갈래마다 따로 — thread_local 과 tss_*94.8★ 그런데 왜 실무는 pthread 를 쓰는가94.9쓸 자리와 쓰지 않을 자리
95.1잃어버린 갱신 — 눈으로 확인한다95.2volatile은 왜 답이 아닌가95.3원자적 타입과 연산95.3.1CAS — 「그대로면 바꿔라」95.4메모리 순서 — 건드리지 않는 것이 기본95.4.1왜 「순서」가 문제가 되는가95.4.2짝이 벽을 세운다 — release 와 acquire95.4.3순서는 공짜인가 — 재어 본다95.5잠금 없음(lock-free)이라는 말95.6쓸 자리와 쓰지 않을 자리95.7자물쇠를 쥔 쪽이 멈추면 — 우선순위 역전과 스핀락95.7.1같은 병의 다른 얼굴 — 사용자 공간의 스핀락
97.1무엇이 승격됐나97.2bool — 무엇을 막는가97.3nullptr — 이름 바꾸기가 아니다97.4나머지 승격과 새 낱말97.5속성 — 타입이 담지 못하는 뜻을 적는다97.5.1속성은 무엇을 하는가 — 재어 본다97.5.2모르는 속성은 무시한다97.6기존 코드를 어떻게 옮기는가
98.1실행 파일 안의 구역, 주소 공간의 구역98.2리눅스의 배치98.3윈도우의 배치98.4운영체제가 없는 세계 — 자유 사용 구현98.4.1Arm Cortex-M — 벡터 테이블의 첫 워드가 스택 포인터다98.4.2AVR — 하버드 구조라 상수도 복사한다98.4.3PIC — 뱅크와 “컴파일된 스택”
99.1장부 ① — 블록마다 붙는 머리말99.2장부 ② — 크기별 목록과 갈래별 캐시99.3단편화 — 누수가 없는데도 메모리가 는다99.4표준 malloc을 갈아 끼우기 — 대안 할당자들99.5크기를 알려 주는 해제 — C23 의 free_sized99.6대안 표준 라이브러리99.7모양을 바꾸는 대안 — 아레나, 풀, 그리고 할당자를 인자로
제13부 — proven — 새로운 안정적인 기반
100.1하나 — 문자열 함수는 그릇의 크기를 모른다100.2둘 — 실패를 확인하게 만드는 장치가 없다100.3셋 — printf는 우리가 말하는 것을 그대로 믿는다100.4넷 — 이건 누가 해제하나100.5다섯 — 아무도 타입을 검사해 줄 수 없는 콜백100.6여섯 번째 — 바이트에는 타입이 있다
101.1왜 이 라이브러리가 있는가101.2이름이 말하는 것 — 프로버넌스(provenance)101.3무엇을 대신하려는가101.4설계를 결정한 약속들101.5무엇이 아닌가101.6설치할 것이 없다는 선택101.7이 책의 예제는 어떻게 빌드되는가101.8첫 프로그램101.9세 가지 규칙 — 이 부 전체를 읽는 열쇠101.10객체 하나의 일생101.11내 프로젝트에 붙이기 — 최소 Makefile
102.1두 가지 반환 모양102.1.1에러 코드 전수102.1.2결과 꾸러미의 갈래102.2버리면 컴파일러가 항의한다102.3실패했을 때 남는 것 — 실패 원자성102.4실패를 위로 올리는 법 — 정리와 함께102.5돌려줄 상대가 없을 때 — 패닉
103.1바이트에는 이름이 있다103.2뷰 — 포인터와 길이를 하나로103.3자르기 — 이 부에서 가장 자주 쓰는 연산103.4넘치지 않는 크기 산술103.5정렬 — 다음 경계로 밀어 올리기
104.1할당자는 값이다104.2세 가지 출처를 갈아 끼우기104.3아레나 — 수명이 같은 것들을 한 번에104.4어느 할당자를 고를 것인가104.5소유와 빌림, 그리고 자기를 가리키는 상태
105.1두 개의 타입, 하나의 규칙105.2거부하되, 자르지 않는다105.3세 갈래의 쓰기 — 거부·자름·성장105.4문자열을 고치는 연산들105.5찾기와 자르기 — 복사 없는 텍스트 처리105.6두 세계의 경계 — NUL 종단과 UTF-16
106.1{} — 타입이 없는 자리표시자106.1.1콜론 뒤의 문법 전부106.1.2라이브러리가 모르는 타입 찍기106.2어느 형식화 함수를 쓸 것인가106.3반대 방향 — 스캐너
107.1네 컨테이너의 한살이107.2자라는 배열107.3침습적 리스트 — 할당 없는 연결107.4링 버퍼 — 고정 크기의 흐름107.5해시 맵, 그리고 공격받는 자료구조107.6최악에도 보장이 있는 정렬107.7바이트를 글자로 — 해시와 인코딩
108.1파일 하나의 한살이108.1.1한 번에 읽고 쓰는 편의 함수들108.2스트림 — 버퍼를 둔 읽고 쓰기108.3시간 — 두 가지 다른 시계108.4난수 — 용도가 생성기를 정한다108.5메모리 매핑
109.1스택리스 코루틴 — 스레드 없이 겹쳐 돌리기109.2작업 시스템 — 코어를 여럿 쓰기109.3스레드, 할당자, 그리고 출처109.4OS 가 없을 때 — 프리스탠딩109.4.1실제 빌드 절차109.5이 라이브러리의 자리 — 무엇이고, 무엇이 아닌가109.5.1지금 무엇이 검증되었는가109.5.2안정성 — 지금은 무엇이 바뀔 수 있는가109.5.3아직 없는 것109.6언제 쓰지 않는 것이 나은가
110.1무엇을 만들 것인가110.2순수 C 판110.3proven 판110.4한 걸음 더 — 중첩과, 재귀 없는 구현110.4.1무엇을 어디에 썼는가110.5그래서 무엇이 달라졌는가
제14부 — 닫으며
111.1make — 무엇을 다시 만들 것인가111.2git — 무엇이 언제 바뀌었나111.3도구 상자의 나머지 — 빌드, 관측, 검사111.3.1① 무엇을 어떻게 만들 것인가 — 빌드와 의존성111.3.2② 무엇이 잘못됐는가 — 디버거와 기록·재생111.3.3③ 어디가 느린가 — 프로파일러111.3.4④ 코드가 옳은가 — 검사와 시험111.3.5⑤ 사람이 읽기 좋은가 — 서식과 문서111.4오늘도 C로 도는 것들 — 그리고 왜111.5업계의 규약 — MISRA C와 그 이웃들111.5.1MISRA C — 자동차에서 나와 업계 전체로111.5.2이웃들 — 안전 규격과 코딩 규칙111.5.3무엇을 금지하는가 — 공통된 패턴111.6C와 C++ — 형제이지, 부모와 자식이 아니다111.7C 코드를 C++ 컴파일러에 넣으면111.8C++ 쪽의 시간은 더 빨리 흐른다111.9섞어 쓰는 법 — extern "C"111.10임베디드 — C의 본진111.11한계를 넘는 법 — 실전의 요령
112.1빌드가 있으면 무엇이 달라지는가112.2파일을 어떻게 나누는가 — 헤더는 계약, 소스는 사정112.3공개와 내부의 경계112.4폴더 구조 — 관행은 무엇을 위한 것인가112.5시험의 갈래 — 각각 무엇을 잡는가112.6골든 시험 — 정답을 파일로 굳힌다112.7얼마나, 그리고 무엇을 시험하지 않는가112.8협업 — 기계가 막는 것과 사람이 보는 것112.9적어 두어야 하는 것
113.1이름이 없으면 같은 것을 다르게 부른다113.2픽스처 — 시험이 딛고 서는 준비물113.3증인 — 고친 결함마다 세워 두는 검사113.4음성 검사 — 그 검사가 정말 무는가113.5래칫 — 늘면 안 되는 수113.6티어 — 늘 전부 돌릴 수는 없다113.7한 건의 일 — 결정과 실행을 가른다113.8조용한 실패 금지 — 그리고 커밋은 결정의 기록이다
115.1회고 — 이미 써 온 C23115.2모던 C 관행 — 한 장의 지침115.3부록 — 이 책 다음으로 건너가는 다리115.4다음에 읽을 책, 그리고 표준 문서115.5마지막으로
부록과 찾아보기
서식 지정의 뼈대출력 변환 — 전체 목록플래그, 폭, 정밀도길이 수식어 — 타입의 폭을 알려 주는 자리고정 폭 정수의 서식입력 변환 — 전체 목록입력에서만 통하는 규칙들반환값 정리자주 하는 실수 모음
정수 진급통상 산술 변환 (두 피연산자의 공통 타입)가변 인자에 걸리는 기본 인자 진급정수 변환 등급 (변환 규칙의 기준자)어떤 변환이 어떤 위험을 부르는가조용한 변환이 일어나는 자리 목록그 밖의 자주 만나는 변환
<string.h> — 문자열과 기억기억 블록 다루기 (mem*)복사와 이어 붙이기 (strcpy·strcat 계열)견주기와 길이찾기와 쪼개기오류 문자열<ctype.h> — 한 바이트의 갈래<stdckdint.h> — 넘침을 묻는 산술<assert.h> — 계약을 실행 중에 확인한다<errno.h> — 실패의 까닭을 남기는 자리<setjmp.h> — 되돌아갈 자리를 적어 둔다<stdarg.h> — 개수를 모르는 인자를 훑는다<stddef.h> — 이름 없는 것들에 이름을 준다<stdbool.h>·<stdalign.h> — 비어 버린 두 헤더<iso646.h>·<stdnoreturn.h> — 흉터 하나와 물러난 이름 하나<limits.h> — 정수가 담을 수 있는 끝<float.h> — 실수가 담을 수 있는 끝<stdint.h> — 폭을 못박은 정수<inttypes.h> — 그 정수를 찍고 읽는 법<stdbit.h> — 비트를 세는 표준 방법<locale.h> — 「어디의 관습인가」를 바꾼다<wctype.h> — 와이드 문자의 갈래<uchar.h> — 유니코드 폭을 못박은 문자<signal.h> — 밖에서 들어오는 끼어듦<fenv.h> — 부동소수점 환경을 만진다<tgmath.h> — 타입에 맞는 것을 골라 준다<complex.h> — 복소수<threads.h> — 표준이 정한 스레드<stdatomic.h> — 쪼개지지 않는 연산<wchar.h> — 와이드 문자의 모든 것<time.h> — 시각과 시간<stdlib.h> — 잡동사니 서랍<stdio.h> — 스트림 입출력<math.h> — 수학 함수
문법을 적는 표기 — BNF에서 EBNF로C 표준의 표기법 읽는 법A.1 어휘 문법A.1.1 어휘 요소A.1.2 키워드A.1.3 식별자A.1.4 보편 문자 이름A.1.5 상수A.1.6 문자열 리터럴A.1.7 구두점A.1.8 헤더 이름A.1.9 전처리 수A.2 구문 문법A.2.1 식A.2.2 선언A.2.3 문장A.2.4 외부 정의A.3 전처리 지시문문법이 답하지 못하는 것표준 문서에서 문법을 찾는 법
덤프란 무엇인가무엇이 들어 있고, 무엇이 없는가파일의 모양 — 리눅스의 코어는 ELF 다주소를 이름으로 바꾸기스택을 되감는 법읽는 절차덤프가 남게 만들기 — 그리고 안 남는 흔한 이유여기서 남기는 것
이 부록을 여는 말전원이 들어오면 — OS 없이 도는 기계OS가 해 주던 일의 목록「초기화하지 않은 전역은 0」은 공짜가 아니다리셋 벡터(reset vector) — 기계가 처음 읽는 자리여기서 남기는 것인터럽트 — 흐름을 가로채는 장치인터럽트·예외·트랩 — 세 낱말의 뜻부터 구별한다이미 써 본 적이 있다 — 하드웨어 예외가 C 함수까지 오는 길밀린 인터럽트는 개수로 쌓이지 않는다처리기를 C 로 쓸 수 있는가 — 칩마다 답이 다르다처리기 안에서 할 수 있는 일이 왜 그렇게 적은가여기서 남기는 것장치 레지스터를 만지는 법 — 기억 사상 입출력과 volatile장치가 주소를 차지한다이름을 붙이자 — 기억 사상 입출력(memory-mapped I/O)반대편 — 명령으로 장치를 구별하는 포트 입출력(port-mapped I/O)그래서 volatile 이 있다프로그램 모르게 기억을 바꾸는 것이 인터럽트만은 아니다 — DMAvolatile 이 해 주지 않는 것인터럽트를 잠깐 끄는 것 — 가장 오래된 자물쇠비트 필드로 하드웨어 레지스터를 그리지 말 것여기서 남기는 것제약 속의 C, 그리고 되가져가기타이머 인터럽트 하나를 끝까지할당자 없이 사는 법폭을 이름에 적는다소수를 정수로 다루기 — 고정소수점스택은 얼마나 깊어지는가시간과 전력이 코드에 보인다되가져가기 — OS 위에서 다시 읽기
먼저 알아 둘 낱말연결을 가르는 네 축가장 오래된 선 — UART 와 RS-232프레임 — 놀고 있는 선에서 글자를 오려 내는 법RS-232 — 같은 프레임, 다른 전압흐름 제어 — 「잠깐만」이라고 말하는 법UART 가 알려 주는 오류 넷드라이버 — 16550 의 레지스터 여덟 칸병렬은 왜 사라졌나두 선으로 여럿 — I2C드라이버 — 제어기에게 신호를 하나씩 시킨다네 선으로 빠르게 — SPI드라이버 — 플래시에게 이름을 묻는다한 선으로도, 그리고 아무나 말해도 — 1-Wire 와 CAN기계 안의 큰길 — PCI 와 PCI Express설정 공간 — 장치가 자기를 소개하는 256바이트밖으로 나가는 큰길 — USB끼어들기 — IRQ대신 옮기기 — DMA셋을 견주면 — 폴링, 인터럽트, DMA여기서 남기는 것
먼저 알아 둘 낱말디스크는 번호 붙은 칸의 줄이다왜 나누는가MBR — 첫 섹터 512바이트에 다 적는다파티션 항목 16바이트파티션이 넷으로 모자랄 때 — 확장 파티션과 EBR 사슬GPT — 배열과 검사와 사본으로 다시 지었다보호 MBR — 0번 섹터는 여전히 MBR 이다GPT 헤더 92바이트GPT 파티션 항목 128바이트GUID 는 글로 쓸 때와 디스크에 놓일 때가 다르다정렬 — 왜 하필 2048섹터에서 시작하는가파티션 안에는 무엇이 있나 — 파일 시스템의 첫 섹터FAT32 — 가장 읽기 쉬운 예부트로더가 파일 하나를 읽기까지ext4 와 NTFS 는 FAT 와 어떻게 다른가매체의 물리 — 왜 무작위 쓰기가 느린가회전하는 원판 — 자리를 찾아가는 시간반도체 — 쓰는 단위와 지우는 단위가 다르다이 기계에서 잰 것fsync — 캐시로 감출 수 없는 비용무엇이 어디서 깨지는가여기서 남기는 것
로더가 파일에게 묻는 네 가지머리가 없는 형식 — 도스의 .COMMZ — 도스가 붙인 최초의 머리a.out — 유닉스의 첫 형식, 그리고 그 한계COFF, 그리고 그 후손 PEELF — 두 개의 목록그 밖의 형식들, 그리고 매직 숫자여기서 남기는 것
전원이 들어오면BIOS 시대 — 512바이트가 전부였다디스크의 배치도 — MBR 에서 GPT 로UEFI — 펌웨어가 파일 시스템을 읽는다부트로더가 실제로 하는 일왜 여러 단계인가 — 446바이트의 그늘UEFI 경로 — 이름과 변수로 찾는다부트로더 없이 — 리눅스 EFI 스텁임베디드 — U-Boot 와 MCUboot넘겨줄 때 무엇을 물려주는가안 뜰 때 — 증상으로 칸을 가린다운영체제마다 — 그 뒤에 무슨 일이 일어나는가MS-DOS윈도우리눅스RTOS 와 작은 기계사슬이 곧 신뢰의 사슬여기서 남기는 것
왜 재는가 — 개념만 알면 남는 것은 틀린 직관이다재는 도구를 먼저 잰다① 기계의 숫자를 예제가 스스로 읽는다② 해상도와 ③ 호출 비용은 다르다④ 첫 번째 함정 — 최적화가 재려던 것을 지운다⑤ 두 번째 함정 — 첫 회는 느리다⑥ 세 번째 함정 — 평균은 한 번의 방해에 끌려간다이 부록의 여섯 규율기억의 사다리를 재다먼저 원리 — 왜 사다리가 생겼는가그래서 어떻게 재야 하는가잰 것 — 계단이 보인다줄과 걸음 — 캐시가 실어 오는 단위먼저 원리 — 왜 낱개가 아니라 줄인가재는 방법 — 걸음 폭을 바꾸되 횟수는 고정잰 것 — 줄이 시간에 나타난다같은 자료, 다른 배치 — AoS 와 SoA주소 번역에도 비용이 있다 — TLB 와 쪽먼저 원리 — 번역표와 그 캐시재는 방법 — 자료는 조금, 쪽은 많이잰 것 셋갈림길의 비용 — 분기 예측먼저 원리 — 왜 미리 짐작해야 하는가재기 전에 — 분기가 아직 남아 있는가잰 것얼마나 규칙적이어야 맞히는가코어 사이의 비용 — 거짓 공유와 원자 연산먼저 원리 — 줄은 한 번에 한 코어의 것거짓 공유 — 남의 변수 때문에 내가 느려진다잰 것진짜로 함께 쓰는 값 — 원자 연산과 자물쇠수 계산의 비용 — 부동소수점먼저, 이 절의 측정이 한 번 실패했다잰 것 ① — 비정규수는 72배 느렸다잰 것 ② — float 가 double 보다 빠른가잰 것 ③ — 연산마다 비용이 다르다잰 것을 한자리에ARM 실기에서 다시 재 보면이 수를 어떻게 쓰나여기서 남기는 것
세 갈래의 기계주소 번역의 구조 — MMU 가 실제로 하는 일주소 번역 때문에 벌어지는 세 가지하나 — 할당은 약속이다둘 — 그 약속의 비용은 나중에 치른다셋 — 복사하지 않는 복사주소 공간을 부려 쓰는 재주주소를 번역하지 않는 기계주소 공간이 여럿일 때캐시와 DMA, 그리고 스크래치패드포인터에 살이 붙는다
왜 그런가 — 표준 C 가 주지 않는 것그래도 표준 C 만으로 되는 것간극의 정체 — C 실력의 문제가 아니다갈래 넷 — 무엇을 더 배워야 하는가화면에 창 하나 — GUI 로 가는 길무엇을 만들 것인가어디서 배울 것인가C 를 더 파고들 곳컴퓨터 과학을 넓게 볼 곳길잡이와 목록