C는 구식이고 투박하고 안전하지 못하다. C의 어떤 점은 사랑스럽지만 많은 이들이 미워하는 언어이다. 대중의 의견은 사실 너무 부정적이어서 누군가가 C로 뭔가를 프로그램을 작성하려고 한다는 걸 믿기 어렵다. 하지만 사람들은 C로 프로그램을 작성한다. 사실 C로 많은 걸 작성하고, 심지어는 새로운 것도 작성한다. 표준은 최근에 업데이트되었고 도구도 진화하고 있다. 많은 이들이 그 언어를 거들떠 보지 않지만, 사라질 위험은 없다.
그러면 왜 사람들은 여전히 C를 사용하는가? 이 언어는 다른 언어가 가지지 못한 어떤 특징을 가지고 있는 것일까? 더 새로운 언어들이 C의 함정을 피하려고 열중하고, 긍정적인 모든 품질을 완전히 무시한다. 이런 긍정적인 품질을 대체할 만한 게 많지 않았기 때문에 결국 C는 여태껏 살아남았다. C가 왜 아직 사용되고 있으며 어떤 특징이 시스템 프로그래밍에 쓰이는지에 대한 몇 가지 주된 이유를 살펴보자.
하드웨어 레벨에서 일하려고 시도하고 있는 중이라면 하드웨어를 실제로 드러낼 수 있는 언어가 필요하다. 프로그램과 그것이 실행되는 하부의 칩 사이에 복잡한 계층이 있으면 안 된다. 특정한 메모리 위치, 레지스터, CPU 명령으로 직접 접근할 필요가 종종 있다. 심지어는 매우 빡빡한 메모리 제어에 의해 많은 동시성 알고리즘이 요구하거나 가장 잘 작동하는 순수한 메모리 사용에도 마찬가지로 적용된다.
C가 기계 위에 제공하는 추상화는 제한되어 있고 프로그래머와 실제 기계 사이의 계층은 거의 없을 정도로 얇다. 심지어는 너무 그 계층이 너무 얇은 경우에는 C 컴파일러가 프로그램에 직접 인스트럭션 셋을 노출하는 인라인 어셈블리를 제공하려는 경향이 있다. 또한 이런 어셈블러는 고수준의 C 코드에 직접 접근하는 것을 허용하여 매우 편안한 통합을 제공한다.
어떤 복잡한 시스템을 프로그래밍하는 것에서 예외(exception)는 필수라고 믿는다. 동시에 장치 제어와 저수준 동시성과 같이 예외를 절대적으로 원하지 않는 상황도 있다. 종종 극도로 엄격한 리턴 처리를 해서, (예외를 통해서나 소멸자 또는 defer 문장과 같은 암시적인 함수 호출에 의해) 현재 스코프를 벗어나는 것에 대해서 걱정하지 않는 것이 이롭다.
C는 또한 어떤 VM이나 가비지 컬렉션 쓰레드를 가지지 않는다. 하부 OS의 보장은 직접적으로 코드에 제공된다. 세밀한 제어와 빠른 응답을 요구하는 어떤 것이든 (보장이 안 된다면) 높은 수준으로라도 예측성을 요구한다. 이것은 자원과 신호 관리를 더욱 어렵게 만들지만 가끔은 절대로 필수적이다.
내가 오버로딩과 생성자, 네임스페이스, 멤버 함수, 템플릿을 좋아하는 만큼이나, 때때로 그것들은 코드 조각에서 정확하게 무슨 일이 발생하고 있는지를 궁금하게 만든다. 일반적으로는 이렇게 상세함을 모호하게 만드는 것이 이익이 되지만 때때로 명백한 손실이 될 수도 있다. 하드웨어에 대한 접근과 보장된 흐름, 이 두 가지 다른 특징을 고려한다면 결국 코드가 뭘 하는지 정확히 알 필요가 있다는 사실에 도달할 것이다.
함수를 이름으로 호출한다면 항상 동일한 함수를 얻을 것이다. 파라미터의 타입은 여기에 영향을 미치지 않는다. 변수를 이름으로 참조한다면 지역 함수의 변수나 전역 변수를 얻을 것이다. 모호하게 멤버 변수나 여러 내장된 네임스페이스 이름들 중의 하나에 매치될 가능성이 없다. 연산자는 항상 같은 일을 한다. 자료 구조는 암시적인 초기화 또는 비-초기화를 하지 않는다. 기본적으로 보는 것을 얻게 된다. 때때로 이것은 지루하겠지만 어떤 함수가 호출되거나 무엇이 수정되었는지에 대한 의문이 거의 생기지 않는다.
많은 언어들이 일반적인 재사용 가능한 패턴에 집중하길 좋아한다. 반복적으로 같은 것을 타입 선언해야 하는 것 대신에 여러 번 적용될 수 있는 구조로 추상화할 수 있다. 이것은 생각과 알고리즘을 본질적인 형태로 캡슐화하게 해준다. 그것은 코드를 깔끔하고 이해할 수 있도록 유지해주는 멋진 계층화를 제공한다. 템플릿과 인터페이스, 동적 타이핑, 등등이 이 목표를 달성하고 있다.
하지만 종종 이런 추상화가 화가 날만큼 복잡하고 때로는 불가능할 뿐이다. 그냥 적당한 코드를 반복하는 매크로를 생성하는 게 종종 더 쉽고 훨씬 더 정확하다. 아마도 추상적인 구조의 과부하 또한 받아들일 수 없을 정도다.중복을 제거해주는 옵티마이저(optimizer)에 늘 의존할 수는 없다. 심화된 경우는 언어 자체의 선언적인 문법의 반복을 요구할 수도 있고, 아마도 자질구레한 것들에서만 그렇다. 전처리기가 그런 이슈들을 많이 해결해준다.
역주
optimizer
컴파일러가 인스트럭션을 만들어 낼 때 불필요하게 중복된 연산을 제거해서 최적화시켜주는 단계 또는 기능을 의미함
대상 플랫폼은 크게 다르다. 저수준에서 일할 때 플랫폼에 특정한 처리를 요구하는 상황을 빈번하게 만나게 될 것이다. 코드 블럭의 일부가 특정 플랫폼에서 아예 컴파일되지 않을 수 있어서 단순히 고수준의 “if”를 사용하는 것만으로는 불충분할 수 있다. 가끔 단일 플랫폼에서도 다른 특징으로 다른 빌드를 제공하고 싶을 수 있다.
이것은 매크로 전처리기의 확장으로 보일 수 있지만 매우 다른 것이며 높게 평가되는 C의 특징이라고 생각된다. 심지어 어떤 언어는 완전한 전처리기없이도 이런 기능을 제공한다. 친숙하게 전처리기의 두 가지 측면을 결합하도록 해주는 점이 C 언어의 또 다른 혜택이다.
C는 여전히 공유 라이브러리 API의 표준이다. 라이브러리가 여러 프로젝트에서, 아마 여러 언어로 사용될 필요가 있다면 C 헤더 파일이 제공될 것이다. C 함수 형태는 간단하고 이해하기 쉽다. 그건 교차-언어 환경에서 지원하기 쉽다. 호출 방식은 표준적이고 구현하기 쉽다. C API가 강제하는 경계는 매우 강력하며 그래서 공유 라이브러리에 적합하도록 만든다. 단지 기초적이고 POD 타입만 가능하므로 메모리가 어떻게 관리되는지에 관심이 없다. 처리할 예외가 없다. 극단적으로 제한된다고 하더라도 극단적으로 이식 가능하다.
역주
POD: Plain Old Data (Structure)
POD 타입은 캡슐화나 객체 지향적 특징을 사용하지 않고, 단순히 필드값을 저장하는 컬렉션으로 표현되는 자료 구조를 의미함
C가 명백하게 표준 라이브러리와 함께 하고 수천 가지의 추가적인 라이브러리들이 존재하지만 여전히 매우 작은 C 프로그램을 만들 수 있다. 무엇보다도 다른 언어들과 다르게 표준 라이브러리를 필요로 하지 않으면서 많은 시스템 프로그래밍을 실제로 할 수 있다. 다음으로 그것을 사용할 때조차 프로젝트에 표준 라이브러리 전체를 들여올 필요가 없다. C는 링커가 사용되지 않는 코드를 잘라낼 수 있는 컴파일과 링크 모델을 가지고 있다. 이것은 예외적으로 작은 바이너리들을 생산할 수 있다는 것을 의미한다. 또한 임베딩에 적합한 작은 바이너리 조각을 만들어낼 수도 있다.
사람들이 C에 끌리는 특징들의 목록의 마지막에 속도를 놓는다. 확실히 C는 극도로 효율적이고 번쩍일 정도로 빠른 프로그램을 생산하는 능력을 가지고 있지만, 이런 종류에서는 유일한 게 아니다. C++과 Ada, Fortran과 같은 언어들은 많은 알고리즘에 대해 동일하게 빠르거나 더 빠른 코드를 생산할 수 있다. 시스템들은 충분히 빨라졌고 옵티마이저들은 충분히 훌륭해져서 저수준 프로그래밍에서조차 미시 최적화를 할 필요가 없다.
여전히 성능은 상관없는 그런 것이 아니고 다른 언어로는 불가능한 성능 향상을 많은 응용들이 실질적으로 낼 수 있다. 확실하게 캐시를 망각하는(cache oblivious), 캐시를 인지하는(cache aware), 그리고 동시적인 알고리즘이 정확하거나 효율적으로 동작하려면 특정한 메모리 레이아웃이 실제로 필요하다. 그리고 소소한 성능 향상이라도 진짜로 도움이 되는 상황에서 C는 미시 최적화를 제공한다.
대안이 별로 없어서, C는 여전히 시스템 프로그래밍에 널리 사용된다. C++이 이런 특징을 보유하고 있지만 고수준 기능을 피하고 행동에 대한 보장을 제공하는 것이 때때로는 어렵다. 그럼에도 불구하고 C++은 보통 C가 제공하는 특징들이 필요하다면 좋은 선택이 된다. Ada도 크리티컬 시스템 프로그램에 사용되니까 여기서 언급될 필요가 있겠다. 표준은 아니지만 공통 Ada 컴파일러는 전처리기와 인라인 어셈블러를 제공한다. C와의 통합도 매우 간단하다. 그래서 아마 Ada도 적당한 조합이 된다.
아직 C를 좋아하지 않는다 하더라도(나도 좋아하지 않으니까) 확실히 다른 프로그래밍 언어에 이런 특징이 없다는 것을 한탄할 것이다. 아마 도메인에 한정된 이야기겠지만, 이상의 특징들 중 최소 2가지에서 혜택을 받을 수 없었던 프로젝트에서 일해본 게 떠오르지 않는다.