전문가를 위한
C
전문가를 위한 C 동시성, OOP부터 최신 C, 고급 기능까지! 극한의 C를 마주하려는 여행자를 위한 가이드북 초판 1쇄 발행 2022년 10월 12일 지은이 캄란 아미니 / 옮긴이 박지윤 / 펴낸이 김태헌 펴낸곳 한빛미디어 (주) / 주소 서울시 서대문구 연희로2길 62 한빛미디어(주) IT출판부 전화 02 – 325 – 5544 / 팩스 02 – 336 – 7124 등록 1999년 6월 24일 제 25100 – 2017– 000058호 / ISBN 979 – 11 – 6921 – 034 –8
93000
총괄 전정아 / 책임편집 서현 / 기획·편집 정지수 / 교정 박지영 베타리더 강수연, 권성민, 박수빈, 사지원, 손승하, 이승표 디자인 표지 최연희 내지 박정화 / 전산편집 이경숙 영업 김형진, 김진불, 조유미 / 마케팅 박상용, 송경석, 한종진, 이행은, 고광일, 성화정 / 제작 박성우, 김정우 이 책에 대한 의견이나 오탈자 및 잘못된 내용에 대한 수정 정보는 한빛미디어(주)의 홈페이지나 아래 이메일로 알려주십시오. 잘못된 책은 구입하신 서점에서 교환해드립니다. 책값은 뒤표지에 표시되어 있습니다. 한빛미디어 홈페이지 www.hanbit.co.kr / 이메일
[email protected]
© HANBIT MEDIA INC. 2022. Copyright ©Packt Publishing 2019. First published in the English language under the title ‘Extreme C – (9781789343625)’ This translation is to be published and sold by permission of Packt Publishing, the owner of all rights to publish and sell the same. 이 책의 저작권은 Packt Publishing과 한빛미디어(주)에 있습니다. 저작권법에 의해 보호를 받는 저작물이므로 무단 복제 및 무단 전재를 금합니다.
지금 하지 않으면 할 수 없는 일이 있습니다. 책으로 펴내고 싶은 아이디어나 원고를 메일 (
[email protected] ) 로 보내주세요. 한빛미디어(주)는 여러분의 소중한 경험과 지식을 기다리고 있습니다.
동시성, OOP부터 최신 C, 고급 기능까지! 극한의 C를 마주하려는 여행자를 위한 가이드북
전문가를 위한
C
캄란 아미니 지음 박지윤 옮김
베타리더 후기
실무만으로 채울 수 없는 부분을 채워주는 책. 주니어에게 너무 좋은 책입니다. 강수연, 임베디드 소프트웨어 개발자
C 언어를 오랫동안 사용한 개발자라도 이 책에서 다루는 다양한 지식을 익숙하게 사용하거나 모든 내용을 사용해본 개발자는 많지 않을 겁니다. 단순히 C 언어의 기능을 나열하는 것이 아 닌, 실행 파일부터 메모리 구조, 테스트에 이르기까지 C 언어를 다루는 시니어 개발자에게 필 요한 내용을 담았습니다. 그만큼 이 책은 전문성도 갖추고, 가독성도 훌륭한 완벽한 책입니다. 이 책은 C 언어 초중급 개발자를 전문가 수준으로 끌어올릴 수 있는 내용으로 가득 차 있습니 다. 탄탄한 예제와 상세한 설명, 다양하지만 꼭 필요한 내용을 담아 C 언어 시니어 개발자가 될 수 있는 훌륭한 이정표 역할을 합니다. 지금까지 읽어본 많은 C 언어 도서 중 꼭 읽어야 할 필 독서로 추천합니다. 손승하, 삼성전자
C++11만 사용하다가 이 책을 접하며 C 언어도 C11을 시작으로 모던modern C로 변화가 이루 어지고 있다는 사실을 알게 되었습니다. 이 책을 읽으며 인상적이었던 부분은 C11 이후의 변 화뿐만 아니라 C 언어의 특성과 활용을 좀 더 깊이 있게 다룬다는 점이었습니다. 예를 들면 C 언어를 C++과 비슷하게 사용하려면 어떻게 표현할 수 있는지, 소스 코드가 최종적으로 플랫 폼에서 실행되기까지의 과정을 상세하게 설명합니다. C 언어는 다른 언어와 통합하기 쉬운 언 어인데 이와 관련된 내용도 다루며 C 언어로 작성한 라이브러리를 다른 언어와 연동해보는 연 습도 매우 유용했습니다. 모던 C뿐만 아니라 C 언어가 실행 파일로 변환되는 과정과 C++ 언 어와의 비교 및 유사하게 사용하는 방법 그리고 표준 C 언어 등 다양한 주제를 생각해볼 수 있 는 책이라 읽는 내내 도움이 많이 되었습니다. 이승표, 게임 서버 프로그래머
4
C의 기본 문법을 익힌 후에 C로 어떤 것을 할 수 있는지, 코드가 어떤 방식으로 작동하는지 자 세히 안내하는 책입니다. 업무에 즉각적으로 도움이 되는 설명으로 가득 차 있습니다. 자세한 설명과 예제를 직접 실행해보며 코드가 작동 가능한 실행 파일로 생성되는 과정을 이해할 수 있었습니다. 특히 동시성을 다루는 5부에서는 단계별 작업을 코드 박스를 통해 세세하게 설명 하고, 헷갈리는 용어와 개념도 자세히 설명해 명확하게 내용을 정리할 수 있었습니다. 또한 빌 드 시스템에 대해서도 간략히 소개하며, 그 덕분에 실제로 프로젝트에 포함된 파일들이 어떤 역할을 하는지도 알 수 있었습니다. C 언어의 기본 문법을 익힌 후, 좀 더 빨리 이 책을 접했더 라면 실력이 더 빨리 늘었을 거라 생각합니다. 권성민, 스마트레이더시스템 선임연구원
절차지향 프로그래밍 언어인 C 언어를 이용해 객체지향 프로그래밍을 시도하는 부분이 매우 흥미로웠습니다. C 언어는 객체지향이 될 수 없다는 것을 인정하고 시작하지만, 그럼에도 불구 하고 이를 구현하기 위해 객체지향의 특성에서부터 세심하게 접근합니다. 그 결과 각 특성을 C 언어로 최대한 유사하게 구현했으며, 저 스스로도 객체지향 프로그래밍의 본질을 깊이 들여다 볼 수 있는 기회였습니다. 그 외에도 C 언어를 이용해 심도 있는 다양한 주제를 제시하고 설명 함으로써, 많은 C 언어 개발자에게 도전의 기회를 제공하는 책입니다. 박수빈, 엔씨소프트 개발자
C 프로그래밍 언어에 대한 매우 자세한 설명과 고급 활용법뿐만 아니라 컴파일 언어의 특징(컴 파일러), 그리고 프로그램이 작성되는 운영체제를 포함해 광범위한 내용을 다루는 책입니다. 이 책에서 설명하는 내용은 다른 프로그래밍 언어를 사용할 때도 충분히 도움 되는 내용이며, 특히 C++을 주로 사용하고 있다면 반드시 읽어야 합니다. 사지원, 카카오모빌리티
5
지은이·옮긴이 소개
지은이
캄란 아미니 Kamran Amini
임베디드 및 커널 전문 개발자. 이란의 여러 유명 회사에서 시니어 엔지니어, 아키텍트, 컨설 턴트, CTO로 근무했습니다. 2017년에는 유럽으로 건너가 제퍼슨 Jeppesen, 아데코 Adecco, 톰 톰 TomTom, 액티브비디오 네트웍스 ActiveVideo Networks와 같은 명망 높은 회사에서 시니어 아키텍 트 겸 엔지니어로 근무했습니다. 암스테르담에 머무르는 동안 이 책을 집필했으며 주된 관심 분야는 컴퓨터 이론, 분산 시스템, 머신러닝, 정보 이론, 양자 컴퓨터입니다. 직업적 커리어를 쌓으면서 천문학과 행성학도 공부합니다. 우주의 초기 발생, 블랙홀의 기하학, 양자장론, 끈 이 론 분야에 학술적 관심을 두고 있습니다.
옮긴이
박지윤
[email protected]
판교의 한 프런트엔드 개발자. C 언어에 관심이 있는 프런트엔드 개발자라니, 하나만 잘하기에 도 벅찬 인생이지만 이 책의 저자만큼이나 관심사가 넓은 편입니다. 언젠가 웹 공간에 원하는 비주얼과 사운드를 펼쳐 놓을 수 있기를 희망하며 관련 공부를 하고 있습니다. C 언어에 대한 관심은 사운드/오디오 개발에서 비롯했습니다. 우주에 대해 캄란 아미니만큼 학술적 관심이 있 지는 않지만, 블랙홀에 대한 최신 소식이 나오면 찾아보거나, 물리학자 카를로 로벨리Carlo Rovelli 의 신간이 나오면 사서 읽어보곤 합니다.
6
리뷰어 소개
리뷰어
알리악바르 아바시 Aliakbar Abbasi
여러 기술과 프로그래밍 언어를 사용하며 6년 이상의 개발 경험이 있는 소프트웨어 개발자이 자 OOP, C/C++, 파이썬 전문가. 기술 서적으로 주로 학습하며 소프트웨어 개발에 대한 지식 을 넓히고 있습니다. 요즘은 암스테르담에서 아내와 함께 지내며 톰톰 TomTom에서 시니어 소프 트 엔지니어로 근무합니다.
리뷰어
로힛 탈워커 Rohit Talwalkar
C, C++, 자바 분야의 아주 숙련된 소프트웨어 개발자. 유료 RTOS (실시간 운영체제), 윈도 우, 윈도우 모바일 장치 그리고 안드로이드 플랫폼에서 애플리케이션, 드라이버 및 서비스를 개발합니다. 뭄바이의 명망 있는 인도 공과대학교에서 기술 학사를 취득했으며 컴퓨터 과학 석 사 학위가 있습니다. 모토로라와 블랙베리에서 근무했으며, 지금은 혼합현실(MR ) 안경을 만 들고 공간 컴퓨팅을 전문으로 하는 매직 리프 Magic Leap에서 애플리케이션 개발 리드 엔지니어 로 일합니다. 브라이언 오버랜드 Brian Overland의 『C++ for the Impatient (참을성 없는 사람을 위한 C++)』의 리뷰에 참여하기도 했습니다.
7
옮긴이의 말
C 언어라는 만만치 않은 주제에 원서 제목에는 ‘익스트림(Extreme )’까지 붙은 데다, 원서 기 준 무려 800여 페이지에 달하는 책이 첫 번역서라니! 작업을 시작할 때는 부담감이 컸지만, 페 이지를 넘길수록 반드시 넘어보고 싶은 산이 되었습니다. 이 책은 C 개발자가 아니더라도 한 번은 읽어보면 좋을 내용으로 가득했습니다. 저자는 이 책의 대상 독자를 C 언어 전문가가 되고자 하는 사람으로 보았지만, 저는 초보자가 시니어가 될 때까지 옆에 두고 계속 보아도 좋은 책이라고 생각합니다. C 언어 그 자체뿐만 아 니라 C 언어와 컴퓨터의 역사, 객체지향, 커널, 스레드, 프로세스, 다른 언어와의 통합에 이르 기까지 그야말로 여러 주제를 폭넓게 다룹니다. 그만큼 C 언어가 IT 기술에 미치는 영향이 방 대하다는 증거이며, C 언어가 아직도 건재한 이유겠지요. C 언어를 전문으로 다루지 않는 사 람이라도 여러 개념과 역사를 훑고 싶다면 이 책은 유용하리라고 봅니다. 역자는 한 사람이지만 번역하는 과정에서 결코 혼자 하는 일이라 생각지 않았습니다. 사실 어 떤 일이든 혼자 할 수 있는 일은 거의 없을 겁니다. 본업인 개발도 그렇고요. 여러분이 혼자 이 책을 읽게 되더라도, 이를 매개로 많은 사람과 연결될 수 있기를 바라고, 이를 통해 더 좋은 일, 더 재미있는 일을 하실 수 있기를 바랍니다. 책을 작업하는 동안 편집자님들의 수고가 컸는데 옮긴이만 혼자 글을 남기려니 머쓱하네요. 정 지수 편집자님, 박지영 실장님, 이 지면을 빌려 감사의 인사를 전합니다. 그리고 작업을 끝까지 믿고 맡겨주신 서현 팀장님, 한빛미디어 측에도 감사드립니다. 박지윤
8
이 책에 대하여
현대 사회에서 우리는 매일 놀라운 기술과 마주합니다. 기술의 발전으로 불과 수십 년 전에는 상상할 수도 없었던 호사와 행복을 누리고 있습니다. 자율주행이 현실이 되는 시대를 살고 있 으며 물리학 및 기타 과학 분야가 진보하면서 현실을 인식하는 방식도 변하고 있습니다. 양자 컴퓨터가 이제 막 걸음마를 시작했다는 뉴스, 블록체인 기술과 암호화폐에 관한 소식, 다른 행 성을 개척한다는 뉴스를 접합니다. 놀랍게도 이런 다양한 혁신의 뿌리는 단 몇 가지 핵심 기술 에 있습니다. 이 책은 핵심 기술 중 하나인 C를 다룹니다. 필자는 고등학교 1학년 때 C++로 프로그래밍을 시작했습니다. 당시 학교에서 2학년 대상의
2D 축구 시뮬레이션 팀에 가입하면서 C++에 이어 리눅스와 C를 알게 되었습니다. 그때는 C와 유닉스의 중요성을 잘 몰랐습니다. 하지만 점차 여러 프로젝트를 통해 C와 유닉스에 대한 경험을 쌓았고, 관련 교육을 들으며 C와 유닉스의 중요한 역할과 위치를 이해했습니다. C에 대 해 알면 알수록 C의 매력에 빠지게 되었고, 마침내 필자의 관심을 사로잡았던 이 언어의 전문 가가 되기로 결심했습니다. 또한 지식을 널리 전파하고 사람들에게 C의 중요성을 알리는 지지 자가 되기로 했습니다. 이 책은 그러한 결심의 결과물입니다.
C는 죽은 언어라는 오해와 일부 기술 전문가들의 무시에도 불구하고, 티오베 TIOBE에서 확인할 수 있는 티오베 지수1는 이를 반증합니다. C는 자바와 함께 지난 15년간 가장 인기 있는 언어 였습니다. 그리고 최근 몇 년 동안 인기가 더 높아지고 있습니다. 필자는 여러 BSD 유닉스 계열과 리눅스, 윈도우를 포함한 다양한 플랫폼에서 C, C++, 고Go, 자바, 파이썬을 사용해 수년간 개발 및 설계 경험을 쌓았고, 그 결과 이 책을 쓰게 되었습니다. 이 책의 주요 목표는 독자의 스킬을 한 단계 높이는 것입니다. C를 사용할 때 한 단계 더 나아 갈 수 있도록 하고, 애써 경험한 내용을 실용적으로 적용하기 위함입니다. 쉬운 여정은 아닐 것 입니다. 그래서 이 책의 원서 제목을 『Extreme C』라고 지었습니다. C로 향하는 여정이 이 책의 핵심 주제이므로 다른 프로그래밍 언어에 관해 논쟁하지는 않겠습니다. 실용적인 책이 되기를 바라지만, 실제 적용과 관련된 핵심 이론도 꽤 많이 설명해야 합니다. 이 책에는 실제 시스템에 1 https://www.tiobe.com/tiobe-index
9
서 마주할 상황을 대처하는 방식을 다루는 예제가 가득합니다. 이런 무거운 주제를 다룰 수 있게 되어 매우 영광입니다. 이토록 의미 있는 주제로 집필하게 된 것도 정말 큰 기쁨입니다. 이 기쁨과 놀라움은 첫 책을 쓸 수 있도록 격려해준 앤드루 왈드 론 Andrew Waldron 덕분입니다. 또한 이 여정을 한 장 한 장 함께한 개발 편집자 이언 휴 Ian Hough에게도 깊은 감사를 전합니다. 지치지 않고 피드백해준 동료 알리아크바르 아바시 Aliakbar Abbasi, 키쇼어 리트 Kishor Rit, 가라프 가바스 Gaurav Gavas, 베로니카 파이스 Veronica Pais 그리고 이 책을 준비하고 출판하는 데 최선의 노 력을 기울여준 소중한 분들에게 감사를 전합니다. 여러분을 이 긴 여정에 함께 할 동반자로 초대합니다. 이 책을 읽으면서 변화가 증명되기를, 여 러분이 C를 새로운 시선으로 볼 수 있기를, 그리고 이 과정을 거쳐 더 나은 개발자가 될 수 있 기를 바랍니다.
대상 독자 C와 C++ 개발에 관해 최소한의 지식을 가진 독자를 위해 썼습니다. C/C++ 주니어와 중급 엔 지니어가 주요 독자입니다. 이 정도 지식이라면 이 책을 최대한 활용할 수 있으며 자신의 전문 지식도 최대한 사용할 수 있습니다. 책을 읽고 나서 시니어 엔지니어로 거듭날 수 있기를 바랍 니다. 더불어 도전 의식을 북돋우고, 더 높은 연봉과 훨씬 더 유의미한 직무를 맡을 수 있기를 바랍니다. 일부 주제는 시니어 C/C++ 엔지니어에게도 유용합니다. 하지만 시니어 엔지니어라 면 대부분의 주제는 익히 알고 있을 것이며 몇 가지 세부 사항 정도가 유용할 것입니다. 이 책은 학생과 연구자에게도 유용한 내용을 다룹니다. 컴퓨터 과학, 소프트웨어 엔지니어링, 인공지능, 사물 인터넷(IoT ), 천문학, 입자 물리학, 우주학과 같은 과학이나 공학 분야의 학 사, 석사, 박사 과정을 밟는 학생뿐만 아니라 해당 분야의 연구자라면 이 책을 통해 C/C++, 유닉스 계열 운영체제, 관련 프로그래밍 스킬을 향상할 수 있습니다. 이 책은 복잡한 원격 제어 장치, 시뮬레이션, 빅데이터 처리, 머신러닝, 딥러닝 등을 수행하거나, 멀티스레드 또는 멀티프
10
로세스 시스템에서 작업하는 엔지니어와 과학자에게도 유용합니다.
다루는 내용 이 책은 7부 part로 구성됩니다. 각 부에서는 C 프로그래밍의 특정 측면을 다룹니다. 1부에서는
C 프로젝트를 빌드하는 방법, 2부에서는 메모리, 3부에서는 객체지향에 초점을 맞춰 설명하 고, 4부에서는 주로 유닉스와 C의 관계를 살펴봅니다. 5부에서는 동시성을, 6부에서는 프로세 스 간 통신을 다루며, 마지막으로 7부에서는 테스트 및 유지 보수에 관해 다룹니다. 다음은 총
23개 장의 핵심 주제를 요약한 내용입니다.
1부 C 프로젝트 빌드 1장 필수 요소: C에서 찾아볼 수 있는 필수 요소를 소개합니다. 이 요소들은 C를 사용하는 방식에 중대
●
한 영향을 미칩니다. 이 책 전반에 걸쳐 이러한 기능을 자주 사용할 예정입니다. 주요 주제는 전처리 및 매크로를 정의하는 지시자, 변수와 함수 포인터, 함수 호출 메커니즘 그리고 구조체입니다. 2장 소스 코드에서 이진 파일로: C 프로젝트를 빌드하는 방법에 관해 설명합니다. 전체 파이프라인 및
●
개별 파이프라인 구성 요소의 관점에서 컴파일 파이프라인을 상세히 배워봅니다. 3장 목적 파일: 컴파일 파이프라인을 사용해 빌드한 이후의 C 프로젝트 결과물을 살펴봅니다. 목적 파
●
일과 그 다양한 종류를 소개합니다. 또한 목적 파일 내부를 들여다보고 어떤 정보를 추출할 수 있는지 알 아봅니다.
2부 메모리 4장 프로세스 메모리 구조: 프로세스 메모리 레이아웃을 들여다봅니다. 메모리 레이아웃에 어떤 세그먼트
●
가 있는지, 그리고 정적 메모리 레이아웃과 동적 메모리 레이아웃이 뜻하는 바가 무엇인지도 살펴봅니다. 5장 스택과 힙: 스택과 힙 세그먼트를 구체적으로 다룹니다. 스택과 힙 변수를 설명한 뒤 그 수명을 C에
●
서 관리하는 방법을 이야기합니다. 힙 변수에 관한 모범 사례를 알아보고 힙 변수를 관리하는 방법도 살 펴봅니다.
11
3부 객체지향 6장 OOP와 캡슐화: C의 객체지향을 다루는 네 개 장 중 첫 번째 장입니다. 6장에서는 객체지향의 배경
●
이론을 살펴보고, 관련 자료에서 자주 사용하는 용어의 중요한 정의를 짚어봅니다. 7장 합성과 집합: 합성과 합성의 특수한 형태인 집합에 초점을 맞춥니다. 합성과 집합의 차이를 살펴보
●
고 이들의 차이를 확인할 수 있는 예제를 제공합니다. 8장 상속과 다형성: 상속은 객체지향 프로그래밍에서 가장 중요한 주제입니다. 8장에서는 두 클래스 사
●
이에서 상속 관계를 만드는 방법과 C에서 이를 어떻게 수행하는지 알아봅니다. 중요한 논제인 다형성에 대해서도 살펴봅니다. 9장 추상화와 C++의 OOP : 추상화에 관해 설명합니다. 구체적으로는 추상화의 자료형 및 C에서 구현
●
하는 방법을 논의합니다. C++의 내부를 살펴보고 객체지향이라는 개념을 C++에서 어떻게 구현하는지 예제로 알아봅니다.
4부 유닉스 10장 유닉스의 역사와 아키텍처: C와 유닉스는 서로 뗄 수 없는 관계입니다. 10장에서는 왜 C와 유닉스
●
가 서로 강하게 연결되어 있는지, 그리고 서로가 살아남을 수 있도록 어떻게 도왔는지 설명합니다. 유닉 스의 아키텍처도 학습하며, 운영체제가 제공하는 기능을 프로그램이 어떻게 사용하는지도 배워봅니다. 11장 시스템 호출과 커널: 유닉스 아키텍처의 커널 링을 중점적으로 살펴봅니다. 시스템 호출을 자세히
●
살펴보고, 리눅스에 새로운 시스템 호출을 추가해봅니다. 커널의 다양한 유형을 살펴보고, 커널 모듈이 작동하는 방식을 알아보기 위해 리눅스에 새로운 간단한 커널 모듈을 작성해봅니다. 12장 최신 C : 최신 버전의 C 표준인 C18에 관해 알아봅니다. 이전 버전인 C11과 얼마나 다른지 살펴
●
봅니다. 또한 C99와 비교해 새로 추가된 몇몇 특성의 예를 알아봅니다.
5부 동시성 13장 동시성: 동시성 개념을 살펴보고, 동시 환경 및 인터리빙과 같은 다양한 속성을 소개합니다. 이러한
●
시스템이 비결정론적인 이유와 경쟁 상태와 같은 동시성 문제가 어떻게 발생하는지 알아봅니다.
12
14장 동기화: 동시 환경에 대한 논의를 계속합니다. 동시 시스템에서 관찰할 수 있는 다양한 문제 유형
●
에 관해서도 논의합니다. 여러 문제 유형 중 이 책에서는 경쟁 상태, 데이터 경쟁 및 교착 상태를 다룹니 다. 이러한 문제 해결 방법과 세마포어, 뮤텍스, 조건 변수를 설명합니다. 15장 스레드 실행: 여러 스레드를 실행하고 관리하는 방법을 예제와 함께 살펴봅니다. 또한 14장에서
●
논의한 동시성 문제에 관한 실제 C 예제도 제공합니다. 16장 스레드 동기화: 여러 스레드를 동기화할 수 있는 기법을 알아봅니다. 중요한 주제인 세마포어, 뮤
●
텍스 및 조건 변수를 예제와 함께 살펴봅니다.
6부 프로세스 간 통신 17장 프로세스 실행: 새로운 프로세스를 생성 및 스폰할 수 있는 방법을 설명합니다. 또한 여러 프로세
●
스 사이에서 상태를 공유하는 푸시, 풀 기반 기법에 관해 논의해보고 14장에서 다뤘던 동시성 문제를 실제 C 사례로 살펴봅니다. 18장 프로세스 동기화: 동일한 머신에 탑재된 여러 프로세스를 동기화할 때 이용할 수 있는 메커니즘을
●
다룹니다. 프로세스-공유 세마포어, 프로세스-공유 뮤텍스, 프로세스-공유 조건 변수 기법을 살펴봅 니다. 19장 싱글 호스트 IPC와 소켓: 푸시 기반 프로세스 간 통신 기법을 다룹니다. 동일한 머신에 탑재된 프
●
로세스에서 사용할 수 있는 기법에 주로 초점을 맞춥니다. 소켓 프로그래밍을 소개하고, 네트워크에 있 는 서로 다른 노드에 탑재된 프로세스 사이에 채널을 설정할 때 필요한 배경도 소개합니다. 20장 소켓 프로그래밍: 예제를 통해 소켓 프로그래밍을 살펴봅니다. 여러 다양한 종류의 소켓을 지원하
●
는 예제를 살펴보며 논의를 지속해봅니다. 스트림 또는 데이터그램 채널에서 작동하는 유닉스 도메인 소켓, TCP, UDP 소켓을 설명합니다. 21장 다른 언어와의 통합: 공유 목적 파일로 빌드된 C 라이브러리가 로드되는 방식과 C++, 자바, 파이
●
썬, 고로 작성한 프로그램에서 해당 라이브러리가 사용되는 방식을 설명합니다.
13
7부 테스트와 유지 보수 22장 유닛 테스트와 디버깅: 테스트와 디버깅을 설명합니다. 여러 수준의 테스트를 소개하며, 특히 C
●
의 유닛 테스트 중심으로 설명합니다. C에서 테스트 스위트를 작성할 때 사용할 수 있는 라이브러리인 CMocka와 구글 테스트를 소개합니다. 디버깅의 개념과 다양한 버그를 디버깅할 때 사용할 수 있는 여 러 도구도 살펴봅니다. 23장 빌드 시스템: 빌드 시스템 및 빌드 스크립트 생성기에 관해 설명합니다. 메이크 Make, 닌자 Ninja, 바
●
젤 Bazel 빌드 시스템과 빌드 스크립트 생성기인 CMake를 소개합니다.
활용 방법 앞서 설명한 대로 이 책을 읽으려면 컴퓨터 프로그래밍과 관련한 최소한의 지식과 기술이 필요 합니다. 최소한의 요구 사항은 다음과 같습니다. 컴퓨터 아키텍처 지식: 메모리, CPU, 주변 장치와 그 특성을 알아야 합니다. 그리고 프로그램이 컴퓨터
●
시스템에서 이러한 요소와 어떻게 상호작용하는지도 알아야 합니다. 프로그래밍 기초 지식: 알고리듬이 무엇인지, 알고리듬의 실행을 어떻게 추적하는지, 소스 코드란 무엇
●
인지, 이진수는 무엇인지, 이와 관련된 수학 개념을 알아야 합니다. 터미널과 셸 명령어 사용법: 리눅스나 macOS 같은 유닉스 계열 운영체제의 터미널과 기초 셸 명령어
●
에 익숙해야 합니다. 프로그래밍 중급 지식: 최소한 하나의 프로그래밍 언어에서 사용하는 조건문, 여러 종류의 루프, 구조체
●
또는 클래스, C나 C++의 포인터, 함수 등과 같은 프로그래밍 주제에 대한 중급 지식이 필요합니다. 객체지향 프로그래밍 기초 지식: 객체지향 프로그래밍에 관해서는 이 책에서 상세히 다룰 예정이니 필
●
수는 아닙니다. 하지만 기초 지식을 알고 있다면 이 책의 3부 객체지향을 읽을 때 더 잘 이해할 수 있습 니다.
14
NOTE_ 깃허브 저장소에서 코드를 내려받아서 셸 박스에 있는 명령어를 따라 해보기를 바랍니다. 리눅스나
macOS에 설치된 플랫폼을 이용하세요. 다른 POSIX 호환 운영체제에서도 사용할 수 있습니다.2 ●
https://github.com/PacktPublishing/Extreme-C
예제 사용 규칙 이 책에서는 코드 박스와 셸 박스를 사용합니다. 코드 박스는 C 코드 또는 의사코드 pseudo-code 를 담고 있습니다. 코드 박스의 내용을 코드 파일에서 가져올 때는 코드 파일의 이름이 박스 위 에 적혀 있습니다. 코드 박스 예제는 다음과 같습니다. 코드 박스 17-1 fork API로 자식 프로세스 생성하기(ExtremeC_examples_chapter17_1.c)
#include #include int main(int argc, char** argv) { printf("This is the parent process with process ID: %d\n", getpid()); printf("Before calling fork() ...\n"); pid_t ret = fork(); if (ret) { printf("The child process is spawned with PID: %d\n", ret); } else { printf("This is the child process with PID: %d\n", getpid()); } printf("Type CTRL+C to exit ...\n");
2 옮긴이_ 역자의 실행 환경은 우분투 18.04.6 LTS, 설치된 gcc 버전은 7.5.0입니다. 우분투는 Virtual Box에 설치했습니다. 2022년 8월 현재, 이 버전의 우분투에서 gcc 설치 시 7.5.0 버전이 선택됩니다. 필자는 gcc 7.3.0으로 실행한 결과를 제시했으나, 우 분투 18.04 LTS 환경에서 gcc 7.3.0 설치를 시도하면 7.5.0 이상 버전 설치가 필요하다는 경고가 나옵니다. 따라서 필자와 완벽히 동 일한 실행 환경을 구성하기 어려운 점을 감안해 역자의 실행 환경은 현재 대부분의 독자가 설정에 큰 어려움이 없을 gcc 7.5.0으로 택했 습니다.
15
while (1); return 0; }
이 코드는 ExtremeC_examples_chapter17_1.c 파일에 있습니다. 앞서 소개한 깃허브에 서 코드 묶음을 다운로드한 뒤 ch17-process-execution 디렉터리를 살펴보면 해당 코드 가 있습니다. 만약 코드 박스에 파일명이 없다면, 코드 묶음에서 찾을 수 없는 의사코드나 C 코드입니다. 아 래 예제를 봅시다. 코드 박스 13-1 다섯 개 명령어가 있는 간단한 작업
작업 P 1. 2. 3. 4. 5. }
{ num = 5 num++ num = num – 2 x = 10 num = num + x
코드 박스 안에는 가끔 밑줄을 치거나 굵은 글자로 표시한 것이 있습니다. 이러한 밑줄은 코드 박스 이전에 다뤘거나 이후에 다룰 코드 행입니다. 여러분의 편의를 위해 굵게 표시했습니다. 셸 박스는 여러 셸 명령어를 실행하는 동안 터미널에서 출력된 결과를 확인할 수 있습니다. 명 령어는 주로 굵은 글자로 표시되며 결과는 일반 폰트로 표시합니다. 아래 예제를 봅시다.
16
셸 박스 17-6 [예제 17-4]에서 생성한 공유 메모리 객체를 읽고 마지막으로 제거하기
$ ls /dev/shm shm0 $ gcc ExtremeC_examples_chapter17_5.c -lrt -o ex17_5.out $ ./ex17_5.out Shared memory is opened with fd: 3 The contents of the shared memory object: ABC $ ls /dev/shm $
명령어는 $ 또는 #로 시작합니다. $로 시작하는 명령어는 일반 사용자 권한으로 실행하고 #로 시작하는 명령어는 슈퍼유저로 실행합니다. 셸 박스의 작업 디렉터리는 대부분 해당 장의 디렉터리와 동일합니다. 작업 디렉터리에서 특정 디렉터리를 찾을 때는 필요한 정보를 함께 제공하니 걱정하지 마세요.
17
감사의 말
어머니 이터람 Ehteram에게 감사를 전합니다. 어머니께서는 헌신적으로 저와 형제 아슈칸 Ashkan 을 키우셨습니다. 어머니는 언제나 저를 응원하고 계신다고 확신합니다. 그리고 제 아름답고 사랑스러운 아내 아프사네 Afsaneh에게도 고마움을 전합니다. 아내는 이 책을 작업하는 내내 저 를 지원해주었습니다. 아내의 인내와 격려가 없었다면 여기까지 해내지 못했을 겁니다. 캄란 아미니
18
CONTENTS
베타리더 후기 ..................................................................................................................
4
지은이·옮긴이 소개 .........................................................................................................
6
리뷰어 소개 ....................................................................................................................
7
옮긴이의 말 ....................................................................................................................
8
이 책에 대하여 ................................................................................................................
9
감사의 말 ....................................................................................................................
PART
18
I C 프로젝트 빌드
CHAPTER
1 필수 요소 1.1 전처리기 지시자 .......................................................................................................... 37 1.1.1 매크로 ................................................................................................................ 38 1.1.2 조건부 컴파일 ...................................................................................................... 53
1.2 포인터 변수 ................................................................................................................ 57 1.2.1 문법 ................................................................................................................... 57 1.2.2 포인터 변수의 산술연산 ......................................................................................... 60 1.2.3 제네릭 포인터 ...................................................................................................... 64 1.2.4 포인터 크기 ......................................................................................................... 66 1.2.5 허상 포인터 ......................................................................................................... 67
1.3 함수 ........................................................................................................................... 70 1.3.1 함수의 구조 ......................................................................................................... 70 1.3.2 설계의 중요성 ...................................................................................................... 71 1.3.3 스택 관리 ............................................................................................................ 72 1.3.4 값에 의한 전달 vs 참조에 의한 전달 ......................................................................... 73
1.4 함수 포인터 ................................................................................................................ 76
19
CONTENTS
1.5 구조체 ........................................................................................................................ 79 1.5.1 왜 구조체인가? .................................................................................................... 79 1.5.2 왜 사용자 정의 자료형인가? ................................................................................... 80 1.5.3 구조체의 역할 ..................................................................................................... 81 1.5.4 메모리 레이아웃 ................................................................................................... 82 1.5.5 중첩 구조체 ......................................................................................................... 87 1.5.6 구조체 포인터 ...................................................................................................... 88
1.6 마무리 ........................................................................................................................ 90
CHAPTER
2 소스 코드에서 이진 파일로 2.1 표준 컴파일 파이프라인 ............................................................................................... 94 2.1.1 프로젝트 빌드하기 ................................................................................................ 96 2.1.2 1단계: 전처리 . ................................................................................................... 103 2.1.3 2단계: 컴파일 . ................................................................................................... 105 2.1.4 3단계: 어셈블리 .................................................................................................. 109 2.1.5 4단계: 링크 ....................................................................................................... 112
2.2 전처리기 ................................................................................................................... 115 2.3 컴파일러 ................................................................................................................... 120 2.3.1 추상 구문 트리 ................................................................................................... 121
2.4 어셈블러 ................................................................................................................... 123 2.5 링커 ......................................................................................................................... 124 2.5.1 링커의 작동 방식 ................................................................................................ 126 2.5.2 링커는 속을 수 있다 ............................................................................................ 135 2.5.3 C++ 네임 맹글링 ................................................................................................ 140
2.6 마무리 ...................................................................................................................... 142
20
CHAPTER
3 목적 파일 3.1 ABI .......................................................................................................................... 144 3.2 목적 파일 형식 .......................................................................................................... 146 3.3 재배치 가능한 목적 파일 ............................................................................................ 148 3.4 실행 가능한 목적 파일 ............................................................................................... 153 3.5 정적 라이브러리 ........................................................................................................ 158 3.6 동적 라이브러리 ........................................................................................................ 168 3.6.1 공유 라이브러리의 수동 로딩 ................................................................................ 173
3.7 마무리 ...................................................................................................................... 176
PART
II 메모리
CHAPTER
4 프로세스 메모리 구조 4.1 프로세스 메모리 레이아웃 .......................................................................................... 180 4.2 메모리 구조 알아보기 ................................................................................................ 182 4.3 정적 메모리 레이아웃 검사하기 .................................................................................. 183 4.3.1 BSS 세그먼트 ................................................................................................... 185 4.3.2 데이터 세그먼트 ................................................................................................. 188 4.3.3 텍스트 세그먼트 ................................................................................................. 192
4.4 동적 메모리 레이아웃 검사하기 .................................................................................. 195 4.4.1 메모리 매핑 ....................................................................................................... 196 4.4.2 스택 세그먼트 .................................................................................................... 201 4.4.3 힙 세그먼트 ....................................................................................................... 203
4.5 마무리 ...................................................................................................................... 207
21
CONTENTS
CHAPTER
5 스택과 힙 5.1 스택 ......................................................................................................................... 210 5.1.1 스택 검사하기 .................................................................................................... 211 5.1.2 스택 메모리 사용 시 주의점 .................................................................................. 219
5.2 힙 ............................................................................................................................ 224 5.2.1 힙 메모리의 할당과 해제 ...................................................................................... 225 5.2.2 힙 메모리 원칙 ................................................................................................... 235
5.3 제한된 환경에서의 메모리 관리 .................................................................................. 239 5.3.1 메모리가 제한된 환경 .......................................................................................... 240 5.3.2 성능이 더 나은 환경 ............................................................................................ 242
5.4 마무리 ...................................................................................................................... 250
PART
III 객체지향
CHAPTER
6 OOP와 캡슐화 6.1 객체지향적 사고 ........................................................................................................ 256 6.1.1 정신적 개념 ....................................................................................................... 256 6.1.2 마인드맵과 객체 모델 .......................................................................................... 258 6.1.3 코드에는 없는 객체 ............................................................................................. 260 6.1.4 객체 속성 .......................................................................................................... 262 6.1.5 도메인 .............................................................................................................. 262 6.1.6 객체 사이의 관계 ................................................................................................ 263 6.1.7 객체지향 작업 .................................................................................................... 264 6.1.8 행위를 갖는 객체 ................................................................................................ 267
22
6.2 C가 객체지향이 아닌 이유 ......................................................................................... 268 6.3 캡슐화 ...................................................................................................................... 269 6.3.1 속성 캡슐화 ....................................................................................................... 269 6.3.2 행위 캡슐화 ....................................................................................................... 272 6.3.3 정보 은닉 .......................................................................................................... 284
6.4 마무리 ...................................................................................................................... 292
CHAPTER
7 합성과 집합 7.1 클래스 간의 관계 ...................................................................................................... 296 7.2 객체 대 클래스 .......................................................................................................... 296 7.3 합성 ......................................................................................................................... 298 7.4 집합 ......................................................................................................................... 306 7.5 마무리 ...................................................................................................................... 313
CHAPTER
8 상속과 다형성 8.1 상속 ......................................................................................................................... 316 8.1.1 상속의 본질 ....................................................................................................... 317
8.2 다형성 ...................................................................................................................... 335 8.2.1 다형성 소개 ....................................................................................................... 335 8.2.2 다형성이 필요한 이유 .......................................................................................... 339 8.2.3 C에서 다형적 행위를 갖는 방법 ............................................................................. 339
8.3 마무리 ...................................................................................................................... 348
23
CONTENTS
CHAPTER
9 추상화와 C++의 OOP 9.1 추상화 ...................................................................................................................... 351 9.2 C++의 객체지향 구성물 ............................................................................................ 356 9.2.1 캡슐화 .............................................................................................................. 356 9.2.2 상속 ................................................................................................................. 360 9.2.3 다형성 .............................................................................................................. 367 9.2.4 추상 클래스 ....................................................................................................... 370
9.3 마무리 ...................................................................................................................... 372
PART
IV 유닉스
CHAPTER
10 유닉스의 역사와 아키텍처 10.1 유닉스의 역사 ..................................................................................................... 376 10.1.1 멀틱스와 유닉스 . ....................................................................................... 376 10.1.2 BCPL과 B ............................................................................................... 378 10.1.3 C로 향하는 길 ........................................................................................... 379
10.2 유닉스 아키텍처 .................................................................................................. 381 10.2.1 철학 ........................................................................................................ 382 10.2.2 유닉스 링 ................................................................................................. 384
10.3 사용자 응용프로그램에 대한 셸 인터페이스 ........................................................... 386 10.4 셸 링에 대한 커널 인터페이스 .............................................................................. 391 10.5 커널 ................................................................................................................... 398 10.6 하드웨어 ............................................................................................................. 402 10.7 마무리 ................................................................................................................ 405
24
CHAPTER
11 시스템 호출과 커널 11.1 시스템 호출 ......................................................................................................... 408 11.1.1 시스템 호출 자세히 보기 .............................................................................. 408 11.1.2 표준 C 건너뛰기: 직접 시스템 호출하기 .......................................................... 410 11.1.3 syscall 함수의 내부 ................................................................................... 413 11.1.4 리눅스에 시스템 호출 추가하기 ..................................................................... 415
11.2 유닉스 커널 ......................................................................................................... 430 11.2.1 모놀리식 커널 대 마이크로커널 ..................................................................... 431 11.2.2 리눅스 ..................................................................................................... 432 11.2.3 커널 모듈 ................................................................................................. 433
11.3 마무리 ................................................................................................................ 440
CHAPTER
12 최신 C 12.1 C11 ................................................................................................................... 442 12.2 C 표준 지원 버전 찾기 ......................................................................................... 443 12.3 gets 함수의 제거 ................................................................................................. 444 12.4 fopen 함수로의 변화 ........................................................................................... 445 12.5 경계 검사 함수 .................................................................................................... 447 12.6 값을 반환하지 않는 함수 ...................................................................................... 448 12.7 타입 제네릭 매크로 .............................................................................................. 449 12.8 유니코드 ............................................................................................................. 450 12.9 익명 구조체와 익명 공용체 ................................................................................... 457 12.10 멀티스레딩 ....................................................................................................... 459 12.11 마무리 .............................................................................................................. 459
25
CONTENTS
PART
V 동시성
CHAPTER
13 동시성 13.1 동시성 소개 ......................................................................................................... 464 13.2 병렬성 ................................................................................................................ 465 13.3 동시성 ................................................................................................................ 467 13.4 작업 스케줄러 유닛 .............................................................................................. 468 13.5 프로세스와 스레드 ............................................................................................... 469 13.6 발생 전 제약 ....................................................................................................... 471 13.7 동시성을 사용해야 하는 경우 ............................................................................... 473 13.8 공유 상태 ............................................................................................................ 481 13.9 마무리 ................................................................................................................ 487
CHAPTER
14 동기화 14.1 동시성 문제 ......................................................................................................... 490 14.2 고유한 동시성 문제 .............................................................................................. 491 14.3 동기화 이후 문제 ................................................................................................. 502 14.4 동기화 기술 ......................................................................................................... 503 14.4.1 바쁜 대기 및 스핀락 .................................................................................... 504 14.4.2 잠자기/알림 메커니즘 ................................................................................. 507 14.4.3 세마포어와 뮤텍스 ...................................................................................... 511 14.4.4 멀티프로세서 유닛 ...................................................................................... 516
14.5 스핀락 ................................................................................................................ 521 14.5.1 조건 변수 ................................................................................................. 523
26
14.6 POSIX의 동시성 .................................................................................................. 525 14.6.1 동시성을 지원하는 커널 ............................................................................... 526
14.7 멀티프로세싱 ....................................................................................................... 528 14.8 멀티스레딩 .......................................................................................................... 531 14.9 마무리 ................................................................................................................ 532
CHAPTER
15 스레드 실행 15.1 스레드 ................................................................................................................ 537 15.2 POSIX 스레드 ..................................................................................................... 540 15.3 POSIX 스레드 스폰하기 ....................................................................................... 542 15.4 경쟁 상태에 대한 예제 ......................................................................................... 548 15.5 데이터 경쟁에 대한 예 ......................................................................................... 557 15.6 마무리 ................................................................................................................ 561
CHAPTER
16 스레드 동기화 16.1 POSIX 동시성 제어 ............................................................................................. 564 16.1.1 POSIX 뮤텍스 .......................................................................................... 564 16.1.2 POSIX 조건 변수 ...................................................................................... 568 16.1.3 POSIX 장벽 ............................................................................................. 572 16.1.4 POSIX 세마포어 ....................................................................................... 575
16.2 POSIX 스레드와 메모리 ....................................................................................... 584 16.2.1 스택 메모리 .............................................................................................. 585 16.2.2 힙 메모리 ................................................................................................. 590 16.2.3 메모리 가시성 ........................................................................................... 596
16.3 마무리 ................................................................................................................ 598
27
CONTENTS
PART
VI 프로세스 간 통신
CHAPTER
17 프로세스 실행 17.1 프로세스 실행 API ............................................................................................... 603 17.1.1 프로세스 생성 ........................................................................................... 607 17.1.2 프로세스 실행 ........................................................................................... 612 17.1.3 프로세스 생성과 실행 비교하기 ..................................................................... 615
17.2 프로세스 실행 단계 .............................................................................................. 616 17.3 공유 상태 ............................................................................................................ 617 17.3.1 공유 기술 ................................................................................................. 618 17.3.2 POSIX 공유 메모리 ................................................................................... 620 17.3.3 파일 시스템 .............................................................................................. 631
17.4 멀티스레딩 대 멀티프로세싱 ................................................................................. 633 17.4.1 멀티스레딩 ............................................................................................... 633 17.4.2 싱글 호스트 멀티프로세싱 ............................................................................ 634 17.4.3 분산 멀티프로세싱 ...................................................................................... 635
17.5 마무리 ................................................................................................................ 636
CHAPTER
18 프로세스 동기화 18.1 싱글 호스트 동시성 제어 ...................................................................................... 638 18.2 기명 POSIX 세마포어 .......................................................................................... 639 18.3 기명 뮤텍스 ......................................................................................................... 643 18.3.1 첫 번째 예제 ............................................................................................. 644 18.3.2 두 번째 예제 ............................................................................................. 648
28
18.4 기명 조건 변수 .................................................................................................... 659 18.4.1 공유 메모리의 클래스 .................................................................................. 660 18.4.2 공유된 32비트 정수 카운터의 클래스 .............................................................. 664 18.4.3 공유 뮤텍스의 클래스 .................................................................................. 666 18.4.4 공유 조건 변수의 클래스 .............................................................................. 670 18.4.5 메인 로직 ................................................................................................. 674
18.5 분산된 동시성 제어 .............................................................................................. 680 18.6 마무리 ................................................................................................................ 682
CHAPTER
19 싱글 호스트 IPC와 소켓 19.1 IPC 기법 ............................................................................................................. 684 19.2 통신 프로토콜 ..................................................................................................... 686 19.2.1 프로토콜 특징 ........................................................................................... 689
19.3 싱글 호스트 통신 ................................................................................................. 691 19.3.1 파일 서술자 .............................................................................................. 692 19.3.2 POSIX 신호 ............................................................................................. 693 19.3.3 POSIX 파이프 .......................................................................................... 697 19.3.4 POSIX 메시지 큐 ...................................................................................... 700 19.3.5 유닉스 도메인 소켓 ..................................................................................... 703
19.4 소켓 프로그래밍 .................................................................................................. 704 19.4.1 컴퓨터 네트워크 . ....................................................................................... 704 19.4.2 소켓 프로그래밍 소개 .................................................................................. 718
19.5 마무리 ................................................................................................................ 724
29
CONTENTS
CHAPTER
20 소켓 프로그래밍 20.1 소켓 프로그래밍 복습 .......................................................................................... 728 20.2 계산기 프로젝트 .................................................................................................. 730 20.2.1 소스 계층 ................................................................................................. 731 20.2.2 프로젝트 빌드하기 ...................................................................................... 735 20.2.3 프로젝트 실행하기 ...................................................................................... 736 20.2.4 응용 프로토콜 ........................................................................................... 737 20.2.5 직렬화/역직렬화 라이브러리 ........................................................................ 741 20.2.6 계산기 서비스 ........................................................................................... 747
20.3 유닉스 도메인 소켓 .............................................................................................. 749 20.3.1 UDS 스트림 서버 ...................................................................................... 749 20.3.2 UDS 스트림 클라이언트 ............................................................................. 758 20.3.3 UDS 데이터그램 서버 ................................................................................ 761 20.3.4 UDS 데이터그램 클라이언트 ....................................................................... 766
20.4 네트워크 소켓 ..................................................................................................... 769 20.4.1 TCP 서버 ................................................................................................. 769 20.4.2 TCP 클라이언트 ........................................................................................ 771 20.4.3 UDP 서버 ................................................................................................ 772 20.4.4 UDP 클라이언트 ....................................................................................... 773
20.5 마무리 ................................................................................................................ 774
CHAPTER
21 다른 언어와의 통합 21.1 통합이 가능한 이유 .............................................................................................. 778 21.2 필수 자료 획득하기 .............................................................................................. 779 21.3 스택 라이브러리 .................................................................................................. 780
30
21.4 C++과 통합하기 ................................................................................................. 788 21.4.1 C++의 네임 맹글링 .................................................................................... 788 21.4.2 C++ 코드 ................................................................................................. 791
21.5 자바와 통합하기 .................................................................................................. 796 21.5.1 자바 부분 작성하기 ..................................................................................... 797 21.5.2 네이티브 부분 작성하기 ............................................................................... 802
21.6 파이썬과 통합하기 ............................................................................................... 812 21.7 고와 통합하기 ..................................................................................................... 816 21.8 마무리 ................................................................................................................ 819
PART
VII 테스트와 유지 보수
CHAPTER
22 유닛 테스트와 디버깅 22.1 소프트웨어 테스트 ............................................................................................... 824 22.1.1 테스트 수준 .............................................................................................. 825
22.2 유닛 테스트 ......................................................................................................... 827 22.2.1 테스트 더블 .............................................................................................. 836
22.3 컴포넌트 테스트 .................................................................................................. 838 22.4 C의 테스트 라이브러리 ........................................................................................ 839 22.4.1 CMocka ................................................................................................. 840 22.4.2 구글 테스트 .............................................................................................. 851
22.5 디버깅 ................................................................................................................ 856 22.5.1 버그의 종류 .............................................................................................. 858 22.5.2 디버거 ..................................................................................................... 859 22.5.3 메모리 검사기 ........................................................................................... 860
31
CONTENTS
22.5.4 스레드 디버거 ........................................................................................... 862 22.5.5 퍼포먼스 프로파일러 ................................................................................... 863
22.6 마무리 ................................................................................................................ 864
CHAPTER
23 빌드 시스템 23.1 빌드 시스템 소개 ................................................................................................. 866 23.2 메이크 ................................................................................................................ 868 23.3 CMake: 빌드 시스템이 아닙니다 .......................................................................... 876 23.4 닌자 ................................................................................................................... 882 23.5 바젤 ................................................................................................................... 884 23.6 빌드 시스템 비교하기 .......................................................................................... 888 23.7 마무리 ................................................................................................................ 889
32
에필로그 .......................................................................................................................
890
찾아보기 .......................................................................................................................
891
Part
I
C 프로젝트 빌드
1장 필수 요소: C에서 찾아볼 수 있는 필수 요소를 소개합니다. 이 요소들은 C를 사용하는 방식에 중대한 영
향을 미칩니다. 이 책 전반에 걸쳐 이러한 기능을 자주 사용할 예정입니다. 주요 주제는 전처리 및 매크로를 정 의하는 지시자, 변수와 함수 포인터, 함수 호출 메커니즘 그리고 구조체입니다. 2장 소스 코드에서 이진 파일로: C 프로젝트를 빌드하는 방법에 관해 설명합니다. 전체 파이프라인 및 개별
파이프라인 구성 요소의 관점에서 컴파일 파이프라인을 상세히 배워봅니다. 3장 목적 파일: 컴파일 파이프라인을 사용해 빌드한 이후의 C 프로젝트 결과물을 살펴봅니다. 목적 파일과 그
다양한 종류를 소개합니다. 또한 목적 파일 내부를 들여다보고 어떤 정보를 추출할 수 있는지 알아봅니다.
장
33
Part I
C 프로젝트 빌드
1장 필수 요소 2장 소스 코드에서 이진 파일로 3장 목적 파일
34
실전 자바 소프트웨어 개발
CHAPTER
1
필수 요소
이 책은 C를 이용하는 애플리케이션의 개발과 유지 보수에 필요한 기초와 고급 지식을 모두 다 룹니다. 일반적으로 프로그래밍 언어의 문법만 안다고 해서 성공적인 프로그램을 작성하기란 어렵습니다. C는 다른 언어에 비해 이 점이 더 중요합니다. 이 책에서는 대규모 소프트웨어를
C로 작성하는 데 필요한 모든 개념을 살펴봅니다. 간단한 단일 프로세스 프로그램부터 복잡한 멀티프로세스 시스템에 이르기까지 말입니다. 첫 번째 장은 C 프로그램을 작성할 때 매우 유용한 C의 특정 요소를 주로 다룹니다. 이 요소는
C로 작성할 때 자주 마주치게 될 상황과 관련됩니다. C의 모든 것을 상세히 설명하고 문법의 거의 모든 측면을 다루는 좋은 책과 튜토리얼은 많지만, 우리가 C를 더 깊게 들여다보기 전에 몇 가지 주요 특성을 먼저 알아보는 편이 더 좋습니다. 이러한 구성 요소에는 전처리기 지시자, 포인터 변수, 함수 포인터, 구조체가 포함됩니다. 이는 당연히 C보다 더 현대적인 프로그래밍 언어인 자바, C#, 파이썬 등에서도 흔히 볼 수 있는 요 소입니다. 예를 들어 자바의 참조 reference는 C의 포인터 변수와 유사합니다. 이러한 요소 및 관 련 개념은 매우 기초적이며, 이런 요소가 없다면 그 어떠한 소프트웨어도 실행될 수 없습니다. 함수 포인터를 사용해야 하는 여러 공유 라이브러리를 로딩하지 않고서는 단순한 ‘hello world’
프로그램조차도 실행할 수 없습니다. 따라서 신호등, 자동차의 중앙 컴퓨터(CPU ), 주방의 전자레인지, 스마트폰의 운영체제, 별생 각 없이 봤던 다른 장치 모두 C로 작성된 소프트웨어를 사용합니다.
1장 필수 요소
35
오늘날 우리의 삶은 C로부터 큰 영향을 받고 있으며 C가 없다면 세상은 지금과 매우 다를 겁 니다.
1장은 전문적인 C 코드를 작성하기 위한 필수 요소와 관련 개념에 초점을 맞춥니다. 그리고 심 도 있는 학습을 위해 엄선한 요소도 함께 다룹니다. 전처리기 지시자, 매크로, 조건부 컴파일: 전처리는 다른 프로그래밍 언어에서 쉽게 찾아볼 수 없는 C의
●
특성 중 하나입니다. 전처리에는 많은 이점이 있습니다. 매크로와 조건부 지시자를 포함한 흥미로운 활 용법을 살펴봅시다. 포인터 변수: 1.2절에서는 포인터 변수와 사용법을 알아봅니다. 포인터 변수를 잘못 사용해서 생기는 몇
●
가지 문제를 해결할 때 도움이 될 만한 지식을 얻을 수 있습니다. 함수: 1.3절에서는 문법 수준을 넘어 함수에 관한 모든 부분을 자세히 살펴봅니다. 문법은 사실 쉬운 부
●
분입니다! 이 절에서 함수를 절차적 procedural 코드를 작성하기 위한 구성 요소로 간주합니다. 여기서는 또한 함수 호출 메커니즘과 함수가 호출자 함수 the caller function로부터 인수 argument를 받는 방식에 관해 서도 설명합니다. 함수 포인터: 함수 포인터는 C의 중요한 특성 중 하나입니다. 함수 포인터는 변수 대신 기존의 함수
●
를 가리키는 포인터입니다. 기존의 로직에 포인터를 저장하는 능력은 알고리듬 설계에서 매우 중요합 니다. 따라서 한 절을 할애해 함수 포인터를 살펴봅니다. 함수 포인터는 동적 라이브러리 로딩부터 다형 성 polymorphism에 이르기까지 매우 다양하게 활용됩니다. 훨씬 더 많은 함수 포인터에 관해서는 다음 몇 장에 걸쳐 알아봅니다. 구조체: C 구조체는 문법이 단순하며, 간단한 아이디어를 표현할 수 있습니다. 하지만 잘 조직된 객체지
●
향 코드를 작성하기 위한 핵심 구성 요소이기도 합니다. 함수 포인터와 더불어 구조체의 중요성도 강조 하지 않을 수 없습니다. 이 장의 마지막 절에서 C의 구조체에 관해 알아야 할 모든 것과 구조체와 관련된 요령을 다시 살펴보겠습니다.
C의 필수 요소와 개념은 유닉스 생태계에서 중요한 역할을 합니다. C는 오래되고 문법이 까다 로운데도 불구하고, 이러한 이유로 인해 여전히 중요하고 영향력이 있습니다. C와 유닉스가 서 로 주고받은 영향에 관해서는 다음 장에서 이야기합니다. 우선 전처리기 지시자를 살펴보는 걸 로 시작해봅시다.
36
1부 C 프로젝트 빌드
NOTE_ 1장을 읽기 전 C에 이미 익숙한 상태여야 한다는 것을 명심하세요. 이번 장의 예제 대부분은 어렵
지 않지만, 다른 장으로 넘어가기 전에 C의 문법을 알고 있기를 권장합니다. 다음은 이 책을 시작하기 전에 잘 알고 있어야 할 주제들입니다. ●
컴퓨터 아키텍처 지식: 메모리, CPU, 주변 장치와 이들 특성에 관해 알아야 하며 컴퓨터 시스템에서 이러한 요소와 프로그램이 어떻게 상호작용하는지도 알아야 합니다.
●
프로그래밍 기초 지식: 알고리듬이 무엇인지, 알고리듬의 실행을 어떻게 추적하는지, 소스 코드란 무엇 이며 이진수는 무엇인지, 이와 관련한 산술연산은 어떻게 작동하는지 알고 있어야 합니다.
●
터미널과 셸 명령어 사용법: 리눅스나 macOS 같은 유닉스 계열 운영체제의 터미널과 기초 셸 명령어 에 익숙해야 합니다.
●
프로그래밍 중급 지식: 최소한 하나의 프로그래밍 언어에서 사용하는 조건문, 여러 종류의 루프, 구조체 나 클래스, C 또는 C++의 포인터, 함수 등과 같은 주제에 관해 중급 지식을 갖추고 있어야 합니다.
●
객체지향 프로그래밍 기초 지식: 객체지향 프로그래밍에 관해서는 이 책에서 상세히 다룰 예정이니 필 수는 아닙니다. 하지만 기초 지식을 알고 있다면 3부 객체지향을 읽을 때 이해하기 더 쉬울 겁니다.
1.1 전처리기 지시자 전처리 preprocessing는 C의 강력한 구성 요소입니다. 2장에서 전처리를 충분히 살펴볼 예정이니
지금은 컴파일러로 보내기 전 소스 코드를 만들고 수정할 수 있도록 하는 과정을 전처리라고 정의하겠습니다. 이는 C의 컴파일 파이프라인이 다른 언어에 비해 최소 한 단계 이상을 더 갖 는다는 뜻입니다. 다른 프로그래밍 언어에서는 컴파일러로 소스 코드가 바로 전달되지만 C와
C++에서는 먼저 전처리를 해야 합니다. 전처리라는 추가 단계는 컴파일러로 소스 코드를 보내기 전 C 개발자가 효과적으로 소스 코드 를 바꿀 수 있다는 점에서 C (그리고 C++)를 독특한 프로그래밍 언어로 만들었습니다. 전처리 요소는 대부분의 고급 프로그래밍 언어에는 존재하지 않습니다. 전처리의 목적은 전처리 지시자를 제거하고 이 지시자를 C 코드에 의해 생성된 동일한 내용으 로 바꾸는 것입니다. 또한 컴파일러로 보낼 준비가 된 최종 소스 파일을 준비하려는 목적도 있 습니다.
1장 필수 요소
37
C 전처리기의 수행은 일련의 지시자 directive를 사용해 통제하고 영향을 줄 수 있습니다. C 지시자 는 헤더와 소스 파일 모두에서 # 문자로 시작하는 코드입니다. 이 코드는 C 전처리기에만 의미 가 있을 뿐 C 컴파일러에는 의미가 없습니다. C에는 다양한 지시자가 있는데, 그중에서도 특히 매크로를 정의할 때 사용되는 지시자와 조건부 컴파일에 사용되는 지시자가 매우 중요합니다. 다음 절에서 매크로를 알아보고, 매크로가 다양하게 사용되는 예제를 살펴봅니다. 또한 매크로 를 분석해 매크로의 장단점을 알아봅니다.
1.1.1 매크로 많이들 C 매크로를 오해하고 있습니다. 누군가는 매크로로 인해 소스 코드가 너무 복잡해져 가 독성이 떨어진다고 주장하고, 또 다른 누군가는 코드에 매크로를 사용할 경우 응용프로그램을 디버깅할 때 문제가 생긴다고 이야기합니다. 아마 이런 이야기를 직접 들어본 적도 있을 것입 니다. 하지만 어디까지가 맞는 말일까요? 매크로는 피해야 할 악일까요? 아니라면 프로젝트에 도움이 되는 이점이 있을까요? 잘 알려진 C 프로젝트라면 어느 것이든 매크로를 찾아볼 수 있다는 게 현실입니다. 그 증거로 아파치 HTTP 서버와 같은 유명한 C 프로젝트를 다운받은 뒤 grep1으로 #define을 검색해 보세요. 매크로가 사용된 파일의 목록이 보입니다. C 개발자로서 매크로에서 벗어날 방법은 없 습니다. 여러분이 매크로를 사용하지 않더라도 다른 사람의 코드에서 발견하게 될 것입니다. 그러므로 매크로가 무엇이고 이를 어떻게 다루는지 알아야 합니다.
NOTE_ grep 명령어는 문자열에서 패턴을 검색하는 유닉스 계열의 표준 셸 유틸리티 프로그램과 관련이
있습니다. 이 명령어는 해당 경로에서 모든 파일 내용의 문자열 또는 패턴을 검색할 때 사용합니다.
매크로는 다음과 같이 다양하게 활용할 수 있습니다. 상수 정의하기
●
C 함수를 작성하지 않고 함수로 사용하기
●
1 옮긴이_ 유닉스/리눅스 문자열 검색 명령어
38
1부 C 프로젝트 빌드
루프 풀기 loop unrolling
●
헤더 가드 header guard
●
코드 생성
●
조건부 컴파일
●
매크로는 더 다양하게 활용할 수 있지만, 다음 절에서는 이 항목들을 주로 살펴보겠습니다.
매크로 정의하기 매크로는 #define 지시자를 이용해 정의합니다. 각 매크로는 이름과 사용 가능한 매개변 수 parameter 리스트를 갖습니다. 또한 값 value을 가지며, 이 값은 매크로 확장이라는 단계를 통해 전처리 단계에서 매크로의 이름으로 대체될 수 있습니다. 매크로는 #undef 지시자로 매크로 의 정의를 제거할 수 있습니다. 간단한 예제인 [예제 1-1]부터 시작해봅시다. 코드 박스 1-1 [예제 1-1] 매크로 정의하기(ExtremeC_examples_chapter1_1.c)2
#define ABC 5 int main(int argc, char** argv) { int x = 2; int y = ABC; int z = x + y; return 0; }
이 [코드 박스 1-1]에서 ABC는 정숫값이나 정수 상수 integer constant를 담는 변수가 아닙니다. 이 는 ABC라는 매크로이며 해당하는 값은 5입니다. 매크로 확장 단계 이후 C 컴파일러로 보낸 코 드의 결과물은 다음과 같습니다. 코드 박스 1-2 매크로 확장 단계 이후 [예제 1-1]에서 생성된 코드
int main(int argc, char** argv) { int x = 2; int y = 5; 2 옮긴이_ 번역 시점에는 깃허브에서 받은 코드 내 주석에 examples가 exampels로 잘못 기재되어 있습니다. ch01부터 ch04까지 이 현상이 존재하니 실습 시 참고해주세요.
1장 필수 요소
39
int z = x + y; return 0; }
[코드 박스 1-2]의 코드는 유효한 C언어 문법이므로 컴파일러에서 코드를 계속 컴파일할 수 있습니다. 앞의 예제에서 전처리기는 매크로 확장을 수행해 매크로의 이름을 값으로 간단히 바 꾸었습니다. 또한 시작하는 줄의 주석도 삭제했습니다. 이제 다른 예제인 [예제 1-2]를 봅시다. 코드 박스 1-3 [예제 1-2] 유사 함수 매크로 정의하기(ExtremeC_examples_chapter1_2.c)
#define ADD(a, b) a + b int main(int argc, char** argv) { int x = 2; int y = 3; int z = ADD(x, y); return 0; }
[코드 박스 1-3]은 [예제 1-1]과 비슷하지만 이 코드에서 ADD는 함수가 아닙니다. 이는 인 수 argument를 받는 유사 함수 매크로 function-like macro입니다. 전처리 이후의 코드 결과는 다음과 같 습니다. 코드 박스 1-4 전처리와 매크로 확장 이후 코드
int main(int argc, char** argv) { int x = 2; int y = 3; int z = x + y; return 0; }
이 코드 박스에서 볼 수 있듯이 매크로 확장이 이루어지는 방식은 다음과 같습니다. 매개변수
a로 사용된 인수 argument x는 매크로 값에 있는 a의 인스턴스로 모두 바뀌었습니다. 매개변수 b 도 마찬가지이며 b에 해당하는 y 인수에서도 그렇습니다. 모두 변환된 이후 전처리된 코드에
40
1부 C 프로젝트 빌드
서는 ADD(a, b) 대신 x + y를 얻습니다. 유사 함수 매크로는 입력 인수를 받을 수 있기 때문에 C 함수를 모방할 수 있습니다. 즉, 자주 사용되는 로직을 C 함수에 넣는 것 대신, 이 로직을 유사 함수 매크로로 명명해 사용할 수 있습 니다. 이처럼 전처리 단계에서 매크로는 자주 사용되는 로직으로 대체되며, C 함수를 새로 작성할 필 요가 없게 됩니다. 이를 더 자세히 알아보고 다음 두 가지 접근 방식을 비교해보겠습니다. 매크로는 컴파일 단계 이전에만 존재합니다. 즉, 컴파일러가 이론적으로는 매크로에 관해 아무 것도 모른다는 의미입니다. 매크로를 함수 대신 사용하려면 이 내용을 꼭 기억해야 합니다. 컴 파일러는 함수의 모든 것을 알고 있습니다. 함수는 C 문법의 일부이며 파스 트리 parse tree 내에 서 함수가 분석되기 때문입니다. 그러나 매크로는 전처리기 자체에 관해서만 알고 있는, C의 전처리기 지시자에 불과합니다. 매크로는 컴파일 이전에 코드를 생성 generate할 수 있도록 합니다. 자바와 같은 다른 프로그래밍 언어에서 이 과정을 처리하기 위해서는 코드 생성기 code generator가 필요합니다. 이런 매크로 적 용에 관해서는 예제를 살펴봅시다. 현대의 C 컴파일러는 C 전처리기 지시자를 인지하고 있습니다. 전처리 단계에 관해 컴파일러 가 아무것도 모른다고 흔히 알고 있지만, 컴파일러는 사실 전처리 단계를 알고 있습니다. 현대 의 C 컴파일러는 전처리 단계에 들어가기 이전의 소스 코드를 알고 있습니다. 다음 코드를 보 시죠. 코드 박스 1-5 선언되지 않은 식별자 오류가 발생하는 매크로 정의(example.c)
#include #define CODE \ printf("%d\n", i); int main(int argc, char** argv) { CODE return 0; }
1장 필수 요소
41
만약 이 코드를 macOS에서 클랭 clang을 이용해 컴파일한다면 결과는 다음과 같습니다. 셸 박스 1-1 매크로 정의를 나타내는 컴파일 출력
$ clang example.c code.c:7:3: error: use of undeclared identifier 'i' CODE ^ code.c:4:16: note: expanded from macro 'CODE' printf("%d\n", i); ^ 1 error generated. $
컴파일러는 매크로가 정의된 바로 그 줄에 오류 메시지를 생성합니다. 참고로 대부분의 현대 컴파일러는 컴파일 직전에 전처리 결과를 볼 수 있습니다. 예를 들어
gcc나 clang을 사용할 경우 전처리 이후 코드를 덤프 dump하기 위해 -E 옵션을 사용할 수 있 습니다. 다음 [셸 박스 1-2]는 -E 옵션의 사용법을 나타냅니다. 결괏값을 모두 싣지는 않았다 는 점을 참고해주세요. 셸 박스 1-2 전처리 단계 이후의 example.c 코드
$ clang -E example.c # 1 "sample.c"# 1 "" 1 # 1 "" 3 # 361 "" 3 ... # 412 "/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/ usr/include/stdio.h" 2 3 4 # 2 "sample.c" 2 ... int main(int argc, char** argv) { printf("%d\n", i); return 0; } $
이제 중요한 정의에 다다랐습니다. 변환 단위 translation unit (혹은 컴파일 단위 compilation unit )는 컴파
42
1부 C 프로젝트 빌드
일러로 전달될 준비가 된, 전처리된 C 코드입니다. 변환 단위에서는 모든 지시자가 포함 inclusion 되거나 매크로 확장으로 대체되며 단 한 줄의 긴 C 코드가 만들어집니다. 매크로에 관해 자세히 알아봤으니 조금 더 어려운 예제를 살펴보겠습니다. 이 예제는 매크로의 위력과 위험을 보여줍니다. 제 생각에는 위험하고 섬세한 것을 능숙하게 다루는 것이 극한의 개발이며, 이것이 바로 C입니다. 흥미로운 다음 예제를 확인해봅시다. 루프를 생성하는 시퀀스에서 매크로가 어떻게 사용되는 지 주목해보세요. 코드 박스 1-6 [예제 1-3] 루프를 생성하는 매크로 사용하기(ExtremeC_examples_chapter1_3.c)
#include #define PRINT(a) printf("%d\n", a); #define LOOP(v, s, e) for (int v = s; v