C(프로그래밍 언어)

(♥ 0)



[ 펼치기 · 접기 ]

기반 학문수학(해석학 · 이산수학 · 수리논리학 · 선형대수학 · 미적분학 · 미분방정식 · 대수학(환론 · 범주론) · 정수론) · 이론 컴퓨터 과학 · 암호학 · 전자공학 · 언어학(형태론 · 통사론 · 의미론 · 화용론 · 음운론) · 인지과학
SoC · CPU · GPU(그래픽 카드 · GPGPU) · ROM · RAM · SSD · HDD · 참조: 틀:컴퓨터 부품
기술기계어 · 어셈블리어 · C/C++ · C\# · Java · Python · BIOS · 절차적 프로그래밍 · 객체 지향 프로그래밍 · 해킹 · ROT13 · 일회용 비밀번호 · 사물인터넷 · 와이파이 · GPS · 임베디드 · 인공신경망 · OpenGL · EXIF · 마이크로아키텍처 · ACPI · UEFI · NERF · gRPC · 리버스 엔지니어링 · HCI · UI · UX · 대역폭 · DBMS · NoSQL · 해시(SHA · 브루트 포스 · 레인보우 테이블 · salt · 암호화폐) · RSA 암호화 · 하드웨어 가속
연구

기타
논리 회로(보수기 · 가산기 · 논리 연산 · 불 대수 · 플립플롭) · 정보이론 · 임베디드 시스템 · 운영 체제 · 데이터베이스 · 프로그래밍 언어{컴파일러(어셈블러 · JIT) · 인터프리터 · 유형 이론 · 파싱 · 링커} · 메타데이터 · 기계학습 · 빅데이터 · 폰노이만 구조 · 양자컴퓨터 · 행위자 모델 · 인코딩(유니코드 · MBCS) · 네트워크 · 컴퓨터 보안 · OCR · 슈퍼컴퓨터 · 튜링 머신 · FPGA · 딥러닝 · 컴퓨터 구조론 · 컴퓨터 비전 · 컴퓨터 그래픽스 · 인공지능 · 시간 복잡도(최적화) · 소프트웨어 개발 방법론 · 디자인 패턴 · 정보처리이론 · 재귀 이론 · 자연어 처리(기계 번역 · 음성인식)





TIOBE 선정 올해의 프로그래밍 언어 / C언어
[ 펼치기 · 접기 ]

2007년
Python
2008년
C
2009년
Go
2016년
Go
2017년
C
2018년
Python
2018년
Python
2019년
C
2020년
Python


[각주]

C
파일:C언어 로고.svg
개발국제 표준화 기구(ISO)
버전18(2018년 6월 업데이트)
파일:홈페이지 아이콘.svg

1. 개요
2. 설명
2.1. 사용 분야
2.2. 등장 배경
2.3. 장점
2.4. 단점
2.5. 저수준 언어로서의 C
2.6. 고수준 언어로서의 C
2.7. 이식성
2.8. 점유율과 플랫폼별 지원 현황
2.8.1. MSVC 2019 이전까지 마이크로소프트의 C언어 지원 수준
2.9. 다른 프로그래밍 언어에 미친 영향
2.9.1. C++와의 관계
2.10. 대한민국에서의 위상
3. 주요 개념
3.2. 스택(Stack)과 힙(Heap)
3.3. 코딩 스타일
3.4.1. 주의사항
4. 역사
4.1. 탄생 이전
4.1.1. ALGOL 60 (1960년)
4.1.2. CPL (1963년)
4.1.3. BCPL (1967년)
4.1.4. B (1969년)
4.2. 탄생 이후
4.2.1. C (1972년)
4.2.2. K&R C (1978년)
4.2.3. 'ANSI C' (1989년), 'ISO C' (1990년)
4.2.4. C95 (1995년)
4.2.5. C99 (1999년)
4.2.6. C11 (2011년), C17 (2018년)
4.2.7. C23 (2023년)
5. C언어를 공부하기 좋은 책과 자료
5.1. The C Programming language
5.2. C Programming: A Modern Approach
5.3. Modern C
5.4. C언어 펀더멘탈
5.5. C Primer Plus 6th Edition
5.6. 열혈 C 프로그래밍
5.7. 이것이 C 언어다 서현우의 C 프로그래밍 정복
5.8. 터보 C 정복
5.9. 기타 관련 자료 소개
6. C언어용 개발 도구들
6.1. 관련 문서
7. 여담
7.1. C언어의 다음 언어?
7.2. C언어와 '++', '#'
7.3. Write in C
8. 관련 문서
9. 관련 커뮤니티


#include <stdio.h>

int main(int argc, char** argv) { // int main(void) 나 int main()도 가능하다.
    printf("Hello, world!\n");
    return 0;
}



1. 개요[편집]



The computing world has undergone a revolution since the publication of The C Programming Language in 1978.

1978년 책 "The C Programming Language" 출판 이후 컴퓨팅 세계는 혁명을 겪어왔다.

- The C Programming Language 2nd Edition [1]

1972년에 벨 연구소(Bell Labs)의 데니스 리치[2]가 만든 범용(general-purpose) 프로그래밍 언어. 원래 명칭은 그냥 'C'지만 한국에서는 'C언어'라는 명칭으로 주로 부른다. 영미권에서도 Clang이라고 부르는 사례가 있다.
Python과 함께 세계적으로 많이 쓰이는 프로그래밍 언어 중 하나이다.

온라인 상으로 코드를 실행시켜 보고 싶다면 여기로.


2. 설명[편집]


C언어의 정신은 C99 Rationale에서 다음과 같이 묘사하고 있다.

  • 프로그래머를 믿어라. (Trust the programmer)
  • 프로그래머가 작업을 못하게 방해하지 마라. (Don't prevent the programmer from doing what needs to be done)
  • 언어를 작고 간단하게 유지하라. (Keep the language small and simple)
  • 명령을 실행하는 방법을 하나만 제공하라. (Provide only one way to do an operation)
  • 호환성은 장담할 수 없더라도 빨리 작동하게 만들어라. (Make it fast, even if it is not guaranteed to be portable)

첫 줄의 '프로그래머를 믿어라' 부분이 오늘날 다른 언어들과 가장 큰 차이를 불러오는 것이다. 오늘날 다른 고생산성 언어들이 프로그래머를 못 믿고 퍼포먼스 희생을 감수하고서라도 문제가 생길만한 부분들을 컴파일러 또는 가상 머신에서 자동으로 처리해주거나 프로그래머가 이상한 코드를 짜지 못하도록 엄격하게 컴파일해준다면, C언어는 "프로그래머인 당신을 믿을 테니까 알아서 프로그래밍해라." 한마디로 끝낸다.[3]

C언어 자체는 지원되는 기능이 적고 문법이 간단하다. 객체 지향 프로그래밍(OOP)이나 코루틴, 클로저, 메타 프로그래밍 등 고수준의 기능들을 지원하는 언어들과 비교하면 특히나 그렇다. 550쪽 정도밖에 안 되는 C언어 표준에서도 순수 문법 부분은 200쪽 정도밖에 안 되며 나머지는 다 라이브러리 관련 부분이고, 함수의 개수로 치면 고작해야 150개 근처이다. 수천 개나 되는 기본 라이브러리를 지원하는 다른 언어들과 비교하면 정말 작고 간단하다. 초반의 포인터 장벽만 넘는다면 문법 자체를 마스터하고 간단한 커맨드라인 프로그래밍을 할 수 있는 수준까지는 엄청 쉽다.

하지만 기능이 적다고 결코 쉬운 건 아니다. 프로그래밍을 할 때 지원하는 기능이 적다고 그 적은 기능만 쓸 수는 없다. 따라서 기본 라이브러리에서 지원하지 않는 기능은 결국 프로그래머가 직접 구현해서 써야 하는데, 1970년대 이후 프로그래밍 언어계에서 오늘날 영어와 같은 위치를 차지하고 있던 덕분에 그런 기능의 구현이나 최적화에 관한 많은 트릭들이 존재하고 이것을 얼마나 많이 알고 있는 가가 사실 C언어의 핵심이다.

오늘날 고수준 언어들이 다수의 프로그래머가 함께 개발하는 것을 염두에 두고 팀에 누가 될 만한 위험하거나 생산성에 저해되는 부분들을 언어 차원에서 강제로 제외시키는 경향이 있다면, C언어는 이런 부분을 완전히 개방했다. 초기 C언어는 커다란 규모의 프로그램을 거의 염두에 두지 않고 개발됐다. 당시 IBM 메인 프레임에 사용되던 System/360이 수천 명의 프로그래머가 달라붙어 어셈블리어로 수백 만 줄이었는데, C언어로 만들어진 가장 큰 프로그램인 초창기 유닉스의 커널은 고작 만 줄 정도였다. 상황이 이렇다 보니 같은 프로그램이라도 프로그래머의 지식 수준과 능력에 따라 퀄리티 차이가 그야말로 극과 극으로 벌어지는 언어이다. 리눅스 등 C언어로 작성된 대규모 오픈소스 프로젝트를 보면 C언어 활용의 예술을 볼 수 있다. 방대한 양의 코드를 함수, 구조체, 포인터, 매크로만을 이용해서 철저히 모듈 단위로 잘 관리하고 있다.

어떤 언어든 그 실력을 충분히 발휘하려면 주로 쓰이는 분야에서 사용하게 되는 기술을 익혀야 하는데, C언어의 주 사용 분야라는 것이 하필 기계 제어. 제대로 사용하려면 프로그래머들이 보통 싫어하는 하드웨어와 어셈블리어에도 결국 손을 대야 한다.

일반적인 프로그래밍도 역시 가능하지만, 그런 용도로는 더 적합한 언어들이 널려 있다. 2010년대부터는 데스크톱 애플리케이션보다 웹, 스마트폰 애플리케이션의 활용도가 높아져서, 하드웨어 컨트롤이나 성능이 중요한 분야[4]가 아니면 C언어를 써야 할 이유가 거의 없다. 즉, 초심자가 C언어를 배우는 것까지는 문제가 없지만 배우고 나서 뭔가 제대로 할만한 건 사실상 없다. 그리고, 바로 그렇기 때문에 프로그래밍 입문용 언어의 자리도 해외 기준으로는 대부분 JavaPython으로 대체됐다. 또한 데스크톱 애플리케이션도 HTML/CSS/JavaScript를 이용하는 Electron 프레임워크가 나오면서 예전보다 개발이 훨씬 쉬워졌다.[5]


2.1. 사용 분야[편집]


C언어는 다음과 같은 분야에서 사용되며, 일반 사용자의 눈에 보이지 않는 컴퓨터 산업의 기반이 되는 곳에서 주로 쓰인다.

  • 운영체제[6][7] 및 디바이스 드라이버
  • 마이크로컨트롤러
  • 임베디드 시스템
  • 암호학 라이브러리[8]
  • 프로그래밍 언어 인터프리터(CPython 등)
  • 웹 서버(Apache 등)
  • 데이터베이스(PostgreSQL 등)
  • 애플리케이션
  • PC제어[9]
  • 이외에 매우 빠른 계산 속도가 필요한 프로그램이나 라이브러리[10]


2.2. 등장 배경[편집]


C언어 이전에도 고수준 언어들은 많이 존재했지만, 대부분 특정 애플리케이션 영역을 대상으로 하거나, 컴퓨터 과학 이론을 입증하기 위해 만들어진 실험실 언어들이었다.

어플리케이션 영역이 아닌 운영체제를 어셈블리어가 아닌 언어로 작성한다는 것은 당시엔 금기였다. 유닉스의 전신인 MULTICS는 그 금기를 어기고 PL/1라는 고수준 언어로 작성하려고 시도했으나, 불운하게도 성공하지 못했다. 유닉스는 멀틱스에 대한 반성에서 단순하게 만드는 방향을 추구했으며, 그렇기에 이름부터가 UNI-로 시작하도록 지었다. C언어와 유닉스는 소수의 예외를 제외하고 대부분을 C언어로 작성하고서도 우려와는 달리 단점보다 장점이 훨씬 많다는 것을 보이면서 이 금기에 정면으로 도전해서 승리했다.[11] 그리고 지금은 운영체제는 C언어가 아니면 안된다는 새로운 금기가 생겼다. 실질적으로 성능과 효율성을 희생하지 않으면서도 수많은 플랫폼에서의 이식성을 제공하는 언어가 C언어 외에 그리 마땅치가 않기 때문이다.

C언어가 등장하던 당시에는 코볼, 포트란 등이 고수준 언어로써 주로 쓰였는데, 이들은 천공 카드가 쓰이던 시절 만들어져서 문법이 매우 불친절하다.[12][13] C언어는 이런 당대 고수준 언어들에 비해 매우 이해가 가기 쉬운 문법을 사용하여 초보자가 쉽게 접근할 수 있었다.


2.3. 장점[편집]


C언어로 짜여진 코드는 속도가 빠르고 바이너리 크기도 작아 속도가 다른 무엇보다 (심지어는 생산성보다도) 중요한 임베디드 혹은 모바일 계열, 또는 시스템 프로그래밍 등에서 주로 쓰인다. Python등의 다른 언어들에 비해 기본으로 포함되는 크기도 작을 뿐더러 같은 알고리즘으로 짜도 결과물의 크기가 더 작은 경향이 있다. 과거에는 메모리 가격이 비쌌던 만큼 메모리를 적게 잡아먹는 프로그래밍 기법을 선호했으며, 화성 탐사선도 이러한 점을 반영하여 C언어로 만든 프로그램을 사용했다.

이러저러한 고급 언어들이 나오는 상황에서도 아직 저수준의 제어를 위해 C언어가 필요한 경우도 많다. 예를 들어, OS를 만든다면 아무리 생산성을 고려한다고 해도 시스템 제어 측면과 OS의 기능 위에서 돌아가는 애플리케이션 때문에라도 속도라는 면은 중요하고[14], 그렇다고 속도를 높이기 위해 어셈블리어나 기계어로만 OS를 짜기에는 생산성이 매우 낮아지니, 타협점으로 C언어를 쓴다. 물론 시스템 콜 인터페이스나 ABI, 인터럽트, 부트 스트랩, 드라이버 등 머신에 직결된 부분에는 어셈블리나 기계어를 사용해야 한다. 아니면 머신 제조업체가 제공하는 라이브러리를 사용하거나. 최근 C언어/C++ 수준의 기계 제어와 안전한 메모리 관리를 동시에 제공하는 Rust라는 언어가 새로 나오기는 했지만[15], C언어와 C++는 원체 레거시가 오랫동안 유지되는 성향이 강해서 Rust가 메이저로 부상하기는 쉽지 않은 상황이다.

또한 대부분의 운영체제가 제공하는 API/시스템 콜은 C언어 기반이므로, 이를 직접 사용하려면 어찌되건 C언어를 래핑하는 방식으로밖에 쓸 수 없다. 그 외에 임베디드 시스템에서 단가 문제로 시스템 처리 능력과 메모리 제한이 매우 심각한 경우가 많은데, 이 경우도 C언어가 그나마 적합하다. 옛 시절 어셈블리어가 차지했던 자리를 현재는 C언어가 차지하고 있다고 봐도 된다. 이렇게 활용되는 부분이 많으므로 당분간 C언어가 사장될 가능성은 없다. 게다가 막대한 분량의 레거시 코드도 있고. 실제로 프로그래밍 언어 점유율 조사에서 한때 자바를 제치고 1위를 차지한 적도 있는 것을 보아서는 당분간 현역으로 왕성하게 활동할 것으로 보인다. 물론 이건 C언어의 점유율이 늘어났다기보다는 타 현대적인 언어들 덕분에 자바의 점유율이 줄어든 것이지만.[16]

안정성보다는 퍼포먼스를 골수까지 극한으로 뽑아내야 하는 게임 프로그래밍 분야 또한 C언어/C++가 대세. 게임 프로그래머들이 C언어에서 C++로 넘어가기를 끝까지 싫어했던 것은 오로지 C언어(지금은 C++)가 다른 언어보다 속도를 빠르게 최적화할 수 있다는 생각 때문이며, 그래서 다른 분야보다 보수적이라는 소리를 듣는 편이다. 그러나 요즘에는 모바일 게임 시장이 급속하게 커지면서, 코어 부분만 C언어/C++로 만들고 그 외의 상당 부분은 Python, Java, C\# 등의 고생산성 언어로 대체하는 경우가 늘어나고 있다.

현 시점에서 가장 중요한 장점 중 하나는 사실상 모든 아키텍처와 운영체제에서 가상머신 등의 추가적인 단계 없이 네이티브로 지원하는 언어라는 것이다. 그 다음으로 널리 쓰이는 C++조차도 이 정도로 지원되지는 않는다. 워낙 널리 쓰이다 보니 CPU 디자이너들이 가장 먼저 하는 일은 C언어를 instruction set으로 포팅하는 것일 정도다. 심지어 C언어 설계 자체가 CPU 인스트럭션 설계에 영향을 주는 단계에 이르렀다. 그런 관계로 이식성이 중요한 경우는 대개 C언어를 사용한다. 자바의 멀티플랫폼과는 성격이 다르다. 자바는 각 플랫폼용으로 만들어진 가상머신 위에서 같은 소스가 실행되는 것이고, C언어는 각 시스템에 맞는 기계어로 컴파일되는 것이다. 위에도 언급했던 자바 가상머신 자체가 C언어로 만들어져 있으므로 당연히 자바보다 범위가 넓다. 기존 C언어 프로그래머들은 진정한 멀티 플랫폼 언어는 자바가 아니라 C언어라고 믿는 사람도 부지기수. 표준을 고려하여 주의깊게 작성된 C언어 코드는 C언어 컴파일러가 있는 어떤 플랫폼에서도 최소한의 노력만으로 컴파일 - 실행이 가능하다.[17] 그게 쉽지 않아서 문제지. 요즘 C언어를 사용하는 이유는 위에도 쓰여 있듯이 저수준 제어인데, 이는 플랫폼에서 제공하는 API를 사용하지 않고는 불가능하다. 이런 특성상 런타임의 관점에서 보면 Rust, Python, Java, C++ 등의 사실상 거의 모든 언어의 런타임들부터가 C언어 런타임에 의존하고 있다.

이미 한물 간 언어처럼 보이지만, 여전히 '프로그래밍' 입문으로 C언어를 추천하는 사람이 많다. C언어라는 언어는 매우 심플하면서도 배우는 과정에서 소프트웨어 구성의 최소 단위인 비트부터 시작해서 메모리 관리, 그리고 고급 개념인 OOP 비스무리한 것까지[18] 흉내내면서 소프트웨어 전반을 훑게 되고, C언어를 배우면서 나오는 과제들은 커맨드 라인에서 이미 쓰이고 있는 기본적인 툴을 'reinvent the wheel'[19] 하는 식의 과제들이 많으므로 바닥부터 훑어가며 견문을 넓히는 데 좋다. 실제로 가장 기저에 놓인 OS API[20]는 오늘날 플랫폼을 불문하고 거의 다 C언어로 되어 있고, 그 외에도 대부분의 인프라가 되는 소프트웨어는 C언어로 만들어진 후 타 언어로의 바인딩을 제공하는 식이다. 로우 레벨부터 단계를 높여가며 관찰을 해보면, 머신 코드는 머신에 따라 달라지고, 어셈블리어도 Intel/AT&T 등 문법에 따라 몇 가지 버전이 존재하지만, 그 위쪽에서 결국 C언어로 대통합이 이루어진다. 그리고, C언어 위쪽으로 가면 다시 C++/Java/C#/Objective-C/Python 등으로 다양하게 갈라진다. 즉, 두 개의 원뿔을 꼭짓점끼리 붙여 놓은 형태이며, 저 꼭짓점 부분에 C언어가 존재하는 형태이니, 이것만으로도 C언어의 중요성은 충분히 알 수 있다. 그래서 이런 견문은 실제로 나중에 더 이상 C언어를 쓰지 않고 타 고급 언어로 넘어가더라도 유용한 경우가 많다. (실제로, 많은 수의 언어가 C언어와의 FFI를 제공한다.) 즉 결론만 말하자면 C언어를 배우면 활용도가 매우 높다는 말. C언어만 알아도 같은 절차지향 프로그래밍 언어뿐만 아닌, 객체지향 프로그래밍 언어도 어느정도 이해할 수 있다. 물론 이해했다는건 단편적이고 객체지향에 대해 공부는 조금 더 해야하긴한다.

하드웨어나 컴퓨터 아키텍처를 배우게 된다면, C언어의 특징은 오히려 장점이 된다. Java나 Python 같은 언어들은 일반적인 상황에서 생산성을 높이기는 좋지만 특정한 상황에서 속도를 높이기는 어렵다. 일반적인 개발을 하려면 많은 상황을 처리할 수 있도록 강력하고 복잡하게 만들어야 하기 때문이다. 따라서 고성능이 필요한 특정 목적이 필요할 경우 언어에서 쌓은 추상화의 장벽을 뚫고 저수준(low level) 개념을 이용할 필요가 있는데, 이에 관한 개념을 제대로 이해하려면 처음부터 OS와 기계 제어를 위해 태어난 C언어를 사용하는 것이 가장 효과적이다. 다시 말해, C언어를 공부한다는 말은 곧 하드웨어를 공부한다는 말과 같다고 할 수 있다.


2.4. 단점[편집]


성능이라는 대명제에 충실해서 작게는 변수 초기화, 배열 범위 점검, 널 포인터 문제에서부터 크게는 쓰레기 수집(Garbage Collection; GC), 예외 처리 같은 것까지 조금이라도 하드웨어에 오버 헤드가 걸릴 것 같은 기능은 다 무시하기 때문에 주니어 프로그래머에게는 어렵다. 프로그래머가 메모리 관리까지 전부 신경써야 하는 것이다.

현재 쓰이는 고수준 개념들 자체는 의외로 오래된 경우가 많다. 예를 들어서 쓰레기 수집은 1959년에 최초로 구상됐고, 타입 에러를 컴파일 타임에 모두 잡아낼 수 있는 Hindley-Milner 타입 인터페이스가 1970년대 후반에 나와서 Haskell 등지에서 쓰이고 있다. 클로저 개념도 1960년대~70년대에 나왔다. 시간상으로는 지원한다 해도 이상하지 않다. 그럼에도 이런 기능을 C89/C90가 지원하지 않았던 이유는, 당시 언어에 고수준 개념을 구현하기에는 하드웨어의 성능이 절대적으로 떨어졌다는 점이다. C언어가 컴파일러를 거쳐 기계어 파일이 나오면 그걸 다시 사람이 직접 최적화를 해줘야 했다. 당시 컴파일러의 프로그램 최적화 성능이 떨어졌던 이유도 있겠지만, 당시 하드웨어는 초고도로 최적화를 하지 않으면 만족할 만한 속도가 나오지 않았다. 그래서 그 당시에는 프로그래머라면 저런 부분은 알아서 관리해가며 쓰는 것이 기본이었다.

어셈블리어보다 이식성이 좋다고는 하지만, 하드웨어마다 달라지는 부분들을 언어 내에서 컨트롤해서 일관성을 유지하는 것이 아니라, 성능을 위해서 전혀 후처리를 하지 않고 그대로 프로그램에 반영해버리므로 사실 C언어의 이석성도 미신이라는 사람들이 많다. 그대로 때려박는 특성상 컴퓨터 아키텍처가 다르면 똑같게 동작할 리 없다는 것이 이유다. 그나마, C99에서는 컴퓨터 성능이 좀 올라간 것을 반영해서 여러가지 엣지 케이스에 대해 어느 정도 통일성을 만들려고 한 노력이 엿보이긴 한다.

다음은 C언어 사용 시에 어려움을 느끼기 쉬운 부분들이다.

  • 느슨한 타입 검사: 특히 포인터와 관련하여 문제가 생기기 쉽다.
  • 범위를 벗어난 배열 접근: 배열에 접근할 때, 인덱스가 배열 범위를 벗어나도 이를 체크하지 않는다. 이를 이용한 flexible array member와 같은 기능도 존재한다.[21] 문제는 이것이 버퍼 오버플로 취약점이라는 심각한 보안 버그로 이어질 수 있다는 것이다. 물론 프로그래머가 꼼꼼히 체크하면 되긴 하는데 사람이 실수를 하지 않을 수는 없기 때문에...[22][23]
  • 문자와 문자열에 대한 추상화 부재: C에서는 문자에 대해 적절한 추상화를 제공하지 않고 날 것의 숫자 코드 그대로 처리하며, 문자열에 대해서도 string과 같은 타입이 따로 없고 그 대신에 char형 배열을 그대로 사용한다. 언어 자체적으로 이를 간편하게 다루는 기능이 없기 때문에 표준 문자열 함수에 의존해서 처리해야 하며, 초보자들이 잘못 이해하거나 혼란을 느끼기도 쉽고 실수를 하거나 버그를 만들기도 쉽다.[24][25]
  • 문자열 처리 방식상의 취약점: C언어에서는 모든 비트가 0인 널(null, \\0) 문자를 써서 문자열의 마지막을 표현하는데, 실수로 널 문자열을 덮어쓸 경우 경계를 침범하기도 쉽고, 문자열의 길이를 구하려면 매번 길이를 세어 보아야 한다는 문제가 있다.[26]
  • 배열과 포인터의 혼동: C언어의 배열은 문맥 상황에 따라 포인터로 바뀌는 경우가 많고, 따라서 초보자가 느끼기에 무척 혼란스럽고 구분이 어렵다. 심지어 익숙해진 다음에도 버그를 만들기가 쉽다.[27]
  • 네임 스페이스 미지원: C언어는 C++과 달리 네임 스페이스를 여러 단계로 구분하지 않기 때문에, 함수 이름과 전역변수끼리 서로 충돌이 날 가능성이 높아진다. C언어에서 서로 이름이 충돌하지 않게 하는 유일한 방법은 충분히 길게 이름을 짓고 다른 라이브러리와 겹치지 않게 기도하는 것 뿐이다.[28]
  • 각종 형변환 규칙: 정수 승격, 일반 산술 변환, 기본 인자 진급 등 암묵적으로 수행되는 생소한 형변환 규칙들이 학습을 어렵게 만들고 학습 이후에도 프로그래머의 실수를 유도한다.[29]
  • 예상하기 어려운 최적화, 부수 효과(side effect): C언어는 불필요하다고 판단되는 수식을 평가하지 않을 수 있다. 즉, 극단적으로 코드를 변형하거나 제거하여 최적화를 수행할 수 있다. 만약 프로그래머가 코드를 올바르게 작성하지 않는다면 컴파일러는 이를 잘못 판단하여 최적화 과정에서 예상하기 어려운 오동작을 만들어낼 수 있다.
  • bool 타입의 부재: C에서는 정수형이 bool 타입을 대신한다. 0은 false이고, 나머지 다른 것들은 전부 true로 처리한다.[30] 때문에 임의의 char 값이 숫자인지 알아보는 isdigit 함수는 숫자가 아니면 0을 리턴하고 숫자일 경우 보통 1을 리턴하긴 하지만, 0이 아닌 그냥 어떤 수를 리턴하는 컴파일러도 있다. 매뉴얼 페이지에는 보통 non-zero라고 되어 있다. 비슷하게 논리적 부정 연산을 의미하는 !의 경우는 0일 경우 1로, 0이 아닌 다른 모든 값은 0으로 바꾼다.[31]

C언어에는 저런 함정들이 매우 많이 도사리고 있으며, 이러한 것들이 가장 기초적인 단계에서부터 서로 얽혀 등장한다. C언어를 배운다는 것은 사실 '어떻게 프로그램을 만들 것인가'를 배우는 게 아니라, '어떻게 저런 함정을 피해갈 것인가'를 배우는 거라는 사람도 있다.

C언어는 타 언어에 비해 기계에 좀 더 가까운 얇은 추상화를 제공하기 때문에, 사용자가 기계에 대해 더 잘 알고 있어야 하고 예외인 경우도 많으며 그에 비해 안전장치는 굉장히 빈약하다. 따라서 위와 같은 여러 어려운 부분들이 생기기 쉽다.

하지만 반대로 저레벨 제어를 할 때는 변수 타입이나 참조 등에 제약이 적어 다른 언어보다 편하고 효율적인 점도 있다. 예를 들어서 블록 암호화 같이 비트/바이트 단위로 바이너리를 자유자재로 조작해야 하는 코드는 고수준 언어로 짜기 불편하다. 익숙해지면 구조체 같은 사용자 정의 데이터 타입을 이리저리 캐스팅해서 포인터 연산을 활용해 전혀 엉뚱한 데이터로 변환해서 쓰는 것도 가능하다.[32]

사실, 초창기 C언어는 비교적 사용하기 편리한 고수준 언어로 분류됐고 그렇기 때문에 큰 인기를 끌었지만, 오늘날 C언어는 오히려 불편한 언어에 속한다. 그럼에도 불구하고 많이 사용되는 이유 중 하나는 투명성이다. 고수준 언어들의 경우, 하드웨어로부터 거의 완전히 추상화를 시킨 경우가 많기 때문에 프로그램 로직에만 신경 쓰면 된다는 장점이 있지만, 그게 정확히 컴퓨터에서 어떤 식으로 돌아가는지를 예측하기는 그 추상화 수준만큼 힘들어지게 된다. 반면, C언어는 기능 자체가 적고 하드웨어에 맞춤 형태로 최소 한도의 추상화만 시킨 수준이기 때문에, 어셈블리어와의 호환성도 좋고 코딩과 동시에 실제 어떤 식으로 하드웨어가 움직일지 예상하기가 비교적 쉽다.

그렇기 때문에 C언어로 코딩을 한다는 것은 곧 저런 장점을 살리고 싶다는 것이고, 그러려면 결국 컴퓨터 아키텍처에 대한 지식도 필요하며, C언어 자체에 대해서도 아주 디테일한 수준까지 알고 있어야 한다. 이런 측면 때문에 언어 자체가 간결하다고 하여 복잡한 기능들을 많이 제공하는 고수준 언어들에 비해 쉽다고 보긴 힘들다.

이것은 별개의 이야기이며, 어느 정도 사람마다 의견이 갈리기는 하나, 포인터에 대한 이해가 빠르다면 오히려 JAVA같은 객체지향 언어보다 C언어가 편하다는 사람들이 많다. 이는 객체지향의 주 포인트인 추상화, 캡슐화, 상속, 다형성이 C에서 없거나 다른 방식으로 구현되기 때문인데, 아무래도 처음 JAVA 등을 접하는 사람은 그냥 위에서부터 아래로 책 읽듯이 읽으면 되는 프로시저 언어에 비해 이리갔다, 저기갔다, 오버라이딩도 하고, 추상화 메서드도 있고, 인터페이스도 있어서 혼란스러운 JAVA 같은 것보다는 훨씬 보기 수월하기 때문. 물론 처음에 이런 불편함을 느꼈던 초보자들이나 프로시저 언어에 매우 익숙했던 사람들도 점차 적응이 되다보면 C가 생각보다 불편한 언어였다는 것을 알고 메롱메롱 하기도 한다.


2.5. 저수준 언어로서의 C[편집]


C언어는 저수준 언어와 고수준 언어의 특성을 둘 다 가진다. 그 중에서 특히 주목받는 것은 저수준 언어로서의 강력함이다. 비록 이식성을 추구하기 위해 어느정도 추상화를 거치긴 했으나, C언어는 정수형 모델, 비트 연산, 공용체, 포인터, 형변환 규칙 등 다양한 부분들에서 기계에 대한 직접적인 접근을 의도하고 있다.

C언어는 처음부터 어셈블리어와 비교할만한 효율을 가지게 저수준으로 설계됐다. K&R의 "C programing language" 책의 초판 서문에서도 C언어를 어셈블리어를 대체하는 이식성 있는 어셈블리어(portable assembler)로 만드는 것을 목표로 설계됐음을 밝히고 있다.[33]

C언어가 주로 쓰이는 곳이 저수준 제어가 필요한 분야이다 보니, 많은 C언어 구현체들이 인라인 어셈블러를 통해 어셈블리 코드를 코드 안에 직접 집어넣을 수 있는 확장 기능을 적극적으로 지원한다. 이런 경우 호환성을 희생하고 속도를 얻을 수 있다.


2.6. 고수준 언어로서의 C[편집]


C언어가 저수준에서 가지는 강력함에도 불구하고, C언어는 분명히 고수준 언어이며, 추상성에 대해 아래에서 설명할 매우 강력한 규칙을 가지고 있다. 다만 C언어가 가정하는 추상 기계가 실제 CPU 및 기계와 상당히 가깝게, 기계어로 번역하기가 편하게, 다양한 하드웨어에서 효율적으로 돌아갈 수 있도록 잘 만들어져 있을 뿐이다. 추상층이 얇을 뿐이지 그 얇은 추상층은 소스 코드와 그것이 번역된 실행 파일을 엄격하게 분리한다.

C언어는 소스 코드를 번역할 때, 어떤 추상 기계를 가정하여 그 기계가 의미론적으로 동작하는 원리에 따라 프로그램을 번역하고 생성한다. 의미론적이라는 얘기는 실제 과정이나 동작에 상관 없이 결과만 같으면 된다는 뜻이다.[34] 또한 의미있는 결과나 부작용을 가지지 않는다고 판단하는 부분에 대해서는 그냥 평가하지 않아도(없애버려도) 무방하다.[35][36][37] 이런 강력한 추상성을 통해 C언어가 얻는 것은 최적화의 가능성과 이식성이며, 이는 (C++이 아닌)C로 제작되는 수많은 수십 년 단위의 초대형 프로젝트들이 증명한다.

많은 C입문자들이 C언어가 기계를 직접적으로 1:1 제어하는, 일종의 이식성을 가진 어셈블러(Portable Assembler)이기를 기대한다. 그러나 현실적으로 C언어는 절대, 전혀, 이식성 있는 어셈블리어라고 할 수가 없다. 왜냐하면 기계와 직접적으로 상호 작용하지 않기 때문이다. 순수한 C언어 문법만으로는 CPU 레지스터, CPU 캐시, I/O 포트, 페이지 테이블, 스택 프레임, 버스, USB, RAM 등 하드웨어와 어떤 방식으로도 직접적으로 접근할 수 없다. 한편으로 위에서 설명한 바와 같이 C언어 소스 코드는 1:1로 그대로 기계어로 치환되지도 않으며, 오히려 의미론적으로 해석되어 최적화를 거친 채 번역되는 것이 보통이다.

이러한 특징은 오히려 이식성에 큰 영향을 끼쳤다고 볼 수도 있는데 하드웨어에 직접 접근하지 못하므로 하드웨어에 의존적인 기능들은 사용하지 않게 됐고 구현체의 추상화 레이어가 기계별로 다른 동작을 일관화시켰기 때문이다. 다만 이 점은 여러 컴파일러가 성능 경쟁을 하고 하드웨어 발전 속도가 빨라진 지금에는 그리 타당성 있다고 볼 수는 없다.


2.7. 이식성[편집]


C언어의 이식성은 java나 기타 다른 좀 더 최근에 만들어진 언어들의 이식성과는 방향이 다르다. 좀 더 최근에 만들어진 언어들은 대체로 엄밀한 정의를 통해 동일한 코드가 동일한 동작을 보이는 쪽을 선호하며, 이를 위한 약간의 낭비나 비효율성은 감수하는 편이다.

반면에 C언어는 효율성을 해치지 않는 범위 내에서의 이식성을 추구한다. C언어가 만들어지고 널리 퍼지던 70~80년대에는 지금보다 더 다양한 특성을 가진 다양한 하드웨어들이 범람하고 있었으며, 그 성능 또한 일체의 낭비를 허용하지 못할 정도로 충분하지가 못했다. 따라서 어떤 최소한의 공통 부분을 제시하되 그 위에 각 환경에 맞는 자율성을 컴파일러 제작자들에게 보장해야 했다.

C언어 표준의 이식성과 관련된 부분들은 크게 3단계로 구분된다. 동작의 내용을 분명하게 명시해야 하는 것(implementation-defined), 동작을 보장하되 그 내용을 명시할 필요가 없는 것(unspecified)', 동작을 보장할 필요가 없는 것(undefined). 이러한 것들을 표준 문서에서는 Portability issues란 이름의 부록으로 따로 정리해 두고 있다. 컴파일러 제작자는 이렇게 제시된 선택의 범위 내에서 컴파일러를 만들어야 하고, 프로그래머는 이러한 선택과 가능성들을 고려하여 코드를 작성해야 한다.

이식성 문제는 다른 환경/기계로의 이식 뿐만 아니라, 잘못된 최적화로 인한 성능저하 및 오동작에도 큰 영향을 미친다. C언어는 불필요하다고 판단되면 코드의 일부를 변형하거나 제거할 수 있는데, 동작을 보장하지 않는 경우(undefined)의 경우에는 이러한 '판단'이 실제 하드웨어의 동작방식이나 프로그래머의 기대에 맞추어 동작하지 않는 경우가 많다. 그 결과 컴파일러의 정상적인 최적화가 프로그래머의 잘못된 가정과 맞물려 오동작과 보안오류를 만들 수 있다.[38][39] 이러한 오동작은 원인이 되는 부분에서 발생하는 것이 아니라 원인이 되는 부분이 정상적으로 동작할 것이라고 상식적으로 판단하여 최적화를 진행하는 전혀 엉뚱한 부분에서 날 수도 있기에 이를 찾고 대처하기가 까다롭다.

따라서 C언어 추상머신의 규칙과 의도를 올바로 이해하지 않고 실제 기계의 동작과 구현에만 의존하여 판단하는 것은 위험하며, 코드를 이식할 필요가 없는 프로그래머들조차도 이식성 문제에 주의를 기울여야 한다.


2.8. 점유율과 플랫폼별 지원 현황[편집]


오랜 시간동안 Java와 함께 몇 년째 1, 2위를 다투고 있었다.출처[40] 그 이외의 언어와는 넘사벽의 비율을 보여준다. 그야말로 부동의 원투 펀치. 다른 언어들이 3위 경쟁을 하는 동안 C언어와 Java가 양대 산맥을 형성하고 있다. 좀 더 넓게 C언어 계열(C++, C# 등 C언어 문법 혹은 그와 매우 유사한 문법 체계를 사용하는 언어)와 JAVA 계열(Arduino 등 JAVA 문법 혹은 그와 매우 유사한 문법 체계를 사용하는 언어)로 보자면 C언어 계열이 단연코 확고한 1위. (C언어와 C++, C#만 합쳐도 1/3이다. 거기에 자바와 파이썬의 점유율을 합치면 거의 절반.)

가장 널리 쓰이는 PC 플랫폼인 윈도우에서는 MSVC 2019 16.8 버전 이후부터 적극 지원 중이다. 사실 그 이전 Visual Studio는 VS개발진 본인들도 반쯤은 버려진 언어임을 인정하기도 했다.[41] MS에서는 C언어를 Internal language로 규정하여 내부적으로 윈도우와 기타 MS 상품들을 만드는 데는 사용하지만, C언어 프로그래밍 환경을 사용자에게 정식으로 제공하지는 않았다. 덕분에 윈도우가 자랑하는 Visual Studio에도 C언어 프로젝트 항목은 없으며, 정식으로 C11/C17을 지원하는 현재(VS 2019/VS 2022)도 기본 사양에는 C 프로젝트 템플릿이 포함되어 있지 않다. (C++ 프로젝트를 선택하여 소스 파일 확장자를 .c로 바꿔주거나 C언어로 컴파일한다고 프로젝트 옵션을 설정해야 한다.) 게다가, 기존에는 그런 식으로 사용을 하더라도 MS의 C언어 지원은 순수하게 C++에 묻어가는 정도라, 새로운 ISO 스탠다드인 C99/C11의 기능들도 거의 지원하지 않았다. 다행히 MSVC 2019 16.8부터는 정식으로 C11/C17을 지원하게 됐으므로 이제는 Visual Studio를 통해 최신 C언어를 경험할 수 있게 됐다.

리눅스의 경우에는 GCC라는 사실상의 오픈소스 표준 컴파일러 덕분에 지원이 괜찮으며, Unix-like 운영체제라는 버프도 있고,[42] C언어를 배우고 여러가지 시험해보면서 놀기에 적합한 환경을 제공해준다. 윈도우와 다르게 커널부터 오픈소스로 개발되고, 이 커널이 C언어로 만들어져 있어 C언어의 사용도 활발하다. 이쪽 프로그래머들은 개발 환경을 vim이나 Emacs로 사용하는 사람들이 많이 있다.

macOS(구 OS X)는 신생 컴파일러인 LLVM/Clang[43]을 사용하며, 역시 지원은 좋은 편이다. 이는 플랫폼 메인 개발 언어를 Objective-C로 잡았기 때문인데, Objective-C는 C언어와 완전히 호환이 되기 때문에 달랑 Objective-C만 지원해도 C언어가 완전히 지원되는 셈. 새로운 스탠다드의 적용도 세 플랫폼 중 가장 빠르다. MS는 위에서 이야기했듯이 C++의 subset인 부분에 한해서만 C언어의 최신 표준을 지원하고, GCC와 LLVM/Clang은 C11을 모두 지원한다.

C언어를 표준으로 공부하려면 현 시점에서는 성능이 우수하고 표준을 엄격하게 따르는 GCCLLVM/Clang을 사용하는 편이 좋다. 그러나 MSVC 2019 이전의 Visual Studio는 사용하지 않는 것이 좋다. 구버전 Visual Studio에 통합되어 있는 MSVC 컴파일러는 C99의 가변 길이 배열조차 지원하지 않을 만큼 C언어 지원이 부족했기 때문이다. MSVC 2019 16.8 이후에는 Visual Studio도 제대로 C11/C17을 지원하므로 윈도우 사용자라면 MSVC 2019 이상을 사용하는 것이 좋다. 만약 Visual Studio를 사용하지 않을 윈도우 사용자라 하더라도 MinGW와 같이 WSL와 달리 GCC/Clang을 사용해서 Win32 네이티브 바이너리를 컴파일하는 환경도 제공하고 있다. 특히 GCC, LLVM/Clang은 -Wall -Wextra -Werror 컴파일 옵션[44]을 넣으면 사소한 경고 사항도 전부 에러로 변환하여 컴파일을 중단시키므로 코드의 품질을 잡는 데 많은 도움이 된다.


2.8.1. MSVC 2019 이전까지 마이크로소프트의 C언어 지원 수준[편집]


아래의 서술은 구버전 MSVC에서의 문제이다. MSVC 2019 (16.8) 이상은 C11/C17 표준을 완전하게 지원하며 MSVC 2019 (16.8) 이후에는 적용되지 않는 내용이니 주의를 바란다.

MSVC 2019 이전까지의 버젼에서, C는 사실상 반쯤 버려진 언어라 MS C++ 컴파일러로 컴파일한 코드에 CRT를 억지로 끼워 맞추는 수준에 가까웠다. 1999년에 확정된 C99마저 제대로 지원하지 않는 것은 MS의 개발 정책 때문이었다. 단순히 비표준 확장을 집어 넣어서 문제가 되는 것이 아니라, 표준의 범위 내에서도 서로 충돌이 나는 것이 문제라서 꽤 골치 아픈 문제였다. 표준과 MS 제품과의 차이점을 정확히 알고, 이중으로 코드를 작성해야 했으니까...

국내 교재나 대학의 경우, 수강자가 친숙하고, 또한 예제를 따라하기 비교적 훨씬 쉬운 윈도우 + Visual Studio 기준으로 설명하는 경우가 많다. 그러나 Visual Studio (MSVC) 컴파일러는 ANSI C를 기반으로 상당히 많은 비표준 확장을 제공하고 있으며, 표준에 어긋나 에러를 일으켜야 할 문장도 MSVC는 어떻게든 실행을 해 버리기 때문에 초보자에게는 오히려 독이 됐다. GCC 역시 비표준 확장을 많이 제공하지만, 이후의 표준인 C99, C11에서 GCC의 비표준 확장이 대부분 표준화돼서 비교적 나은 편이다.

따라서 한동안은 웬만하면 GCC/Clang환경에서 표준으로 먼저 배우고 MSVC 등의 비표준 기능은 차후에 문서를 보고 따로 익히거나, 꼭 MSVC로 배우고 싶다면 표준과 비표준, 나아가서 가능하면 C89/C99의 기능을 구분해서 설명하는 교재로 배우는 것이 권장됐다.

MSVC의 비표준 문법의 문제는 C언어뿐만이 아니라 C++에서도 나타나는데, C++가 아닌 MSVC 방언이라고 불릴 만큼 마이크로소프트는 표준 C++를 무시하고 있었다. GCC나 Clang에서 오류를 내는 익명 클래스의 모호한 상속, 표준 라이브러리의 fstream wstring 오버 라이드 등 수많은 문제를 만날 수 있었다.

최근 들어서는 MS도 이 문제를 인지하고 점차 개선하는 중이지만, 여전히 표준 준수에 대해서는 GCC, Clang에 비해 갈 길이 멀다. __cplusplus 매크로와 같은 경우에도 Visual Studio 2017에 와서야 해결이 된 만큼, 구형 MSVC에서 컴파일 되는 코드들이 (non-secure 함수를 제외하고서도) 클래스 또는 템플릿에서 오류를 내는 경우가 잦다.


2.9. 다른 프로그래밍 언어에 미친 영향[편집]


  • {...}을 이용한 블럭 (ALGOL/PASCAL 스타일의 begin … end 보다 간결하다.[45])
  • '대입'을 뜻하는 연산자를 '='로, '동일함'을 뜻하는 연산 기호를 '=='로 사용한다. 농담 좀 섞어서, 초심자의 C언어 컴파일 오류의 90%는 여기서 나온다.[46]
  • '다르다'를 뜻하는 연산 기호를 !=로 사용한다.
  • '또는'과 '그리고'를 ||와 &&로 사용한다.
  • +=, -=, *=, /=등의 직관적인 복합 연산자를 지원한다.
  • ++ 와 \-\-\ 라는 단항 연산자를 사용한다. **와 //는[47] 다른 뜻을 가지고 있다. **은 이중 포인터이고, //은 주석이다.
  • 그 외에 if, for, while 등 많은 예약어의 사용 방식.

어떤 의미에서는 프로그래밍 언어의 라틴어[48]/한자라고 할 수도 있을지도 모른다. 현재 많은 주요 언어에서 { }를 이용한 블럭 표기나 C언어에서 쓰이는 표현식(==, ||, &&), 예약어(if, while) 등을 채택해서 사용하고 있다. 따라서 다른 언어를 배울 때 C언어를 먼저 배웠다면 친숙하게 느껴질 것이다.[49]

추후 C++로 발전됐으며, C++에서는 OOP 기능을 지원한다. 다만, C언어로 OOP를 구현할 수 없는 것은 아닌데, 객체 지향은 개념일 뿐이며 C언어로도 그 개념을 구현할 수 있다. 일례로, 당장 C 표준의 일부인 파일 I/O는 객체 지향[50]이며, Win32 API나 리눅스의 VFS(가상 파일 시스템)도 이처럼 '객체지향적'으로 코딩되어 있다. 다만, 언어 차원에서 지원이 없기 때문에 군더더기가 늘어날 수 있다는 점은 감안해야 한다.

JavaC\#, Objective-C 등 여러 언어들의 모태가 된다. 때문에 C언어를 기초로 만들어진 언어들을 흔히 C-like Language[51]라고 부른다. 그런 이유로 C언어를 제대로 익히고 나면 C-like 언어들은 쉽고 빠르게 익힐 수 있다. 단, 위에서 이미 언급했지만 C언어 자체는 엄청나게 어렵다. 그 대신 C언어나 C언어를 모태로 한 언어를 공부하면 자연스럽게 컴퓨터와 프로그램의 작동 방식에 대한 기초 지식을 습득할 수 있어 다른 언어나 프로그래밍 관련 스터디를 할 때 도움이 된다.


2.9.1. C++와의 관계[편집]


C++의 시작은 C with Class였으나, 그 이후 수십년간의 변화는 C언어와 C++의 공통적인 부분에서도 차이점을 만들기 시작했다. 쉽게 말해 C언어로 작성된 소스 코드를 그대로 복사하여 C++ 코드에 옮겨 붙인 뒤 컴파일하면 문제가 발생할 수 있다는 것이다. 두 언어의 근본적인 정체성과 지향점 때문에 발생하는 차이인지라, 이러한 차이점들은 오히려 늘어날 것이다.[52]

참고로, Objective-C(Objective-C++이 아니다!)의 경우에는 C++와 달리 C언어를 완전히 포함한다. 즉 Objective-C는 C언어의 완전한 상위 집합(superset)이다.


2.10. 대한민국에서의 위상[편집]


대한민국에선 많은 곳이 C언어로 공부를 시작하며 나머지는 C++, Java, C##, 비주얼 베이직, 어도비 플래시(액션스크립트) 등이 차지하고 있다. 즉 독점에 가까운 위치를 점유하고 있다.

이는 각 대학 혹은 학원들의 커리큘럼 탓이 가장 크다고 볼 수 있는데, 이 때문에 아직도 '자바 먼저' vs 'C언어 먼저'의 떡밥은 개발자들 사이에서 좋은 키보드 배틀 거리가 되고 있다. 그러나 후술할 내용처럼 C언어가 미친 영향은 Java를 포함해서 매우 광범위한 지라 어떻게 해도 결국 C언어가 맨 앞에 선다. 대한민국컴퓨터과학과 학부 과정에서는 이게 프로그래밍 언어의 기초 취급을 받고 있다.

C언어 자체의 난이도는 위에서 봤듯이 무시할 게 못 된다. 그럼에도 C언어를 먼저 권유하는 입장은 간단하다. 컴퓨터 아키텍처 및 시스템 프로그래밍, OS를 배우기 위해선 C언어(+ 어셈블리어 조금)만한 게 없기 때문이다. Java, Python 등의 고수준 언어는 추상화 레이어가 여러 OS 및 아키텍처 개념들을 가리고 있으며 C++는 다양한 멀티 패러다임을 언어에 집어넣느라 복잡한 문법이 많아지고 Raw pointer 접근을 하지 않는 쪽으로 발전하고 있다 보니 low-level한 직관을 얻기 힘들기 때문이다.

물론 이때문에 실전성이 2024년 기준 거의 제로에 수렴한 언어에 쓸데없이 매달린다는 비판 또한 존재한다.[53] C가 하던 일은 현재는 C++과 C#이 대부분 대체했고, 포인터 등의 이론 또한 반드시 C를 해야만 익힐 수 있는 건 아니기 때문이다.


3. 주요 개념[편집]



3.1. 포인터[편집]



파일:상세 내용 아이콘.svg
  "display: none; display: 문단=inline"를
의 [[C(프로그래밍 언어)/포인터#s-"display: inline; display: 앵커=none@"
@앵커@@앵커_1@ 부분을
참고하십시오.



3.2. 스택(Stack)과 힙(Heap)[편집]


현대 컴퓨터와 운영체제의 메모리 관리에서 가장 중요한 개념 중 하나는 스택과 힙이다. 스택과 힙은 메모리 저장공간의 유형이며, 어디에 자료가 저장되는가를 나타내고, 또한 해당 데이터의 생성과 소멸이 어떤 방식으로 이루어지는지를 결정한다. 스택은 힙과 비교할 때 다음과 같은 특성을 가진다. 스택은 이름 그대로 스택 자료구조와 스택 포인터를 가지고 구현되며, 더 빠르고 간단한 방식으로 할당과 해제가 이루어진다. 대신 그 순서가 후입선출법을 따르기에 자유롭지 못하고, 할당 가능한 용량도 힙에 비해 작은 편이다.

그러나 C언어에서는 이 스택과 힙의 개념을 사용하지 않고[54], 대신에 저장수명(storage duration)이라는 개념을 사용한다. 즉, C언어의 추상기계는 스택과 힙을 모르며, 이에 의지하여 동작하지 않는다.

저장수명은 크게 static, thread, automatic, allocated의 네 종류가 있다. static은 프로그램의 시작부터 끝에까지 존재하고, thread는 쓰레드별로 분리하여 존재하며, automatic은 해당 블록이 시작할 때 생성되어 끝날 때 소멸되고, allocated는 메모리 할당 함수들에 의해 생성되고 free() 함수로 해제될 때까지 존재한다.

이 네 가지의 저장 수명들을 가지는 메모리 개체들을 힙과 스택 중 어디에 저장할 것인지는 C언어가 알아서 처리한다. 일반적으로는 automatic은 스택에, static은 BSS, Data, Text영역 중 한곳에, allocated는 힙에 저장하나 꼭 그래야 하는 것은 아니다. 컴파일러는 굳이 그걸 어떤 영역에 보관해야 한다고 보장하거나 알릴 필요는 없다.

그러나 그럼에도 불구하고 C언어 코드 상의 어떤 변수가 스택, 힙, 또는 어떤 메모리 영역에 저장될 것인지에 대한 감각을 훈련하는 것은 효율적인 코드를 작성하기 위해 매우 중요하다. 일반적으로 힙에서 메모리를 할당받아 오는 것이 스택에 변수를 만드는 것보다 더 많은 동작을 필요로 하고, 캐시의 지역성까지도 이에 영향을 받을 수 있기 때문에 각각의 저장수명들을 필요와 상황에 맞게 적절히 활용할 수 있어야 한다.


3.3. 코딩 스타일[편집]


C언어는 스코프를 지정할 때 중괄호 쌍 { }을 사용한다. C언어는 whitespace(공백, 탭, 리턴 문자열)가 의미 없는 언어라서[55] 여는 중괄호를 엔터를 쳐서 다음 줄에 놓는 방법과, 이전 표현 바로 옆에 놓는 방식 두 가지 모두 가능하다.

용례는 코딩 스타일 문서 참고.

C언어계 책의 9할쯤은 전부 다 줄 수를 줄인 K&R 스타일이다. 반면 실제 코드의 스타일은 4할 정도뿐. 예제는 K&R 스타일로 쓰고서도 본문 중에 실제 작업에서는 Allman 스타일을 쓴다고 밝히는 저자도 있다. 2할은 기타 스타일. 그 중 Horstmann 스타일은, 중괄호 블록이 명료하게 보인다는 Allman의 장점과 적은 공간을 차지한다는 K&R의 장점을 합친 형태이다.[56]

참고로, Clang 컴파일러의 일부인 clang-format을 사용하면 소스 코드의 스타일을 일괄적으로 정리하고 유지할 수 있다.

C에서는 대입 연산자(=)가 값을 반환하기 때문에, 실수로 == 를 쓸 곳에 =를 하나만 찍는 오타를 범했을 경우, integer 값이 리턴되는 경우에는 역시 문제 없이 컴파일되고 직접 돌려서 해당 기능이 오작동을 하기 전까지 버그를 알아챌 수조차 없게 된다. 이 때문에 == 사용 시 lvalue에 일부러 상수항을 사용하는 프로그래머들도 있다.[57] 예를 들어, a = 42; if(a == 37) { ... } 같은 경우, 실수로 if(a = 37)이라 쓰면 false가 아닌 true가 뜨며(a = 37이라는 expression의 값은 37이고 0이 아닌 값은 죄다 true로 간주된다.), a에 37이 대입돼버려 완전히 오작동을 하게 된다. 문제는, 컴파일이 문제 없이 돼서 버그 찾기가 더더욱 힘들어진다는 점. 상수항을 왼쪽으로 옮겨서 if(37 == a) 로 써주면, 37 = a라 썼을 때 당연히 말이 되지 않으니[58] 컴파일 시 에러가 떠서 쉽게 알아챌 수 있다...라는 유명한 팁이 있지만, 사람의 직관과 맞지 않아 어색하게 느껴지기 때문에 일부러 익숙해지지 않으면 잘 쓰이지 않는다. 요새는 컴파일러가 좋아져서 이런 위험 코드는 대체로 경고 처리해준다.[59][60]


3.4. 문법[편집]


파일:상세 내용 아이콘.svg
  "display: none; display: 문단=inline"를
의 [[C(프로그래밍 언어)/문법#s-"display: inline; display: 앵커=none@"
@앵커@@앵커_1@ 부분을
참고하십시오.



3.4.1. 주의사항[편집]


아래와 같은 방식으로 코딩하거나 교육하는 사람이 있을 경우 주의할 필요가 있다.
  • gets 함수를 사용한다: gets 함수는 얼마나 입력받을 지 미리 정할 수 없어 버퍼에 들어온 모든 입력을 써버리는 치명적인 문제가 존재한다. 버퍼 오버플로 참조. 현재는 표준 라이브러리에서 퇴출되어 더 이상 사용할 수 없다. fgets, gets_s를 쓰도록 하자.[61] C언어 입문서에서 흔히 사용되는 scanf도 마찬가지로 문자열을 읽어들일 때 버퍼 오버플로 문제를 야기할 수 있으며 이쪽도 대안으로 VC++ 컴파일러의 scanf_s가 존재한다.[62] 같이 쓰이는 puts와 printf의 경우에는 출력 함수라 버퍼 오버플로 문제가 존재하지 않는다.[63]
  • short 자료형을 특별한 이유 없이 사용한다: 32비트 아키텍처 이상 CPU는 자료형의 팩/언팩 작업 때문에 short 자료형의 퍼포먼스가 int형보다 오히려 더 낮으며 오버플로 위험만 높인다.
  • 간단한 함수를 모조리 #define 매크로로 작성한다: #define은 전처리 시점에서 특정 코드를 미리 정해놓은 코드로 통째로 치환시키는 전처리문으로 실행 시 별도로 함수를 불러오는 오버 헤드가 발생하지 않는다는 이점이 있다. 이로 인해 과거 컴퓨터 성능이 좋지 못하던 시절에는 간단한 함수를 매크로로 작성하는 방법으로 프로그램의 속도 향상을 꾀할 수 있었다. 그러나 현대에는 컴퓨터 성능이 상당히 좋아졌기에 이 정도의 오버 헤드는 크게 문제되지 않는다. 또한 #define은 정해진 코드를 통째로 치환하기 때문에 예상치 못한 동작을 보일 수 있으므로 사용에 주의가 필요하다. 또한 매크로의 경우 IDE에서 Step 디버그 시 다소 애로사항이 생기기도 하므로 단순히 자주 사용되는 작은 크기의 함수를 위해서는 inline attribute 사용이 권장된다. 특히 inline의 경우 컴파일러가 코드 상황에 따라 인라인을 결정하고 꼭 필요하다면 __forceinline(MSVC/ICC)나 always_inline(GCC/Clang)를 사용해서 무조건 인라인을 시키는 방법도 있기 때문.


4. 역사[편집]



4.1. 탄생 이전[편집]



4.1.1. ALGOL 60 (1960년)[편집]


C언어의 뿌리는 멀리서 보면 1960년에 발표된 ALGOL 60부터 영향을 받았다고 볼 수 있다.


4.1.2. CPL (1963년)[편집]


1963년에 ALGOL 60보다 더 다양한 용도로 확장하기 위해 CPL 언어가 케임브리지 대학교의 수학 연구소와 런던 대학교의 컴퓨터 공학부에서 공동 개발됐다. 처음에는 Cambridge Programming Language의 약어였으나, 후에 Combined Programming Language 로 변경되었다.


4.1.3. BCPL (1967년)[편집]


1967년에 마틴 리차드가 CPL을 좀 더 단순화시킨 BCPL(Basic CPL)을 개발했다.


4.1.4. B (1969년)[편집]


1969년에 BCPL이 미국으로 물건너와 켄 톰슨이 'B'라는 언어를 만들었다. 보통 여기까지가 C언어의 조상격 프로그래밍 언어로 보고 있다.

알파벳 상에서 C와 B의 앞 글자인 A와는 별 관련이 없다. 얘는 오히려 R이나 MATLAB의 조상 삘.


4.2. 탄생 이후[편집]



4.2.1. C (1972년)[편집]


1972년에 같은 벨 연구소데니스 리치가 PDP-11 컴퓨터를 제어하기 위해 B언어의 특징을 물려받은, 최종적으로 'C'라는 이름으로 언어를 만들었다. CPL의 'C'는 케임브리지 대학의 C를 뜻했다가 후에 임페리얼 칼리지 런던과 조인트 프로젝트가 된 이후에는 'Combined'를 뜻하게 되는데, 스트로우스트루프의 회고에 의하면 저건 윗분들의 생각이고, 자신들에게 있어서 'C'는 언제나 CPL의 언어 디자이너였던 크리스토퍼 스트레이치(Christopher Strachey)였다고 한다. BCPL을 거쳐 'B' 가 된 것은 '벨' 연구소의 B를 딴 것.

C언어가 나올 당시 시점에서 당시 널리 쓰이던 포트란이나 코볼, 베이식과 비교해 보자면, C언어는 언어 자체에는 아주 기초적인 기능만을 탑재하여 언어에 기본 탑재되는 명령어를 최소 한도로 줄였다는 특징이 있는데 START나 END같은 명령어도 배제하고, 대부분을 { } 같은 기호로 표현했기 때문에 코드가 매우 깔쌈하고 용량도 줄었다. C언어에서는 포트란에는 아직 남아 있던 천공 카드 시대의 흔적이 거의 사라졌다.

최초의 C언어 컴파일러는 간단히 말하면 어셈블리어로 쓰였다. B 언어를 붙잡고 여러 번 씨름하다가 나온 부산물 중 하나가 C언어 컴파일러고, C언어 역시 리치에 의해 여러 번 리파인됐다. 이 문제가 유명해진 이유는 최초의 C언어 컴파일러 중 하나가 C언어 인터프리터로 개발됐으며 그렇게 개발된 컴파일러를 개선시키기 위해서 역시 또 그 컴파일러를 사용했기 때문이다. 혹자는 이것을 "진흙탕에 빠진 사람이 자신의 구두끈을 잡아당겨 빠져나온 격"이라고 표현하기도 한다. 이후 여러 언어들이 개발될 때 이와 같은 방식을 이용했다.[64][65]


4.2.2. K&R C (1978년)[편집]


1978년에 브라이언 커니핸과 데니스 리치는 The C Programming Language의 초판을 집필해서 출간했다. C언어 프로그래머에게 "K&R"로 유명한 책으로 한동안 이 책을 기준 사양으로 사용됐다.


4.2.3. 'ANSI C' (1989년), 'ISO C' (1990년)[편집]


1983년에 ANSI가 K&R C를 확장 겸 표준화하기 위해 짐 브로디를 주축으로 X3J11 위원회를 소집하고 1989년에 ANSI X3.159-1989라는 공식 명칭으로 C언어 표준을 제정했다. K&R C의 확장안 성격을 가지고 있으며, 이때 제정된 C언어 표준이라고 해서 C89라고도 불렀다.

1990년에 ANSI가 C언어 표준을 국제 표준화기구(ISO)에 넘기면서 내용 변화 없이 ISO/IEC 9899:1990라는 또 다른 공식 명칭으로도 채택됐다. 내용 자체는 먼저 제정된 ANSI C와 동일하기 때문에 C90이라고 부르기도 하지만 보통은 먼저 승인된 C89가 더 많이 불려진다.

The C Programming Language 2nd Ed. 는 ANSI C 에 맞게 수정되었다.

2019년에도 이 C89/90가 사실상의 표준인데, 이유는 여러 가지가 있다. 먼저 C89/90 다음 버전부터는 마이크로소프트에서 표준안을 제대로 지키는 컴파일러를 내놓지 않은 영향이 가장 크다. 또 거의 대부분의 C언어 컴파일러는 여기서 큰 차이 없는 C99를 따르기 때문이다. 사실상 표준인 C89/90은 ANSI가 직접 제정한 표준이라서 관용적으로 'ANSI C'라고도 부른다. 이 페이지에서도 ANSI C는 C89/90를 의미한다. 하지만 공식적으로 2019년 시점에서 ANSI C와 ISO C는 최신 표준인 C18을 가리키므로. 혼동을 주기 쉬운 표현이다. 왜냐하면 ANSI가 C89/90이후 C언어 표준 제정에서 손을 떼고 ISO를 받아들였고, ISO에서는 C99, C11, C18까지 계속 업데이트를 했기 때문에. 참고로 ANSI는 미국의 표준화 기구이고, ISO는 국제 표준화 기구인데 맨 처음 C언어 표준안을 발표할 때만 ANSI 이름으로 발표했었고, 그 이후엔 ANSI 위원들과 ISO 위원들이 함께 표준안을 개발하나 표준 발표는 ISO 이름으로 하고 있다. 현재는 대부분의 표준안이 ISO 이름으로 발표, 제정되며 ANSI와 같은 각국의 표준 위원회가 받아들여 사용하는 형식을 취하고 있다.

2020년 9월에 드디어 마이크로소프트의 Visual Studio 2019부터 C11/C17 을 공식 지원하게 됐다. 많이 늦은 감은 있지만 이제 Windows에서 Visual Studio를 사용할 때에도 최신 C 문법을 통해 사용할 길이 열리게 된 것이다.


4.2.4. C95 (1995년)[편집]


1995년에 기존 C90 표준의 일부 세부 사항이 수정되고 국제 문자 집합을 추가 지원하기 위해 ISO/IEC 9899/AMD1:1995라는 공식 명칭으로 C언어 표준 첫 수정안이 발표됐다.


4.2.5. C99 (1999년)[편집]


1999년에 ISO/IEC 9899:1999라는 공식 명칭으로 표준안이 발표됐고 이후 세 차례 개정됐다.

인라인 함수, 가변 길이 배열 및 가변 배열 원소, IEEE 754 부동 소수점 지원 강화, 가변 매크로 인수 등의 새로운 기능들이 추가 지원됐으며 이는 여러 C언어 컴파일러에서 이미 비표준 확장으로 구현해 놓았던 내용이다.

C90과 대부분 상호 호환이 가능하지만 형식 지정자가 없는 선언을 허용하는 않는 등의 엄격함이 더해졌는데, 대부분의 프로그래머들이 명시적으로 선언하는 습관을 기르도록 교육받았기 때문에 큰 혼란은 없었다.


4.2.6. C11 (2011년), C17 (2018년)[편집]


2007년부터 비공식으로 "C1X"라 불렸던 C언어 표준의 개정판이 논의됐다가 2011년 4월에 제출된 후 12월 8일에 최종 승인됐다.

제네릭 매크로, 익명 구조, 향상된 유니코드 지원, 원자적 조작, 멀티스레드, 경계 검사 기능 등 문법 및 라이브러리에 새로운 기능들이 대거 추가됐다.

2018년 6월에는 최신 표준인 C17이 최종 승인됐다. C17에는 새로운 기능은 없고, 이전 C11 표준의 결함에 대한 기술적인 수정 및 설명만 추가됐다. 단, 컴파일러 옵션은 여전히 -std=c11이 최신이다.

C11, C17은 2019년 현재까지는 실무에서 아직 잘 안 쓰인다. 가변 길이 배열, 가변 인자 매크로 등 오늘날 유용하게 쓰이는 대부분의 문법은 C99에서 추가됐고, 2021년에 실무 코드베이스에서 C언어를 쓴다는 것 자체가 엄청나게 오래된 컴파일러와도 호환돼야 할 요구 조건이 있을 확률이 높기 때문이다.


4.2.7. C23 (2023년)[편집]


signed 정수형에서 2의 보수 표현을 제외한 다른 표현 지원 제거,[66] 삼중자 제거, 10진 부동 소수점, memccpy(), strdup() 등 여러 POSIX 함수의 표준 편입 등의 소소한 기능들이 추가된다. 큰 변화가 생긴 버전이라고 할 순 없다. 상세 변경점 내역은 링크 참고.


5. C언어를 공부하기 좋은 책과 자료[편집]


C언어를 다룬 책들에 대해서 소개해둔 문서이다. 책을 고르기 전 반드시 알아둬야 할 것은, 잘못된 정보를 전달하는 책으로 공부하면 어려운 책으로 공부할 때보다 더 시간이 걸릴 수도 있다는 것이다.


5.1. The C Programming language[편집]


  • 원 저자: 데니스 리치 & 브라이언 커니핸
  • 번역서: (Kernighan의) C언어 프로그래밍
  • 번역자: 김석환(2005년) → 김석환, 박용규, 최홍순(2016년 수정2판)
  • 출판사: 대영사(2005년) → 휴먼사이언스(2016년 수정2판)

다른 언어들의 경우 여러 교재들이 서로 경쟁하는 추세이지만, 유독 C언어의 경우는 창시자인 데니스 리치와 브라이언 커니핸이 쓴 The C Programming language 2nd Edition이 독보적인 위치를 점하고 있다. 이 두 사람의 이니셜을 따서 보통 K&R 이라 칭하거나,[67] 제목을 축약해서 TCPL이라고 부른다. 이 책이 나온 지 매우 오래 되긴 했지만, C언어가 별로 변화가 없는 언어인 데다가 많은 사람들이 C89/90을 표준으로 따르고 있기 때문에 낡은 책이라도 사용에 지장이 없다.

또한, 분량은 전부 다 합쳐도 300쪽도 안 된다. 그것도 Appendix 부분을 빼면 순수 튜토리얼은 200쪽도 안되며, 언어 자체뿐 아니라 프로그래밍에 대한 여러가지 깨알같은 조언까지 다 포함이 되어있다. 보통, 일반적인 프로그래밍 언어 교재가 작아도 500쪽을 가볍게 넘어가고, C++의 창시자인 비아르네 스트로우스트루프(Bjarne Stroustrup)가 K&R과 비슷한 네이밍으로 쓴 The C++ Programming language의 경우 1300쪽에 육박하는 것[68]에 비하면 엄청나게 짧은 분량이다.

K&R이 바이블 취급을 받는 이유는 단순히 역사적이고 C언어 창시자가 쓴 책이기 때문만은 아니다. C언어가 겉으로는 심플해보이지만 워낙 숨은 함정이 많은 언어라 저런 부분들을 제대로 다 짚고 넘어가야 하는데, K&R 이외에 제대로 완전하게 짚어주는 교재가 거의 없다.[69] 보통의 고수준 언어들처럼 접근해서 직관적으로 '이렇게 하면 된다.' 식으로 설명을 하는 교재들이 많은데, 사실 온갖 정의되지 않은 행동(Undefined Behaviour)과 구현 특화 상세 내역(Implementation-specific Details), 하드웨어에 의존적인(Hardware Dependent) 함정들이 곳곳에 숨어 있는 C언어는 '이렇게 하면 된다.' 보다 '이렇게 하면 안된다.'를 중점적으로 설명해야 하는 언어이다. 특히 UI나 퍼포먼스 중심으로 돌아가는 어플리케이션 영역이 메인인 언어들과는 달리, OS와 딱 붙어서 보안이나 안정성 및 호환성이 중시되는 시스템 프로그래밍 영역이 메인인 언어라 어느 정도 깐깐하게 접근하는 게 맞는 언어다.[70] C언어를 C++를 익히기 전의 워밍업 정도로 취급하는 사람들에게는 저런식으로 설렁설렁 쉽게 써진 C언어 교재들도 좋은 평가를 듣는 경우도 많지만, 본격적으로 C언어 프로그래밍을 하려는 사람들 사이에서는 해외 기준으로 K&R과 C Programming: A Modern Approach(일명 K&K)[71] 두 가지 정도만이 제대로 된 교재 취급을 받는다. K&R이 나온 지 오래된 책이기는 하지만 중요하고 핵심적인 사항들은 오늘날에도 변함없이 적용되는 내용들이다. K&R을 나온 지 오래 된 책이라고 무시하기엔 좋은 내용들이 굉장히 많다.

그러나, 분량이 많은 교재들은 그만큼 친절하고 자세한 설명을 동반하여 비교적 술술 읽히는 것에 반해, K&R은 짧은 만큼 매 문장 하나 하나[72]의 중요도가 높기 때문에 프로그래밍 초심자에게 추천할 만한 책은 아니라는 주장도 있다. 심지어 이 책의 한국어판 번역자는 아예 서문에서 대놓고 초심자에게는 적절한 책이 아니라고 했을 정도이다.

게다가 K&R이 좋은 교재인 건 맞지만 오늘날 K&R을 교재로 추천하기가 마냥 좋지만은 않은 이유 중 하나도 책이 너무 오래돼서 초창기 C언어의 유행이나 트릭 정도만을 담고 있고, 그 이후의 여러가지 패러다임의 변화 등을 반영하지 못하고 있기 때문이다.[73] 어차피 저런 걸 모두 담고 있는 교재가 없긴 하지만, 더하고 덜하고 정도의 차이는 있게 마련이고, 그 차이는 나중의 삽질과 시간 낭비로 반드시 뿌린 대로 거두게 된다. 정 K&R을 첫번째 교재로 삼기가 어렵다면 다른 데서 C언어의 기초를 먼저 배우고 복습 차원으로 K&R을 보는 것도 좋다. 그렇게 하면 K&R의 내용 중 구식인 것과 아닌 것을 스스로 가려낼 수 있게 될 테니까.

결론적으로 2020년대 기준 K&R은 초심자가 입문하기에는 좋은 책이 결코 아니다. 다만 중급자 정도의 실력이 되고 복습이 필요할 때, 구식 스타일의 코딩에 대해 알고 싶거나 근본적인 C언어 기능들에 대해 알고 싶다면 K&R을 보기에 좋다고 할 수 있겠다.


5.2. C Programming: A Modern Approach[편집]


  • 원 저자: K. N. King
  • 번역서: (없음)
  • 번역자: (없음)
  • 출판사: (없음)

저자의 명칭을 따서 속칭 "K&K"라고 불리며,[74] The C Programming language와 함께 C언어 표준을 준수하면서 세계적으로 인정받은 바이블격 도서 중에 하나다.[75] 다만, 2023년 현재까지도 한국어 번역서가 없어서 영어 원문을 봐야하는지라, C언어 첫 번째 기본 입문서로는 독해하기 어려울 수 있는 게 가장 큰 단점이다. 그나마 다행인 것은 대학교 이상 수준의 영어를 요구하지는 않아서, 수능 영어 독해할 수 있는 실력이면 그럭저럭 읽을 수 있다는 것이다. 결론적으로 수능 영어 독해할 수준이 되는 사람에게는 적어도 K&R보다는 훨씬 입문하기 좋은 책이다.

K&K 책의 분량만 보면 800페이지가 넘어가기 때문에, K&R에 비교하면 2배 이상으로 배울 것이 많아 보일 수도 있다. 하지만 이는 오히려 장점인데, K&R에서 다루지 않는 (비교적 최신 C언어 버전인) C99에 관한 내용까지 다루는 한편, 어려운 설명을 최대한 풀어서 썼다는 소리이기 때문이다.[76] 직접 읽어보면 나름 술술 잘 읽히고, 설명도 자세하고 친절한 편이며, 어려운 단어들은 최대한 안쓰거나 풀어 쓰려고 한 것이 느껴진다. 어렵기로 악명 높은 C언어의 포인터를 설명하는 부분도, 부가설명을 많이 하고 나름 그림까지 많이 동원해가며 이해하기 쉽게 설명하려고 노력한 흔적들이 보인다.

2008년에 마지막 최종본이 나왔기 때문에 코딩 스타일은 위의 K&R보다는 최신 트렌드를 따라가지만 밑의 여러 최신 책들보다는 구식이다. 정확히는 C99라는 1999년에 발표된 표준을 지키도록 안내하는데 그 덕분에 K&K로 C언어를 배우면 C99 스타일을 엄격히 지키는 코드를 작성할 수 있게 된다. 아쉬운 점은 2008년에 출판했다는 특성상 C11이나 C18 등의 최신 C언어에 대해서는 다루지 않기 때문에 최신 C언어 트렌드를 배우려면 다른 책을 추가적으로 공부해야 한다는 것.[77] 나쁘게 말하면 최신 용어나 개념에 대해서는 내용이 책에 없는 것이지만, 좋게 말하면 어떤 구식 컴파일러에서든 돌아가는 코드를 작성하는데에는 도움이 되는 책인 셈이다.[78] 그리고 C89와 C99 사이의 차이에 대해서도 엄격히 구분해서 안내하기 때문에 C89 방식으로 구닥다리 스타일 코드를 쓰는 것도 배울 수 있을 뿐만 아니라,[79] C89(즉 K&R에서 다루는 ANSI C)와 그 이후 C99 등의 나름 최신 C언어와의 차이를 익히는 데에도 쓸모가 있다.

K&K는 영문 원서 특성상 오프라인에서는 구하기 힘든 책이고 온라인 해외배송으로 시켜야 책을 볼 수 있는데, Wayback Machine 사이트에 원문 책이 통째로 공개되어 있으며 위키독스에 어떤 개인이 한글로 번역한 것도 공개되어 있으니 돈 없는 사람들은 이걸로 공부해도 된다. 다만 한글판에는 문제가 많은데, 우선 반 정도밖에 번역이 안 돼서 뒤쪽에 중요한 내용은 어차피 원서를 봐야 하고,[80] 공식 번역이 아니라 개인이 번역했기 때문에 번역 품질도 엄청 좋지는 않으며,[81] 실물 책으로 볼 수 없는지라 인터넷이 연결돼야만 볼 수 있다는 단점이 있다. 게다가 개인 번역자에 따르면, 번역권 문제 때문에 장래에도 위키독스 번역본은 공식 출판이 어려울 것 같다고 한다. 이런 저런 문제 때문에 수능 영어 독해할 수준이 되는 실력이면 원서를 구해 보는 것이 낫다.[82]


5.3. Modern C[편집]


  • 원 저자: Jens Gustedt
  • 번역서: 모던 C (전문가를 위한 C 작성법!)
  • 번역자: 남기혁
  • 출판사: 길벗

2019년 기준 C11/C18 표준과 현업에서의 C언어 프로그래밍 스킬 및 트렌드에 대해 입문자 수준부터 전문가 수준까지 깊이 있게, 정확히 체계적으로 설명하고 있다. 입문용으로도 좋으며, K&K나 K&R을 다 읽은 중급자 이상이 C11/C18에서 변한 표준을 알아보거나, 최신 C언어 트렌드를 알려고 할 때도 유용하다고 평가받는다. 2019년 12월 출시됐고, 번역서는 2022년 1월 중순에 길벗 출판사를 통해 나왔다. 영어로 된 원서는 인터넷에서 pdf로 받을 수 있다. 다만, 원서의 경우 K&K보다도 영어 수준이 높기 때문에 수능 독해 이상의 영어 실력이 필요하며,[83] 이미 시중에 그럭저럭 번역이 잘된 한국어 번역본도 있기 때문에,[84] 굳이 원서로 공부할 필요는 없을 것으로 보인다.


5.4. C언어 펀더멘탈[편집]


  • 저자: 전웅
  • 출판: 한빛미디어

한국인이 쓴 책 중에서는 C99 표준을 제대로 정확하게 설명하는 몇 안되는 중급서. 실제 프로그래밍 도구의 사용법이나 하드웨어 특화적인 내용을 설명하지는 않기 때문에 직접적으로 곧바로 프로그래밍 스킬을 늘려주지는 않지만, C언어 전반에 대해 단단한 기반 지식을 형성해주는 책이라 할 수 있다. C언어 구석구석의 내용까지 충분한 설명 및 예제와 함께 다루기 때문에 구해서 읽을 수만 있다면 C언어 자체에 대한 이해에 큰 도움이 될 것이다. 일단 이 책의 범위까지만 익히면 암호문 같던 컴파일러의 에러 메시지가 해석이 되고, 컴파일러 매뉴얼의 각 항목들이 왜 그렇게 쓰여졌는지가 이해가 된다. 그러나 유감스럽게도 현재는 절판되어 있다. 저자는 한때 han.comp.lang.c 뉴스 그룹과 KLDP에서 답변자로서 왕성한 활동을 보인 바 있다.

서평을 검색해보면 호불호가 극명하게 갈리는데, 이는 해당 책의 관점 때문이다. C언어 표준의 관점에서 C언어를 설명하고 있기 때문에 용어 자체가 생소한 것들이 많은데, 컴파일러 대신 구현체(implementation)와 같이 일반적으로 잘 쓰이지 않는 용어를 사용한다던가, 정의되지 않은 동작(Undefined behavior)과 같이 사전 지식 없이는 오해되기 쉬운 표준의 용어들을 빈번하게 사용하기 때문에 적절한 사전 지식이 없다면 글 자체를 읽기가 어렵다. 한편으로는, 널리 사용되지 않는 오래된 아키텍처의 범위까지도 포괄하기 때문에 일반적인 프로그래머들이 받아들이기 힘든 부분들도 많이 포함되어 있어, 이해하기 어렵다거나 불필요한 내용이 너무 많다는 악평들이 많다. 만약 알기 쉬운 입문서를 기대하고 이 책을 읽는다면 큰 실망을 하게 될 것이다.

반면에 중급자의 수준에서 C언어의 깊이있는 부분을 제대로 알고 싶다면, 이만큼 자세하고 알기 쉽게 설명한 책도 달리 없다. 적어도 한국에서는, C FAQ 온라인 번역문 이외에는 이와 비슷한 내용을 접하기조차 쉽지 않다. C++의 경우에는 언어가 복잡한 만큼 오히려 Effective C++과 같은 중간 교량 역할을 하는 책들이 있지만 순수 C언어는 오히려 그런 책들이 드물고, 따라서 이 책의 가치는 더더욱 높을 수밖에 없다.


5.5. C Primer Plus 6th Edition[편집]


  • 원 저자: Stephen Prata
  • 번역서: C 기초 플러스 6판
  • 번역자: 윤성일, 이선민, 조혜란
  • 출판사: 성안당

K&R이나 K&K 같은 두 바이블 양대 산맥의 도서만큼은 아니더라도 C언어 최신 표준이 충실하게 반영된 도서이다. 새로운 표준이 등장할 때마다 여러 차례 개정돼서 최신 개정판 기준으로 1000페이지가 넘는다. C언어 학습용 첫 번째 도서로써는 두꺼운 분량이라는 진입 장벽이 있지만 C11 표준을 다루는 몇 안 되는 번역서라서 이를 제대로 배우고자 하는 학습자에게 십중팔구 추천서로 거론되는 책이다. 한국에서는 'C 기초 플러스'라는 번역서로 현재 C11 표준까지 반영된 6판까지 출판됐다. 하지만 내용 자체가 너무 길어서 장황하게 보이기 쉬운 탓인지 입문자에겐 중도 포기하기 쉬운 게 단점이다.


5.6. 열혈 C 프로그래밍[편집]


  • 저자: 윤성우
  • 출판: 프리렉(2003년) → 오렌지미디어(2010년)

한국 한정으로 유명한 C언어 도서 중에 하나로, 초판 당시엔 '열혈강의 C 프로그래밍'이었으나 개정되면서 책 이름도 '강의' 글자만 빠진 이름으로 바뀌었다. 컴퓨터 하드웨어 구조에 대한 기초 지식이 없는 입문자에게 추천하는 책으로 널리 알려져 있다. 그런 독자를 타겟으로 집필된 기본서다보니 어려운 개념은 빠져있는데 여기까지는 초보자용 C언어 기본서로서 납득할 수 있는 부분이지만, 하필 중요한 개념 중에서도 오개념이 많이 존재하는 데다(대표적으로 Call by Reference) 소개하는 문법마다 C언어 표준인지 비표준인지 명확하게 구분하지 않아 전문가들에겐 혹평을 받는 책이다. 거기다 소위 트릭이라고 말하는 함정들을 거의 다루지 않는다. C언어 표준을 제대로 공부하고 싶은 학습자, C언어로 실무를 봐야 하는 현업자에겐 추천하지 않지만, 프로그래밍 입문자에겐 이해하기 쉽게 서술되어 있어서 C언어 첫 번째 교재로써는 나쁘지 않다. 그렇기 때문에 이 책으로 입문하는 대신 제대로 배우고 싶다면 다른 좋은 기본서 한 권 더 사서 복습 겸 공부하는 것이 좋다.


5.7. 이것이 C 언어다 서현우의 C 프로그래밍 정복[편집]


파일:이것이C언어다.jpg
저자서현우
출판한빛미디어

뇌를 자극하는 C 프로그래밍의 저자가 집필한 C언어 입문서. 보다시피 표지에 건담 프라모델이 있는 것이 특징.[85] 건담...? 입문서이기 때문에 열혈 C 프로그래밍과 비슷한 한계를 지니고 있지만 C언어 첫 번째 교재로서 많이 추천받는 교재로 평가받고 있다.[86] 또한, 2019년부터 책의 내용은 거의 그대로 유지한 채 개정 연도인 2019년에 맞춰 컴파일러를 Visual Studio 2013에서 2019로 바꾸고, 가독성과 가시성을 높인 개정판 '혼자 공부하는 C언어'가 출판되고 있다.


5.8. 터보 C 정복[편집]


  • 저자: 임인건
  • 출판: 가남사(1999)

PC통신 시절 국내 한정으로 C언어 레퍼런스 중 하나만 꼽으라고 하면 열의 다섯은 주저 없이 이 책을 꼽았던 명저로, 특히 배열과 포인터에 관한 설명은 당시 국내 도서 중에서는 비교 대상이 없다는 평가를 받았던 책이다. 1299페이지라는 압도적인 두께가 흠이긴 하지만, 그만큼 물샐 틈 없이 설명을 해주고 있다.

다만, 지금 시점에서 읽겠다면 시대적인 한계와 Turbo-C를 기준으로 쓰여진 책이라는 점은 고려를 해야 할 것이다. 1991년에 벌써 5판이 발매된 너무 오래된 책이기도 하거니와, 요새처럼 C언어 표준 드래프트 문서를 곧바로 받아볼 수 있는 시대에는 그 자세한 설명도 의미가 퇴색될 수밖에 없다.

저자인 임인건씨는 도스 시절 한라프로라는 인기 한글 라이브러리를 만들었던 사람으로, 그의 PC통신 아이디인 터보이빨(turbo28)과 프로그래머 십계명으로도 유명하다.


5.9. 기타 관련 자료 소개[편집]




6. C언어용 개발 도구들[편집]


마이크로소프트에서 만든 IDE. Visual Studio 2019 16.8이후 부터 C언어 표준과 C++언어 표준을 각각 지원한다. 출처[88]

IntelliJ IDEA로 유명세를 날리고 있는 JetBrains에서 개발한 C/C++ IDE이다. 인터페이스가 직관적이고 다양한 종류의 컴파일러로 크로스플랫폼 개발이 가능하다는 장점이 있다. CMake 기반으로 프로젝트를 생성하며, 유료 구독권 형식으로 판매되고 있기 때문에 월/년마다 정기 구매해야 한다. 윈도우 환경에서 WSL에 접속하여 리눅스 기반 컴파일을 할 때 가장 편리한 툴이기도 하다. 또한 유일하게 제대로 된 윈도우 환경에서의 C언어 IDE이기도 하다.

애플에서 직접 제작한 IDE. 예전에는 GCC를 그대로 가져다[89] 썼지만, 요즘은 LLVM이라는 새 컴파일러 셋으로 이주했다.[90] C언어, C++, Objective-C, Swift 등의 언어를 컴파일할 수 있다. macOS용 애플리케이션이나 iOS용 앱을 제작하는 데는 필수이다.

마이크로소프트텍스트 에디터로, C/C++ 플러그인을 설치하면 VS와 동일한 인텔리센스가 제공된다. IDE가 아니므로 컴파일러와 디버거, 빌드 툴 등을 직접 세팅해야 하지만, 그만큼 VS에 비해 가벼우므로 환경 구성법만 안다면 단순 코드 작성에는 이쪽이 나을 수 있다.

오픈 소스 IDE 중 하나로, 원래 오라클에서 배포했으나 2016년에 아파치 소프트웨어 재단으로 소유권이 이전됐다.

오픈 소스 IDE인 이클립스의 C언어/C++ 개발용 플러그인이다.

C/C++/Fortran용 오픈 소스 통합 개발 환경이며 라이선스는 GPL 3.0 버전을 따르고 있다.

GPL 라이선스를 따르는 오픈 소스 IDE. 2006년 이후로는 소식이 없다. 설치 속도가 빠르고 편리하지만 리소스 에디터가 없다. 설치할 때 기본적으로 같이 깔리는 컴파일러는 MinGW+GCC.

오웰(Orwell)이 Bloodshed Dev-C++ 4.9.9.2 소스로 개발하고 있는 IDE이다. 2015년 이후로는 무소식.

볼랜드에서 개발한 유료 IDE. MS-DOS 시절에는 터보 파스칼과 함께 빠른 컴파일 속도로 인기가 많았으나, 윈도우의 시대가 오면서 점차 Visual Studio에 밀리게 됐다. 단순 컴파일러는 현재 무료로 다운로드 가능하다. 볼랜드가 개발 언어 쪽만 전담하는 코드기어라는 자회사를 설립하면서 모든 권한을 넘겼는데, 코드기어가 엠바카데로와 합병되면 홈페이지가 여러 차례 이동됐다. 참고로 델파이의 개발도 볼랜드 → 코드기어 → 엠바카데로의 순서로 넘어갔다.

볼랜드 Turbo C++를 Windows 7, 8, 10을 위해 확장한 버전. 그 외에는 소식이 없다.

도스 시절 끝무렵에 DOS/4GW라는 메모리 확장 프로그램과 함께 잠깐 동안 쓰였다(8.5 버전부터 포함됐다). MS-DOS의 640KB 메모리 한계를 넘어 64MB까지의 메모리를 손쉽게 사용할 수 있게 해 주었기 때문이다. 그 당시의 게이머들이라면 실행 초기에 DOS/4GW라고 뜨던 안내문을 기억할 것이다. 공식 사이트가 위키 형식으로 되어 있다. Open Watcom Public License라는 라이선스를 따른다.

A Retargetable C Compiler: Design and Implementation라는 책에 소스 코드가 실려있는 C언어 컴파일러. 데이브 핸슨(Dave Hanson)과 크리스 프레이저(Chris Fraser)가 만들었다. 아래에 설명하겠지만 이를 기반으로 하는 컴파일러가 몇몇 존재한다.

위의 LCC를 바탕으로 한 윈도우용 C언어 컴파일러. 제이컵 나비아(Jacob Navia)가 만들었다. 월간 마이크로소프트웨어를 통해 국내에 소개된 적이 있다.

위의 LCC를 기반으로 스웨덴의 펠레 오리니우스(Pelle Orinius)가 개발한 컴파일러. 윈도우 전용의 프리웨어 통합 개발 환경이다. MS와는 다르게 C99는 물론이고, C11도 완벽 지원하는 컴파일러를 제공한다. 게다가 툴이 가볍고 무료다.

인텔에서 자기들이 만든 CPU에 최적화된 코드를 만들어 낼 수 있도록 직접 제작한 컴파일러 스위트. 사용 설명서 시작 부분에 '인텔에서 제작하지 않은 CPU에서 구동 시 최적화된 성능을 보장할 수 없습니다.'라고 적혀 있다.# C언어/C++ 및 Fortran 컴파일러를 제공한다. 윈도우에서 설치 시, Visual Studio에 애드온 형태로 설치가 돼서 Visual Studio 내에서 기존 컴파일러 대신에 사용할 수 있다.
컴파일러 이외에도 어셈블리어 단위로 한땀한땀 손으로 최적화한 수치해석 라이브러리#와 다수의 컴퓨터가 동시에 컴퓨팅을 할 때 사용되는 MPI 라이브러리, 코드의 성능을 분석해주는 도구#, 코드 최적화를 도와주는 도구#, 성능에 악영향을 주는 에러를 찾아주는 도구#, 등을 하나로 묶어서 Intel Parallel Studio라는 이름으로 판매한다. 인텔 CPU와 인텔 가속기를 수만 개씩 사용하는 슈퍼컴퓨터 등에서는 매우 자주 쓰이는 유용한 도구.
Intel Parallel Studio는 학생 대상으로 무료로 사용 가능(윈도우용은 Visual Studio 통합 포함)하고, 수치 해석 라이브러리는 일반인 대상으로 무료 사용 가능하다.


6.1. 관련 문서[편집]




7. 여담[편집]


C언어의 추종자들은 다음과 같이 말한다.

전산과 신입생은 CPU부터 시작해서 C언어를 활용하는 데까지 차곡차곡 기초를 닦아야 합니다. 저는 솔직히 너무나도 많은 컴퓨터 관련 교육 과정들이 자바가 가장 좋은 초보자용 언어라고 선전하는 현실에 질려 버렸습니다. 흔히 자바는 쉽고, 따분한 문자열이나 malloc과 같은 골칫덩어리를 다루는 과정에서 혼란을 겪지 않으며, 아주 큰 프로그램을 모듈로 나눠서 만들 수 있는 근사한 객체 지향 프로그래밍 기법을 배울 수 있다는 화려한 이유들이 따라 나옵니다. 하지만 여기에는 교육적인 재앙이 있습니다. 졸업생들은 하향 평준화돼 러시아 페인트공 알고리즘[91]

을 여기저기에 만들어내며, 심지어 자신의 잘못을 인식조차 못할 겁니다.[92] 펄 스크립트에서 이런 사실을 결코 볼 수 없을지라도, (물론 어렵지만) 기본적으로 문자열이 무엇인지 아주 깊은 단계에서 이해하지 못하기 때문입니다. 다른 이들이 뭔가를 잘하도록 가르치길 원한다면, 기초부터 시작해야 합니다. 이는 마치 베스트 키드와 비슷합니다. 마루바닥을 쓸고 닦고 쓸고 닦고, 이렇게 3주만 하면, 자연스럽게 목표물을 향해 발이 쭉쭉 뻗어나갑니다.

-- 조엘 온 소프트웨어 (조엘 스폴스키)


C언어를 깊게 공부하면 시스템의 저수준에 대해서 이해하는데 많은 도움이 되지만, 그렇다고 해서 그것을 알아야만 잘 짜여진 프로그램을 만들 수 있는 것은 아니다. 그리고 컴퓨터의 속도가 매우 빨라지고 보편화된 현대에는 고도로 추상화되고 생산성이 높은 언어에 대한 수요가 매우 크기 때문에, 다른 언어를 먼저 공부하는 것도 나쁘지 않은 선택이다.[93]

다소 어렵긴 어려운지, 대학교에서 C 언어를 공부하는 많은 학생들이 공부 공부 대상과 똑같은 학점을 받는 경우가 있는 모양.


7.1. C언어의 다음 언어?[편집]


B언어 기반으로 C언어가 탄생하면서 한때는 프로그래머 유머로 'C' 다음 언어가 과연 'D'일지 'P'일지[94] 묻는 이야기가 있었지만 C++ 같은 것도 나왔고, ANSI C, C99, C0x C11,[95] C18 등으로 가지를 뻗으며 진화 중인 현재의 C언어에게는 이미 과거의 이야기가 되어버렸다.[96] 사실 D라는 프로그래밍 언어도 있기는 있다. Birth와 Death 사이의 C언어 벨 연구소가 아닌 디지털 마르스(Digital Mars)에서 만든 것이기는 하지만. C++와의 문법적인 호환성은 없지만 C++의 단점을 보완하면서 고생산성을 추구하는 하이 레벨 언어들의 특성들을 반영한 네이티브 컴파일 언어라 사실상 C++의 리엔지니어링 버전처럼 취급하고 있다. ABA Games의 게임들 대부분도 D언어로 만든 것이다.

잘 모르는 사실이지만 1997년에 등장한 E라는 프로그래밍 언어도 있다. 등장한 순서만 따지면 2001년 12월에 등장해서 2007년에 정식판이 나온 D언어보다 훨씬 먼저 나온 셈. 그 외에 단일 영문자 이름을 지닌 프로그래밍 언어는 A, F, G, J, K, P, Q, R, S, T, V, Z가 있다.


7.2. C언어와 '++', '#'[편집]


C언어의 문법으로 나오는 ++는 초기화된 변수의 값을 1 증가시켜 대입하는 연산자이다. 다시 말해 C++에서 ++은 C를 1 증가시켜 대입했다는 말. C\#에서 #은 ++++이다. (++이 위 아래로 두 개) 또한 C♯, 그러니깐 음악에서도 의미를 따왔다.

++와 반대로 1을 감소시키는 연산자 \-\-를 이름에 사용한 C\-\-라는 언어도 존재하는데 보다 고수준 언어의 컴파일러가 중간 언어로 생성하는 것을 목적으로 한 저수준 언어로, 주로 Haskell과 같은 함수형 언어들의 컴파일러가 이 C--를 사용한다.


7.3. Write in C[편집]


C언어와 관련해서 다음과 같은 노래도 있다. 참고로 다음 노래는 비틀즈Let It Be를 패러디한 것.



When I find my code in tons of trouble

내가 짠 프로그램에 문제가 가득하단 걸 알았을 때

friends and colleagues come to me,

친구와 동료들이 다가와

speaking words of wisdom…

슬기로운 말을 해주었네

…"write in C"

"C언어로 짜"

And as the deadline fast approaches,

마감은 빠르게 다가오는데

and bugs are all that I can see

버그밖에 보이지 않아

Somewhere someone whispers:

어디선가 누군가가 속삭였지

"Write in C"

"C언어로 짜"

Write in C, Write in C, Write in C, Write in C,

C언어로 짜, C언어로 짜, C언어로 짜, C언어로 짜

LOGO's dead and burried,

LOGO는 이미 죽어서 묻혀버렸어

Write in C

C언어로 짜

I used to write a lot of FORTRAN

예전엔 포트란으로 많이 짰었어

For science it worked flawlessly

수식 계산에선 나무랄 데 없었지만

Try using it for Grahpics!

포트란으로 그래픽을 해 보라고!

Write in C!

C언어로 짜

And if you've just spent nearly 30 hours

어셈블리 디버깅을

debugging some assembly

30시간 정도 해 봤었다면

Soon you will be glad to

금방 고마움을 느낄 거야

write in C

C언어로 짜

Write in C, Write in C, Write in C, Write in C.

C언어로 짜, C언어로 짜, C언어로 짜, C언어로 짜

BASIC's not the answer,

베이식으론 해결이 안 돼

Write in C.

C언어로 짜

Write in C, Write in C, Write in C, Write in C.

C언어로 짜, C언어로 짜, C언어로 짜, C언어로 짜

PASCAL won't quite cut it,

파스칼로는 부족한 걸

Write in C!

C언어로 짜


이 노래에 대한 또 다른 패러디로, Go 버전인 Write in Go도 존재한다.


8. 관련 문서[편집]




9. 관련 커뮤니티[편집]




파일:CC-white.svg 이 문서의 내용 중 전체 또는 일부는 2024-06-18 04:07:46에 나무위키 C(프로그래밍 언어) 문서에서 가져왔습니다.


[1] 데니스 리치와 브라이언 커니핸 지음. #[2] 켄 톰슨과 브라이언 커니헨은 발전 과정에 기여했을 뿐, 공동 개발자가 아니다. 켄 톰슨은 유닉스 OS와 C언어의 전신인 B언어를 만들었고, 브라이언 커니핸은 K&R로 불리는 The C Programming Language라는 C언어의 역사에 중요한 책을 썼다.[3] 좋게 말하면 프로그래밍 스크립트 제작에 제한이 거의 없다는 뜻이 되지만, 다르게 말하면 스크립트에 에러나 버그 등의 문제가 여럿 나타날 수 있다는 확률이 다른 언어들에 비해 상대적으로 높다는 얘기다.[4] 단순히 고성능 라이브러리를 가져다 쓰는 게 아닌 고성능이 필요한 라이브러리 그 자체[5] 현재 Electron은 마이크로소프트에게 인수됐으며, 스카이프Visual Studio Code의 개발에 요긴하게 사용됐다.[6] 윈도우, 리눅스, 유닉스 커널의 핵심부는 모두 C언어로 짜여 있다.[7] 리눅스 외의 커널에서는 C++도 사용되곤 한다.[8] 비트 수준 연산을 많이 사용하고, 속도도 빨라야 한다.[9] PLC와 연계해서 사용하기도 하며, 주로 비전쪽에 사용한다. 필요에 따라 고수준 언어를 사용하기도 한다.[10] 특히나 양자역학 등 과학 계산 용도. 쓸모 있는 데이터 하나를 얻기 위해 엄청난 연산량을 요구하는 이런 분야에서는 계산 하나하나의 사소한 퍼포먼스 저하가 연구 성과를 심하면 문자 그대로 몇 년씩 늦추므로 극한의 퍼포먼스가 필요하다.[11] 유닉스는 처음에는 어셈블리어로 만들어졌지만 점차 C언어로 대체됐다. 그리고 그 덕분에 이식성을 확보하여 여러 기계로 퍼져나갔다.[12] 천공 카드의 잔재로는 제일 왼쪽 몇 칸은 주석, 그 다음 몇 칸은 정의하는 식으로 코딩할 때 칸까지 맞추어야 하는 규칙이 있다. 당시 프로그래밍 자료를 보면 요즘 사람들의 눈에는 모눈 원고지로밖에 보이지 않을 것이다.[13] 그리고 코볼은 원래는 프로그램 코드가 업무 서류로도 사용이 가능하도록(!) 설계가 되어 있어서 그렇다. 주석이 코드고 코드가 주석일 경우의 아주 나쁜 사례. 2000년대에 애자일 프로그래밍에서 코드를 쉽게 읽을 수 있게 한다는 개념과는 다르다. 이쪽은 추상화로 마치 자연어처럼 읽히는 코드를 써서 주석 자체를 코드로 최대한 대체해서 주석이 코드의 변화를 못 따라가는 불상사를 방지하는 것이다.[14] 쉽게 말해서, OS의 특정 기능이 느리면 그 기능을 사용하는 애플리케이션 전부가 덩달아 느려진다.[15] unsafe 콘텍스트로 C언어가 할 수 있는 모든 것을 동일한 성능으로 할 수 있다.[16] 하지만 2020년 5월 기준 C언어가 다시 자바를 누르고 1위를 차지했는데, C언어가 최근 1년간 점유율이 2.82%나 증가해서 나온 결과다.[17] 다만, C언어 표준 자체가 많은 부분을 '모든 컴파일러에 동일하게'가 아니라 구현체에 따라(implementation-dependent) 정의하게 하므로 이런 부분들에 대해서는 전부 각 플랫폼마다의 특성을 따로 반영하여 코딩해주어야 한다.[18] 실제로 요즘 나오는 C언어 교재 중에는 후반부에 OOP 챕터도 넣어 놓은 경우가 가끔 있다.[19] 직역하면 '바퀴의 재발명'이다. 이미 다 존재해서 가져다 쓰기만 하면 되는 것을 괜히 고민하면서 또 만들어내는 것을 비판하는 데 쓰이는 문구이다. 다만 실전 개발에서 안 좋은 습성이고, 교육용으로는 일부러라도 한 번씩은 거치게 하는 편이다. 재발명하면서 기반 시스템의 구조에 대해서 알아갈 수 있다는 것이 이유다.[20] 유닉스의 POSIX API, 윈도우의 Windows API[21] 원래는 zero-sized array라는 일종의 테크닉이었는데, C99에서 정식으로 편입됐다.[22] 버퍼 오버플로 취약점을 이용하면 배열 경계를 넘겨서 그 위에 선언한 변수들을 하나하나 덮어쓰기가 되고, 최종적으로 리턴 주소(함수의 실행을 끝낸 후 다시 함수를 호출한 곳으로 돌아가는 주소)까지 덮어쓰는 게 가능해진다. 이를 응용하여 기존 스택 프레임에 바이트를 조작하여 실행 가능한 기계어 코드를 주입하고, 반환 주소에 주입한 코드의 처음 주소를 가리키게 하여 공격자가 인위적으로 만들어낸 코드를 실행시키는 공격 기법을 코드 인젝션(Code Injection)이라 한다. 기본적으로 이 취약점을 가진 프로그램을 네트워크에 물리면 원하는 동작을 원격에서 실행할 수 있게 되며, 최악의 경우는 루트 권한으로 실행되는 프로그램에서 리턴 어드레스를 셸을 실행하는 코드가 있는 곳으로 덮어쓰면 루트 계정을 원격으로 탈취당할 수도 있다. 이를 버퍼 오버플로 취약점이라고 한다. 1998년 전 세계 서버를 감염시킨 모리스 웜도, finger라는 유닉스 유틸리티의 버퍼 오버플로 취약점을 이용했다. 이 웜은 쉽게 들통나지 않도록 여러가지 위장 전략이 많이 포함되어 있었고, 코넬 대학은 결국 서버를 다운시키는 등의 조치를 취했지만 전 세계로 퍼져나갔다. 당시 이 웜을 만든 23세의 모리스는 벨 연구소에서 유닉스의 로그인 암호화를 담당했던 사람의 아들이었고, 현재 MIT 대학의 교수다. 2001년 한국에도 사이버 대란을 일으킨 코드 레드 의 경우 전세계의 서버 몇 십만 개를 감염시킨 사례가 있다. 이 취약점 때문에 경계 체크를 안 하는 gets 같은 표준 함수 여러 개를 deprecated(사용 자제) 처리해야 했고, printf의 포맷 문자(\\n) 하나가 날아갔다. 심지어 운영체제 자체에서 버퍼 오버플로 방지 메커니즘을 제공하기까지 하는데도 간간히 뚫리는 게 이 취약점이다. 물론 공돌이들이 손 놓고 있는 게 아니니 방지책도 많이 마련되어 있긴 하지만 어쨌든 뚫릴 취약점은 뚫린다. 2014년 OpenSSL에 생긴 재앙적인 버그인 하트블리드도 근본적으로는 C언어가 배열의 경계를 체크하지 않는다는 점에서 생겼다.[23] visual studio 2012 버전부터 함수에 _s 가 붙은 (printf_s, scanf_s 등) 보안을 위한 함수가 추가됐다. 기존의 printf 같은 함수는 그냥 쓸 수 있지만 scanf와 같은 함수를 쓰려고 하면 보안에 따른 에러 메시지가 뜬다. Visual Studio에서는 _CRT_SECURE_NO_WARNINGS를 전처리기에 선언하라고 안내한다. 그리고 이 안전 함수들은 C11에도 추가됐다.[24] 예를 들어 대문자 판별을 위해 c >= 'A' && c <= 'Z' 와 같이 쓰는 경우가 많은데, 이는 EBCDIC 코드와 같이 A부터 Z까지 연속이 아닌 문자셋에서 문제가 발생한다. 따라서 isupper()와 같은 함수로 판별하는 것이 바람직하다.[25] 특히 주로 문자열 상수(String literal. "str" 같은 따옴표로 둘러싸인 부분을 뜻함.)에 대해 잘못 이해하는 경우가 많은데, 문자열 상수 자체는 '문자열'이나 '값'이 아닌 '배열'이며, 결과적으로는 해당 배열의 첫 번째 글자가 저장된 위치를 가리키는 '주소 값'이 된다. 이걸 제대로 다루기 위해서는 악명높은 포인터까지 완전히 배워야 하기 때문에, 언어 난이도가 급상승한다.[26] 비교삼아 다른 언어의 문자열 처리 방식을 살펴보자면, 파스칼과 같은 언어는 문자열의 길이를 맨 앞에 따로 저장하여 처리한다.#[27] 덕분에 교재마다 접근 방법이 달라지는데, 어떤 교재에서는 '비슷하다'고 전제한 뒤 차이점을 설명하는 식이고, 어떤 교재에서는 '다르다'고 전제한 뒤 공통점을 설명하는 식이다. 어느 쪽으로 접근하더라도 함정과 예외가 상당히 많아지기 때문에, 둘의 차이를 충분히 구분할 만한 상황적 경험이 적을 수밖에 없는 초심자들에게 결국은 이해보다 암기로 흘러가고, 어렵게 느끼는 경우가 많다.[28] 소스 수준에서 play_sound라는 함수를 어디엔가 정의를 해 놓고 다른 소스 파일에서는 play_sound라는 이름만 같은 다른 함수를 만들고 같이 컴파일하면 에러가 난다. 더욱 골때리는 건, 컴파일 단계는 무사히 진행되는데 링커 수준에서 에러가 난다는 것. 따라서, 외부 소스 파일로 노출시키고 싶지 않은 내부 함수들은 static 지시자를 앞에 붙여줘서 이름 유출을 방지하고 다른 소스에서 쓸만한 함수는 <모듈 이름>_함수 이름(e.g. NamuWiki_add_document)을 사용하여 이름 충돌을 최소화 하는 게 일반적이다. 이는 사실 장점이자 단점인데, C++에서는 컴파일된 심볼명에 개별 지시자를 넣어 다른 네임 스페이스나 클래스의 함수 등이 충돌이 일어나지 않으나, 문제는 C++ 표준에는 이 Naming Mangling에 대한 표준이 정의되어 있지가 않아 컴파일러마다 호환이 되지 않는다는 점이다. 그러나 C언어는 이러한 네이밍을 하지 않기 때문에 컴파일러 호환성 유지가 가능한 한 가지 방법이며, 이 경우 C++로 구현되어 있으나 외부 API를 제공하는 경우 C언어형 네이밍 스킴을 사용한다.[29] 특히 정수형 계산 관련하여 골치 아픈 사례들이 많은데, 대표적으로는 부호형(signed)과 무부호형(unsigned)이 만나 음수가 양수로 변하는 마술이 있다.[30] 음수도 true로 처리된단 뜻이다. 없으면 define 문으로 선언해서 쓰면 된다.[31] 다만, C99에서 _Bool 타입이 생기긴 했다. stdbool.h를 포함할 경우, 그냥 bool로 사용도 가능하긴 하다. 왜 그냥 bool로 안 넣었냐면, 기존 프로그램 중에 bool 이름을 사용한 코드가 있을 가능성이 있기 때문. 반면, 언더 스코어(_) 뒤에 대문자가 오는 이름은 C언어 표준에서 사용하지 말라고 먼저 명시해놨기 때문에 이것을 사용한 _Bool이 됐다.[32] 소켓 프로그래밍이 보통 이렇다. 그리고 사실 구조체, 배열, 공용체는 다른 타입과의 포함 관계라서 문법적으로 같은 주소/다른 타입이 가능한 경우이기도 하다.[33] 서문 내용: C is a relatively "low level" language. This characterization is not pejorative; it simply means that C deals with the same sort of objects that most computers do, namely characters, numbers, and addresses. ......... Again, because the language reflects the capabilities of current computers, C programs tend to be efficient enough that there is no compulsion to write assembly language instead. ......... Although C matches the capabilities of many computers, it is independent of any particular machine architecture, and so with a little care it is easy to write "portable" programs...[34] 예를 들어 1부터 100까지 루프를 통해 합을 구할 때, 그냥 5050을 대입해도 상관없다.[35] 예를 들어 시간 지연을 위해 집어넣은 루프를 없애 효과가 없는 경우가 발생한다.[36] 또 다른 예로는, if (signed_i+1<signed_i) printf("OVERFLOW!"); 같은 오버플로우 검출 코드는 항상 틀린 것으로 간주하여 그냥 없애버릴 수 있다.[37] 해당 규칙은 함수 호출이나 volatile인 대상체에 대한 접근 포함이다. 따라서 volatile 키워드를 사용했다고 해서 최적화를 항상 막을 수는 없다.[38] 컴파일러의 판단 기준은 C언어 표준의 규칙과 그로 인한 추상머신의 동작이다. 어떤 정의되지 않은 동작이 특정 하드웨어에서는 정상적인 동작을 가진다 해도, 컴파일러는 그런 상황 자체가 없을 것이라 가정하고 최적화를 진행할 수 있다.[39] 대표적으로는 부호있는 정수형의 오버플로와 쉬프트 연산 등이 있다. 예를 들어 if (signed_i+1<signed_i) f(); 과 같은 코드를 컴파일러는 항상 거짓이라 판단하여 지워버릴 수 있다. 부호있는 정수형의 오버플로가 나머지 연산으로 정상동작하는 환경에서조차도.[40] 현재는 Python이 1위를 차지하고 C언어는 2위, Java는 3위로 밀려났다. 4위와 5위는 각각 C언어의 파생언어(라고는 하지만 C언어와는 용도가 전혀 다르다.)인 C++C\#이다.[41] "For many years Visual Studio has only supported C to the extent of it being required for C++". 링크 참고.[42] C언어는 애초에 유닉스 운영체제를 만들기 위해 탄생한 언어이기 때문에 유닉스 운영체제와 시스템 궁합이 좋은 편이다.[43] 사실, Clang은 GCC를 대체하는 것이 목표기 때문에 macOS 외에 FreeBSD 등의 유닉스 계열 운영체제에도 사용된다.[44] MSVC의 경우 /Wall /WX /Za /permissive-[45] 베릴로그 HDL에서도 블럭을 이렇게 묶는다.[46] 때문에 대부분 언어에서는 if에 bool 값이 아닌 int 값 등이 들어오면 에러를 내며, 일부 언어에서는 if 같이 비교 연산이 필요한 곳에서 대입 연산을 쓰면 컴파일 에러를 낸다.[47] 1을 곱하거나 나눠서 달라지는 게 없다.. 만약 2를 곱하고 나누고 싶다면 시프트 연산을 사용하면 된다.[48] 라틴어는 지금의 프랑스어, 이탈리아어, 스페인어, 포르투갈어, 루마니아어의 기원이자 영어, 네덜란드어, 독일어에 지대한 영향을 끼친 언어이다. C언어도 후에 나온 프로그래밍 언어의 뼈대가 되는 문법을 만드는 데 지대한 영향을 끼친 것이다.[49] Python과 그에 영향을 받은 언어들은 이런 영향에서 약간 자유롭다. 이런 언어들은 C언어와 중괄호가 아닌 들여쓰기로 블럭을 구분하는 특징이 있다[50] fopen() 등의 결과로 반환되는 FILE 포인터를 파일 객체에 대한 포인터로 보면 이해가 쉽다.[51] C언어족 언어, C언어계 언어, C과 언어, C목 언어 등으로 번역할 수 있다.[52] ISO C와 ISO C++의 차이, N4860 C.5 C++ and ISO C 참고.[53] 서울대학교(자연대, 공대), 카이스트, 포항공대 모두 공통졸업요건으로 CS101 수강을 요구하는데, 세 학교 중 가장 끝까지 C를 고집했던 포항공대 학부생들의 반발이 적지 않았다. 2010년대 후반까지만 해도 C로 CS101 과목을 계속 돌릴 태세를 취하다가, 결국 2023년에서야 다른 두 학교를 따라 Python으로 변경.[54] C 표준에는 스택과 힙이라는 단어들이 단 한번도 나오지 않는다.[55] 정확히는 화이트 스페이스 문자들인 뉴 라인, 탭, 스페이스를 구분하지 않고 아무 whitespace 하나 이상은 공백 하나로 계산된다. 예외는 C언어/C++의 문법의 일부가 아닌 매크로로서, 무조건 뉴 라인을 이용해서 매크로의 끝을 구분지어야 하며 여러 줄에 걸쳐 작성할 경우 뉴 라인 이전에 역슬래시를 하나 추가해야 한다.[56] for(i=0;i<n;i++) { work(); } 이런 식으로 쓰는 사람들도 있기는 있다. for과 if 등은 한 함수를 실행시킬 거면, 중괄호를 안 쳐도 되긴 하다. 다만, 들여쓰기 잘못하면 피바람 분다. 함수가 한 개든 여러 개든 그냥 전부 중괄호를 치는 습관을 들이자.[57] Yoda condition이라고 불린다. 스타워즈요다가 문장에 주어의 위치를 도치하던 것에서 유래.[58] expression의 l-value에는 수정할 수 있는 값(a와 같은 변수 등)이 와야 하기 때문이다.[59] gcc 4.8 기준으로 'suggest parentheses around assignment used as truth value'(값을 진리 값으로 사용하는 assignment는 괄호를 씌우는 것을 권장함)라는 워닝 메시지가 뜬다.[60] 보통 if 문 내부에 의도된 대입 연산자의 경우에는 ((result = fn(...)) != NULL) { ... } 같은 식으로 대입 표현식을 괄호로 감싼다.[61] 다만 fgets는 입력의 처리가 gets와 조금 다르고, gets_s는 GCC와 LLVM/Clang에서는 지원하지 않으니 잘 알아보고 쓸 필요가 있다.[62] 비표준 함수로 windows에서만 사용 가능하다.[63] 다만 printf의 경우 별도로 포맷 스트링 문제가 존재한다.[64] 앨 스티븐스(Al Stevens)의 책에 나오는 말이다.[65] 또, 동일한 이유로 운영체제 시동을 뜻하는 단어가 bootstraping을 줄여 booting이 됐다. 운영체제가 하는 일이 하드웨어 위에서 소프트웨어를 로드하여 돌릴 수 있도록 하는 것인데, 운영체제 그 자신도 소프트웨어이기 때문에 누가 운영체제를 로드할 것인가에 대한 모순이 생기기 때문.[66] 음수를 표현하는 방법은 2의 보수 외에도 1의 보수, signed magnitude 같은 방법이 예전에는 있었다. 하지만 이를 실제로 사용하는 메이저 컴파일러는 사실상 없다.[67] 주의해야 할 것은, K&R C와 착각하면 안된다. 인터넷에서 검색하면 (주로 C언어 역사) K&R이라는 단어를 K&R C를 지칭하며 사용하는 경우도 가끔 있다. K&R C는 ANIS C 이전 버전의 '언어'이고, 현재 이 페이지에서 칭하는 K&R 은 C언어 교재이며, K&R 1st Edition은 표준화 이전 K&R C의 교재이고, 본문의 K&R 2nd edition은 표준화된 ANSI C(C89) 교재이다.[68] 3차 개정판 1000쪽, C++11로 업데이트 된 4차 개정판에서 1300쪽[69] 그나마 K&R만큼 완전히 짚어주는 교재는 후술할 K&K(C Programming: A Modern Approach)라는 교재가 뽑히는데, 이 때문에 K&R과 묶여서 C언어의 바이블로 취급할 정도로 이 두 책은 고평가받는다.[70] 그렇기에 더더욱 초심자에게는 어울리지 않는다.[71] 저자의 이름인 킹(K. N. King)의 앞 글자를 땄다.[72] 실제로 아주 중요한 내용이 구석탱이에 딱 한줄 써있는 경우가 많다.[73] 참고로 후술할 K&K라는 책은 2008년에 최종판이 출판됐는데도 이미 고전 취급받는데, K&R은 최종판이 1988년에 출판됐으므로 거의 40년 전 책이다. 그만큼 K&R이 오래된 책이며, 구닥다리 내용을 담고 있기 때문에 C언어의 최신 트렌드를 익히는데는 도움이 안된다는 소리다.[74] 위의 유명한 K&R이라는 책과 대구를 맞추려고 K&K라고 부르게 됐다는 소리도 있다. 다만, K&R에 맞먹을 정도로 좋은 평가를 받는다는 소리이기도 하다.[75] 최신 C언어 트렌드를 따라가는 프로그래머들도, 오래됐지만 K&R과 K&K는 읽어볼 가치가 있다고 평가하는 경우가 많다. 그만큼 두 책이 고평가 받는다는 소리다.[76] 게다가 중요한 내용은 꼬박꼬박 강조 표시를 해서 분량은 많아보이지만 중요 포인트는 잘 보이는 것도 장점이다. 특히 상술했듯이 C언어 특성상 피해야 할 행동이 매우 많은데, K&K에서는 이런 것들은 제발좀 피하라고 굵은 글씨와 느낌표와 박스까지 쳐가며 강조 표시를 해놨다. 반면, K&R은 중요한 문장을 평범하게 표시해 놓는 등 강조 부분에서 배려가 살짝 부족한 편이다.[77] 게다가 C99에서 잠깐 존재했다가 이후에 사라진 문법인 VLA같은 것을 유용한 문법이라고 K&K 본문에서 안내하기도 한다. VLA는 스택 오버플로를 낼 가능성이 있기 때문에 프로그래머들은 웬만하면 사용을 꺼리고, 심지어 최신 C 컴파일러에서는 VLA이 들어간 코드를 컴파일할 시 오류를 뿜으면서 컴파일이 안되는 경우까지 있다. 결국 K&K만 읽었다고 좋은 코드를 작성할 수는 없으며, 최신 C언어 트렌드를 따로 배워야 한다는 소리가 된다.[78] 상술했듯이, 애초에 2020년대에도 실무에서 C18 등의 최신 표준을 지키는 코드를 쓸 일이 거의 없다. 실무 코드베이스에서 C언어를 쓴다는 것 자체가 엄청나게 오래 된 컴파일러와도 호환돼야 할 요구 조건이 있을 확률이 높기 때문이다. 이런 면에서 볼 때 구식 컴파일러에서도 잘 작동되는 코드를 안내한다는 점에서 K&K은 나름 괜찮은 입문서라고 평가할 수 있을 것이다.[79] 이렇게 C언어 버전을 구분해서 안내하는 책이 별로 없다.[80] 책 특성상 뒷 단원과 앞 단원을 왔다갔다 하면서 모르는 개념을 참고하거나 복습할 부분이 많기 때문에 이 단점이 더욱 뼈아프다.[81] 위키독스 사이트 특성상 댓글로 번역 수정할 것을 건의하면 언젠가 수정이 반영되기는 한다.[82] 단 어려운 용어나 개념이 나올 때 한글 번역을 조금 참고하는 용도로는 상당히 좋다. 무엇보다 공짜이니..[83] 모던 C 책에서는 C11/C18를 설명하거나 새로운 C언어 트렌드를 안내하면서 새로운 개념과 새로운 용어가 많이 등장한다. 때문에 이미 C언어와 관련된 영어 용어들을 잘 아는 사람이 아닌 이상, 모르는 단어가 많이 나와서 독해하기가 쉽지 않다.[84] 다만 이 번역에 대한 비판이 좀 있다.표현 같은게 크게 어색한 것은 아닌데, 원서랑 비교하면 누락된 내용이 있다는 것. 직접 pdf와 책을 비교해보고 판단하는 것을 추천한다.[85] 한빛미디어의 "이것이 ~다" 시리즈에는 앞표지에 모두 건담 프라모델이 들어가 있다. 다만 책별로 모델은 다르다.[86] 대학에서 프로그래밍 입문 교재로 쓰는 경우도 있다.[87] 책을 구매하지 않으면 심사 문제만 접근할 수 있도록 사실상 유료화 됐다.[88] 여기서 ISO C17(2018)은 C18이다.[89] 사실은 애플이 GCC를 가져다가 사용하는 대신, 자체적으로 GCC에 추가한 기능 일부를 다시 GCC 측에 돌려주기로 약속했었다.[90] 게다가 LLVM은 이것을 처음으로 고안한 사람을 애플이 스카우트하면서 거의 애플 소유의 프로젝트가 됐다. 하지만 스티브 잡스의 애플 복귀 이후 애플이 진행하는 대부분의 소프트웨어 프로젝트가 오픈 소스인지라 이것도 역시 오픈 소스로 계속 진행중. 오라클 보고 있나? [91] 처리할 데이터의 양이 커지면 처리 시간이 지나치게 증가하는 알고리즘.[92] 이는 알고리즘의 문제이지 C언어를 모름으로써 발생하는 문제가 아니다. 굳이 C언어로 프로그래밍을 시작하지 않아도 이산수학이론 컴퓨터 과학을 제대로 배우고 익히면 충분하다는 소리다.[93] 최근 들어 많은 대학의 컴퓨터공학과에서 1학년 프로그래밍 입문 수업은 파이썬이나 자바 같은 비교적 쉬운 언어로 진행하고, 2학년 이후의 수업부터 C언어/C++로 넘어가는 편이다. 물론 1학년부터 바로 C언어/C++를 가르치는 학교도 있다...[94] B언어의 전신이 BCPL인 데서 따온 듯 하다.[95] 과거 새로운 표준이 200x년에 나올 줄 알고 C0x로 불렸으나, 2011년에 등장하여 C11이 됐다. C++ 역시 같은 이유로 C++0x로 불리다가 2011년에 등장하여 C++11이 됐다.[96] 이들은 다른 언어가 아니라 버전이 다르다.