C(프로그래밍 언어)



<syntaxhighlight lang="cpp" line="1">

  1. include <stdio.h>

int main(void){ //표준 C 에서 main 함수의 리턴 타입은 반드시 int이다
printf("Hello world \n");return 0;
}
</syntaxhighlight>

{

TIOBE에서 선정한 프로그래밍 언어 월간 점유율 순위 (2017년 3월 기준)
자세한 내용은 이곳에서 확인할 수 있으며, 점유율을 정하는 기준은 다음과 같다.
1. Java2. C3. C++4. C\#5. Python
점유율 16.384%
작년 대비 4.14% 감소
점유율 7.742%
작년 대비 6.86% 감소
점유율 5.184%
작년 대비 1.54% 감소
점유율 4.409%
작년 대비 0.14% 증가
점유율 3.919%
작년 대비 0.34% 감소

1 개요

1972년에 벨 연구소(Bell Labs)의 데니스 리치(Dennis MacAlistair Ritchie, 1941.9.9~2011.10.12)가 만든 프로그래밍 언어. 대표적인 프로그래밍 언어로 특히 대한민국에서는 프로그래머의 기본이 되는 언어. 보통 C 언어라고 한다.

2 역사

케임브리지 대학교 에서 시작된 CPL 이라는 언어에서 BCPL(Basic CPL) 이 탄생하였고, BCPL 이 미국으로 물건너와 켄 톰슨이 'B' 라는 언어를 만든 뒤에, 같은 벨 연구소의 데니스 리치가 최종적으로 'C'라는 이름으로 언어를 만들었다. 참고로, CPL 의 'C' 는 케임브리지 대학의 C 를 뜻했다가 후에 임페리얼 칼리지 런던과 조인트 프로젝트가 된 이후에는 'Combined' 를 뜻하게 되는데, Stroustrup 의 회고에 의하면 저건 윗분들의 생각이고, 자신들에게 있어서 'C' 는 언제나 CPL 의 언어 디자이너였던 Christopher Strachey였다고 한다. BCPL 을 거쳐 'B' 가 된것은 '벨' 연구소의 B를 딴 것. 그리고 C 가 되면서 한때는 프로그래머 유머로 'C' 다음 언어가 과연 'D'일지 'P'일지 묻는 이야기가 있었지만 C++같은 것도 나왔고, Ansi C, C99, C0xC11[1] 등등등[2]으로 가지를 뻗으며 진화중인 현재의 C언어에게는 이미 과거의 이야기가 되어버렸다. 사실 D라는 프로그래밍 언어도 있기는 있다. 벨 연구소가 아닌 디지털 마르스(Digital Mars)에서 만든 것이기는 하지만. ABA Games 의 게임들 대부분도 D로 만든 것이다.

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

참고로 ++은 그 변수를 1증가시켜 대입하는 연산자이다. 다시말해 C++에서 ++은 C를 1 증가시켜 대입했다는 말. C\#에서 #은 ++++이다(++이 위 아래로 두개) 또한 C♯, 그러니깐 음악에서도 의미를 따왔다.

3 설명

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는 "믿을테니까 알아서 해라." 한마디로 끝낸다고 보면 된다.(...)

C 언어 이전에도 고수준 언어들은 많이 존재했지만, 대부분 특정 어플리케이션 영역을 타겟으로 하거나, 컴퓨터 과학 이론을 입증하기 위해 만들어진 실험실 언어들이었다. 어플리케이션 영역이 아닌 운영체제를 어셈블리어가 아닌 언어로 작성한다는것은 당시엔 일종의 금기에 가까웠고[4], C 언어와 유닉스는 소수의 예외를 제외하고 대부분을 C 언어로 작성하고서도 우려와는 달리 단점보다 장점이 훨씬 많다는것을 보이면서 이 금기에 정면으로 도전해서 승리하였다고 볼 수 있다.[5] 그리고 지금은 운영체제는 C 언어가 아니면 안된다는 새로운 금기가 생겼다.(...)

세계적으로 엄청나게 많이 쓰이는 프로그래밍 언어로 대한민국에서는 특히 비중이 높다. 어셈블리 코드를 코드 안에 집어넣어서 구동 속도 면에서 이점을 얻을 수도 있으며(그러나 매우 어렵고 호환성은 안드로메다로...) 기본적으로는 기존의 언어들 물론 어디까지나 C가 탄생하던 6-70년대 이야기다. 에 비해 매우 이해가 가기 쉬운 문법을 사용하여 초보자가 쉽게 접근할 수 있도록 제작되었다. 참고로 C가 개발된 시기는 70년대인데, C 개발 전에 주로 쓰이던 코볼 내지 포트란이 얼마나 불친절한 언어인지는 더 이상의 자세한 설명은 생략한다. 요즘 프로그래밍에 입문하는 사람들은 상상도 못하겠지만 코볼과 포트란은 소스 코딩할 때 칸까지 맞추어야 했다. 파이썬?제일 왼쪽 몇 칸은 주석, 그 다음 몇 칸은 정의... 이런 식으로.이 언어들은 천공카드가 쓰이던 시절 만들어져서 그렇다. 그리고 사실 코볼의 경우 원래는 프로그램 코드가 업무 서류로도 사용이 가능하도록(!) 설계가 돼있어서 그렇다. 주석이 코드고 코드가 주석일 경우의 아주 나쁜 사례. 2000년대에 애자일 프로그래밍 정신중 하나로 부활했다 카더라.[6]

다른 언어들의 경우 여러 교재들이 서로 경쟁하는 추세이지만, 유독 C 의 경우는 창시자인 데니스 리치와 브라이언 커니건이 쓴 The C Programming language 2nd Edition 이 독보적인 위치를 점하고 있다. 이 두사람의 이니셜을 따서 보통 K&R 이라 칭하거나,[7] 제목을 축약해서 TCPL이라고 부른다. 이 책이 나온지 매우 오래 되긴 했지만, C 라는 언어가 별로 변화가 없는 언어인데다가 C99, C11 등 ISO에서 계속 업데이트를 했음에도 불구하고 캐무시하는 프로그래머들의 성향[8] 때문에 ANSI C 가 현재 사실상 표준인 상태이다.[9] 또한, 분량은 전부 다합쳐도 300페이지도 안된다. 그것도 Appendix 부분을 빼면 순수 튜토리얼은 200페이지도 안되며, 언어 자체뿐 아니라 프로그래밍에 대한 여러가지 깨알같은 조언까지 다 포함이 되어있다. 보통, 일반적인 프로그래밍 언어 교재가 작아도 500 페이지를 가볍게 넘어가고, C++의 창시자인 Bjarne Stroustrup 이 K&R 과 비슷한 네이밍으로 쓴 The C++ Programming language 의 경우 1000페이지에 육박하는것[10]에 비하면 엄청나게 짧은 분량이라 할 수 있다. 그러나, 분량이 많은 교재들은 그만큼 친절하고 자세한 설명을 동반하여 비교적 술술 읽히는것에 반해, K&R 은 짧은만큼 매 문장 하나하나[11]의 중요도가 높기 때문에 프로그래밍 초심자에게 추천할만한 책은 아니라는 주장도 있다.그런데, The C++ Programming Language는 난이도도 TCPL 보다 더 높다. 사실, K&R이 바이블 취급을 받는 이유는 단순히 역사적이고 C 언어 창시자가 쓴 책이기 때문만은 아니다. C 언어가 겉으로는 심플해보이지만 워낙 숨은 함정이 많은 언어라, 저런 부분들을 제대로 다 짚고 넘어가야 하는데, K&R 이외에 제대로 완전하게 짚어주는 교재가 거의 없다. 보통의 고수준 언어들처럼 접근해서 직관적으로 '이렇게 하면 된다.' 식으로 설명을 하는 교재들이 많은데, 사실 온갖 정의되지 않은 행동(Undefined Behaviour)과 implementation-specific details, 하드웨어에 의존적인(hardware dependent) 함정들이 곳곳에 숨어있는 C 언어는 '이렇게 하면 된다.' 보다 '이렇게 하면 안된다.'를 중점적으로 설명해야 하는 언어이다. 특히나 UI 나 퍼포먼스 중심으로 돌아가는 어플리케이션 영역이 메인인 언어들과는 달리, OS 와 딱 붙어서 보안이나 안정성 및 호환성이 중시되는 시스템 프로그래밍 영역이 메인인 언어라 어느정도 깐깐하게 접근하는게 맞는 언어이다.[12] C 언어를 C++이나 Objective-C를 익히기 전의 워밍업정도로 취급하는 사람들에게는 저런식으로 설렁설렁 쉽게 써진 C 교재들도 좋은 평가를 듣는 경우도 많지만, 본격적으로 C 프로그래밍을 하려는 사람들 사이에서는 해외 기준으로 K&R과 C Programming A Modern Approach 두개정도만 제대로 된 교재 취급을 받는다. 국내교재나 대학의 경우, 닥치고 윈도우 기준으로 설명하는 경우가 많은데, 비주얼 스튜디오 컴파일러는 ANSI C를 기반으로 상당히 많은 비표준 확장을 제공하고 있다. gcc 역시 비표준 확장을 많이 제공하지만, 이후의 표준인 C99, C11 에서 그런 비표준 확장 상당부분이 표준화되었고 gcc 는 C99도 지원하므로 상황이 나은편이다. 웬만하면 유닉스에서 표준으로 먼저 배우고 MS 나 gcc 등의 비표준 기능은 차후에 문서보고 따로 익히거나, 꼭 MSVC로 배우고싶다면 표준과 비표준, 나아가서 가능하면 C89/C99 의 기능을 구분해서 설명하는 교재로 배우는게 나중을 위해서 훨씬 나은 선택이다.근데 어차피 C 는 MS 월드에서 찬밥이다. C 언어는 유닉스를 만들기 위한 언어로 시작했기 때문에 유닉스 표준인 POSIX 와도 관련이 깊고, 결국 제대로 표준으로 공부하려면 어찌됐건 유닉스 환경이 훨씬 편리하다.

대한민국에선 많은 곳이 C 혹은 C++로 공부를 시작하며 나머지는 Java, C#, 비주얼 베이직, 어도비 플래시(액션스크립트) 등이 차지하고 있다. 즉 독점에 가까운 위치를 점유하고 있다. 이는 각 대학교 혹은 학원들의 커리큘럼 탓이 가장 크다고 볼 수 있는데, 이 때문에 아직도 '자바 먼저' vs 'C 먼저'의 떡밥은 개발자들 사이에서의 좋은 키배거리가 되고 있다. 요즘은 Python vs C언어 키배로 넘어가는 중 그러나 후술할 듯 C가 미친 영향은 자바를 포함해서 매우 광범위한지라 어떻게 해도 결국 C가 1라운드 보스 맨 앞에 선다.(……) 사실,대한민국컴퓨터과학과 학부과정에서는 이게 프로그래밍 언어의 기초쯤 되는 취급을 받고 있다. 하지만 C언어 자체의 난이도는 위에서 봤듯이 무시할게 못된다. C를 먼저 권유하는 입장은 간단하다. 컴퓨터 아키텍쳐 및 시스템 프로그래밍, OS를 배우기 위해선 C언어 (+ 어셈블리 조금) 만한게 없기 때문이다. Java등의 고수준 언어를 통해서는 추상화 레이어에 가려져 여러 OS, 아키텍쳐 개념들에 대한 low-level한 직관을 얻기 힘들다.

C언어 자체는 지원되는 기능이 적기에 배우기가 크게 어렵지는 않다. 다른 프로그래밍 언어를 배운 경험자라면 대강의 기본적인 문법 정도는 늦어도 며칠, 빠르면 심지어 수시간 내에 습득이 가능할정도. 특히나, OOP 등 생각과 경험을 요구하는 고수준 기능들이 명시적으로 제공되지 않기에 단순 문법정도가 사실 언어가 제공하는 전부다. C언어 커리큘럼의 절반을 함수 사용법에 의존할 정도.(……) 게다가 그 함수도 150 여개밖에 안된다. 다른 고생산성 언어들, 예를들어 PHP 만 봐도 함수가 수천개가 넘어간다.

하지만 기능이 적다고 결코 쉬운 건 아니다. 초반의 포인터 장벽만 넘는다면 문법 자체를 마스터하고 간단한 커맨드라인 프로그래밍을 할 수 있는 수준까지는 엄청 쉽지만, 프로그래밍이란 게 기능이 적다고 그 적은 기능만 쓰는건 절대로 아니다. 없으면 결국 직접 구현해서 쓰게 되는데, 거의 30년간 프로그래밍 언어계에서 오늘날 영어와 같은 위치를 차지하고 있던 덕분에 그런 기능의 구현이나 최적화에 관한 많은 트릭들이 존재하고 이것을 얼마나 많이 알고있는가가 사실 C 언어의 핵심이다. K&R이 좋은 교재인건 맞지만 오늘날 K&R을 교재로 추천하기가 마냥 좋지만은 않은 이유중 하나도 너무 오래되어서 초창기 C 언어의 유행이나 트릭정도만을 담고있고, 그 이후의 여러가지 트릭이나 패러다임의 변화등을 반영하지 못하고 있기 때문이다.[13] 또한, 오늘날 고수준 언어들이 다수의 프로그래머가 함께 개발하는것을 염두에 두고 팀에 누가 될만한 위험하거나 생산성에 저해되는 부분들을 언어차원에서 강제로 제외시키는 경향이 있다면, C 언어는 저것들에 대해 완전히 개방된 언어에 속한다.[14] 상황이 이렇다보니 같은 프로그램이라도 프로그래머의 지식수준과 능력에 따라 퀄리티 차이가 그야말로 극과 극으로 벌어지는 언어이다. C언어 활용의 예술을 보려면 리눅스 등 C언어로 작성된 대규모 오픈소스 프로젝트를 보면 된다. 방대한 양의 코드를 함수, 구조체, 포인터, 매크로만을 이용해서 철저히 모듈 단위로 잘 관리하는 모습을 볼 수 있다.

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

C는 작지만 어셈블리어 다음으로 기계레벨까지 접근 가능하고, 충분히 강력한 언어이기 때문에 C의 힘을 제대로 발휘하려면 어셈블리어 수준으로 프로그래밍해야 하고 하드웨어의 기능을 꿰어차고 있어야 한다. 오죽하면 C가 '시스템 독립적인 어셈블리'(System Independent Assembly)라 불릴 정도. 애초에 C 자체가 운영체제를 만들기 위해 고안된 언어이니만큼 그럴 수밖에 없었다.

일반적인 프로그래밍도 역시 가능하지만, 그런 용도로는 더 적합한 언어들이 널려있다. 현재 대한민국에서는 양산형 Java 유저들(...)이 C언어 우습게 봤다가 데꿀멍하는 사태가 자주 있다.

즉, 초심자가 C언어를 배우는것까지는 물론 문제가 없지만 배우고 나서 뭔가 제대로 할만한게 거의 없다고 보면 된다. 그리고, 바로 그렇기때문에 프로그래밍 입문용 언어의 자리도 해외기준으로는 자바나 파이썬등으로 거의 대체되었다.

그래도 정 배우고 싶은 사람은 이곳을 참조해보자. C 언어 코딩 도장 JoinC 이것을 쉽게 배울수 있는 보드게임도 있다.

여담으로 유우키 아스나를 까면 이 언어로 컴퓨터가 털린다는 괴담이 있다.

3.1 단점

닥치고 성능이라는 대명제에 충실해서 작게는 변수 초기화, 배열 범위 점검, 허상포인터(Null Pointer) 문제에서부터 크게는 쓰레기 수집(Garbage Collection; GC), 예외처리 같은 경우까지 조금이라도 하드웨어에 오버헤드가 걸릴것같은 기능은 다 무시한(그래서 프로그래머가 그런거까지 다 신경써야되는), 저급프로그래머 입장에서는 악몽같은 언어다. 이 모양인 이유는 사실 C가 처음 나왔을 때는 그런 기능들에 신경쓸 여유도 없는 데다가[15] 그 당시에는 프로그래머라면 모름지기 저걸 알아서 관리해가며 쓰는게 기본이었기 때문. 언어에 고수준 개념들을 구현하기에는 하드웨어의 성능이 절대적으로 떨어졌기 때문에 그럴 수 밖에 없었다. [16]

덕분에 어셈블리어에 비해 호환성이 좋다고는 하지만, 하드웨어마다 달라지는 부분들을 언어 내에서 컨트롤해서 일관성을 유지하는 게 아니라, 성능을 위해서 전혀 후처리를 하지 않고 그대로 프로그램에 반영해버리기 때문에 CM롬 올리는 기분이랄까 사실 C언어의 호환성도 미신이라는 사람들이 많다. 그나마, C99에서는 컴퓨터 성능이 좀 올라간걸 반영해서 여러가지 엣지케이스에 대해 어느정도 통일성을 만들려고 한 노력이 엿보이긴 한다.

C++에서는 전보다 조금 나아지긴 했지만 포인터를 그대로 유지하면서 여러가지 상위개념을 얹었기때문에 말 그대로 조금이다. 특히나 저 포인터에 더해서 여러가지 상위개념들의 컨셉까지 익혀야 하고, 그것들과 포인터의 화려한 향연까지 존재하기때문에 습득하기도 엄청나게 어려운 언어가 되었다. 저수준부터 고수준까지 다되긴 하지만, 일단 발을 들여놓으면 그야말로 평생 공부해야 하는 평생언어가 되었다. C++에 들어서서 STL이 개발되어 그보다 조금 고급 개념을 제공하기는 한다. 그래도 위에 나온 것들 신경 덜 쓰고 싶다면 현재 쓰는 개념을 꽤 많이 탑재한 Java가 낫다.

추가로 아래와 같은 점들 때문에 종종 C가 까이곤 한다.

  • 느슨한 타입 검사. 초창기의 K&R C에 비해 ANSI C부터 타입검사가 비교적 양호해졌지만 아직도 서로 다른 종류의 포인터끼리의 대입조차 가능하다. 사실 느슨한 타입 검사 자체가 나쁜 건 아니고 최신 언어들은 느슨한 타입 검사를 채용하는 언어가 많은데, 문제는 C는 강타입 언어라 느슨한 타입 검사를 통과한 타입 미스매치가 실행시에 치명적인 문제를 일으킬 수 있다는 거. (그니깐 컴파일 시 warning을 무시하지 마라. warning을 잡아주면 당신의 명줄이 더욱 길어진다.)
  • 배열 안에 접근할 때, 인덱스가 배열 범위를 벗어나도 이를 체크하지 않는다. 이는 C언어에서 배열을 경계가 정해진 추상적인 자료구조로 취급하는 게 아닌, 연속적인 메모리 뭉치의 첫부분을 가리키는 포인터로 다루기 때문에 일어난다. 때문에 이런 구조체 핵같은 변태같은 짓도 가능한데, 예로 구조체 struct _EE_PACKET 에서 앞부분의 데이터는 int cnt; int checksum; int magic; 3개의 int형 변수를 가지며 당연히 이들의 자료형 크기는 불변이다. 하지만 이 구조체 바로 뒷부분에 다수의 struct _EE_EE의 구조체 데이터가 붙어 오게 되며 몇 개가 있는지는 _EE_PACKET 구조체 내부의 cnt에 의해 정해진다. (즉, 가변이다.) 이러한 경우 뒤의 _EE_EE 구조체 데이터에 접근하기 위해서는 struct _EE_EE *EE = (struct _EE_EE *)(((struct _EE_PACKET *)packet_ptr) + 1); 와 같이 할 수도 있지만, _EE_PACKET의 제일 뒷부분에 struct _EE_EE EE[0]; 을 넣어 주고 struct _EE_EE *EE = &packet_ptr->EE[0]; 처럼 할 수도 있다. zero-sized array이나 구조체 내의 변수선언부분의 제일 뒤라면 선언해도 문제가 되지는 않는다. 이는 사실 struct hack 이라는 유명한 트릭으로, 웬만한(제대로 된) C 교재에서도 대부분 다루고 있다. 참고로, ANSI C 에서는 zero-sized array 를 보장하지 않았기때문에 과거에는 주로 더미값 [1] 을 사용했는데, 이 트릭이 워낙 자주 쓰이다보니 gcc 등 C 컴파일러들은 저 트릭을 위해 자체적으로 zero-sized array 를 따로 허용을 하였고, 결국 C99 에 와서 flexible array member 라는 이름으로 표준화가 되었다.[17] 이렇게 배열 범위를 체크하지 않는다는 점은 심각한 보안 구멍을 만들어낸다. 일명 buffer overflow 취약점이라고도 하는데 배열 체크를 하지 않는 C언어 때문에 생기는 보안 상의 만악의 근원. 물론 프로그래머가 꼼꼼히 체크하면 되긴 하는데 여러분도 알다시피 프로그래머는 항상 실수한다(..) 따라서 언어 자체에 내재된 취약점이라 볼 수 있다. [18][19]
  • 문자 처리 과정에서 실수가 있으면, 치명적 문제가 생길 수 있다. C에서는 문자열의 끝을 표시하기 위해 모든 비트가 0인 널(null) 문자를 맨 마지막에 달아 사용하는데, 문자열을 처리하는 과정에서 이 널 문자를 잘못 달아주는 실수를 프로그래머가 저지르기 쉽다. 이런 경우, 메모리의 경계를 넘어 다른 영역에 접근하는 치명적인 문제가 생긴다. 문자열을 출력했는데 뒤에 왠지모를 한자가 딸려나온다거나.. 긄꿻얋儆儆儆儆儆儆儆儆 라인피드와 캐리지리턴까지 섞이면 이때는 대략 정신이 멍해진다 참고로 파스칼과 같은 언어는 문자열의 길이를 따로 저장하여 처리한다.
  • 문자열 타입이 없어서 문자형 배열을 대신 사용한다. 그 덕분에 문자열 처리가 까다로우며[20], 초보자들이 잘못 이해하거나 혼란을 느끼기가 쉽다. 주로 문자열 상수[21]에 대해 잘못 이해하는 경우가 많은데, 문자열 상수 자체는 '문자열'이나 '값' 자체가 아닌 '배열'이며[22] , 결과적으로는 해당 배열의 첫번째 글자가 저장된 위치를 가리키는 '주소값'이 된다. [23][24] 초보들은 char * s를 문자에 대한 포인터가 아닌 문자열에 대한 포인터로 잘못 이해하여 s가 가리키고 있는 '문자열'을 가져오기 위해 s에 참조 연산자 *를 붙이는 경우가 있는데, 이렇게 하면 의도한 대로 원래의 문자열 전체를 가져올 수 없다. 다른 타입들에 대해서는 예를 들어 int i, *j = &i; *j = 3; 식으로 해당 포인터가 가리키는곳에 있는 '값'을 의미할때는 항상 * operator 를 붙여줘야 하지만, 문자열일 경우에는 참조 연산자 *를 쓰지 않고 char *s; s = "Hello"; 처럼 써야 한다. *s 는 s[0] 과 마찬가지로 문자 'H'의 값 그 자체를 의미하기 때문이다.
  • 대입 (=) 연산자가 값을 반환한다. 가장 흔한 실수로 == 를 쓸곳에 = 를 하나만 찍는 오타를 범했을경우, integer 값이 리턴되는 경우에는 역시 문제없이 컴파일되고 직접 돌려서 해당 기능이 오작동을 하기 전까지 버그를 알아챌수조차 없게된다. 이때문에 == 사용시 lvalue 에 일부러 상수항을 사용하는 프로그래머들도 있다. 예를 들어, int a = 42; if(a == 37) { ... } 같은경우, 실수로 if(a = 37) 이라 쓰면 false가 아닌 true가 뜨며(a = 37 이라는 expression의 값은 37 이고 0 이 아닌 값은 죄다 true로 간주된다.), a에 37 이 대입돼버려 완전히 오작동을 하게된다. 문제는, 컴파일이 문제없이 되기때문에 버그찾기가 더더욱 힘들어진다. 하지만, 상수항을 lvalue에 사용해서 if(37 == a) 로 써주면, 실수로 37 = a라 썼을때 상수항에 대입연산자가 사용되었기때문에 컴파일시 에러가 떠서 쉽게 알아챌 수 있다. 요새는 컴파일러가 좋아져서 이런 위험코드는 대체로 경고처리해준다. [25][26]
  • 배열과 포인터의 차이가 비직관적이다. 사실 이 부분이 C 언어를 배우는데 가장 커다란 난관이 되는 부분이다. 배열과 포인터는 비슷하다고 보기에는 차이가 너무 크고, 다르다고 보기에는 비슷한 부분이 너무 많다고 생각하는 경우가 많다. 덕분에 교재마다 접근방법이 달라지는데, 어떤 교재에서는 '비슷하다'고 전제한 뒤 차이점을 설명하는식이고, 어떤 교재에서는 '다르다'고 전제한 뒤 공통점을 설명하는식이다. 어느쪽으로 접근하더라도 함정과 예외가 상당히 많아지기 때문에, 둘의 차이를 충분히 구분할만한 상황적 경험이 적을수밖에 없는 초심자들에게 결국은 이해보다 암기로 흘러가고 어렵다고 느끼는 경우가 많다. 배열타입과 포인터 타입의 차이를 제대로 느끼지 못하는 상황은 배열타입을 식에서 이용할 때 처음 원소를 가리키는 포인터로써 사용된다는 규칙을 제대로 숙지하지 못한 상황에서 배열과 포인터를 사용할 때 주로 발생한다. 즉, 식에서 배열타입을 이용할 때, 실제로는 배열타입이 아닌 포인터타입으로써 이용이 되는데 프로그래머가 이를 배열타입을 이용하는 것으로 착각하면 "읭 포인터 타입하고 많이 비슷하네?"라는 반응을 보이게 되는 것이다. 이렇게 겉으로 보기에는 배열타입이지만 실제로는 포인터 타입으로써 사용 될 수 있는 비 직관적인 요소가 존재한다.
  • 비직관적이고 암묵적인 규칙이 많다. 대체로 성능을 위해 생각하기를 포기한 부분이거나, 예전부터 이어진 전통인 경우(가변 인자 함수가 이에 해당)가 많다. 예를 들어 함수 매개변수에 배열을 선언하면 배열이 아닌 포인터로 선언된다. 이는, 함수의 매개변수로 선언된 배열(실제로는 포인터 변수)에 대해서 sizeof를 사용하면 배열의 크기가 아니라 포인터의 크기를 반환한다는 것을 의미한다. 배열이 그 첫번째 원소를 가리키는 포인터 주소값으로 변환되는 규칙과 그 예외상황 3가지도 의외로 아는 사람이 드물다. 부작용(side effect)과 관련된 문제들도 마찬가지. 함수 매개변수의 평가 순서나, 이항연산자의 피연산자 간의 평가 순서, 두 시퀸스 포인트 사이에서 동일 객체의 두번 이상의 변경 금지같은 규칙들이 이에 해당한다. 가장 끝판왕은 형변환. 부호형과 무부호형을 섞어 쓴 수식의 형변환 규칙이나 오버플로우 문제는 경험많은 프로그래머들조차도 눈뜨고 당하는 경우가 많다. 심지어 아예 무부호형을 피하는 방법도 권장된다
  • 숫자 타입을 처리하는 규칙들이 비직관적이고 복잡하다. 덕분에 언어 자체의 syntax/semantics를 넘어서 이에 대한 여러가지 꼼수와 내부적으로 처리하는 방식, 머신따라 달라지는 부분까지 염두에 두어야 한다. 숫자타입과 관련된 문제들은 한두가지들이 아니므로 하위 항목으로 분류한다.
    • C언어에서는 int형과 double형만 존재하는 것처럼 느껴질 수 있는데, 실제로, 모든 리터럴은 뒤에 f 등의 타입을 붙여주지 않으면 그냥 int 아니면 double 이다. char건 true/false 값이건 short 건 그냥 다 int로 간주한다. 그리고, 타입변환이 필요할 때마다 정수 진급(integral promotion, C99에선 integer promotion)이라 해서, char, short 등의 타입은 일단 먼저 int로 바꾸고 나서 변환을 시작한다.
    • bool 타입은 존재하지 않으며[27], 0은 false이고, 나머지 다른 것들은 전부 true로 처리한다.[28] 때문에 임의의 char 값이 숫자인지 알아보는 isdigit 함수는 숫자가 아니면 0을 리턴하지만, 숫자일 경우 보통 1을 리턴하기니 하지만, 0이 아닌 그냥 어떤 수를 리턴하는 컴파일러도 있다. 매뉴얼페이지에는 보통 non-zero라고 되어있다. (...) 비슷하게 논리적 부정 연산을 의미하는 !의 경우는 0일 경우 1로, 0이 아닌 다른 모든 값은 0으로 바꾼다.
    • char 타입은 아예 int형 타입과 거의 자유자재로 호환이 되며[29], 문자 상수 'A' 같이 분명히 char 타입으로 보이는것들도 실상은 char 타입이 아니라, int 타입이다. (C++에서는 이와 달리 문자 상수는 const char형이다.) 문자열은 그냥 char 형 배열로 쓴다. 문자열 값의 끝에 붙는 널문자 '\0' 역시 int 값으로 그냥 0 이다.[30]
    • unsigned 타입이 섞인 계산에서는 음수가 양수가 되어버린다. 이 문제에 대해서는 다소 긴 설명이 필요하다. C 언어에서는 사칙연산과 비교연산자를 포함하여 많은 이항연산자들이 있는데, 이것들은 서로 다른 타입간에 이루어지는 계산을 처리하기 위해 usual arithmetic conversions 라는 공통규칙을 적용한다. 그런데 이 규칙에 의하면, 같은 랭크(숫자 타입의 우선순위. 간단하게는 size라 생각해도 무방하다)인 부호형 정수와 무부호형 정수 사이에는 무부호형이 우선권을 가지며, 따라서 이항연산자의 두 항이 모두 무부호형으로 처리된다. 이 간단한 규칙이 불러오는 참극인 즉슨, unsigned 타입이 수식 중에 단 하나라도 섞여 있으면 그 안에 포함된 모든 음수가 양수로 변해버린다(절대값이 아니라 mod 연산된 값으로 변한다. 경고나 에러 메시지 없이). 그 결과 일반적인 수학 수식과는 전혀 동떨어진 이상한 결과가 나온다. 이를 방지하기 위해서는 그보다 더 랭크가 높고 실제 표현 범위도 큰 유부호 정수형으로 강제 형변환을 해야 한다(혹은 unsigned형을 signed형으로 형변환한다).
  • 네임스페이스를 지원하지 않는다. 소스 수준에서 play_sound라는 함수를 어디엔가 정의를 해놓고 다른 소스 파일에서는 play_sound라는 이름만 같은 다른 함수를 만들고 같이 컴파일하면 에러가 난다. 더욱 골때리는건, 컴파일 단계는 무사히 진행되는데 링커 수준에서 에러가 난다는 것. 따라서, 외부 소스 파일로 노출시키고 싶지 않은 내부 함수들은 static 지시자를 앞에 붙여줘서 이름 유출을 방지하고 다른 소스에서 쓸만한 함수는 <모듈이름>_함수이름(e.g. NamuWiki_add_document)을 사용하여 이름 충돌을 최소화 하는게 일반적이다.
  • 이식성 문제가 굉장히 까다롭다. 많은 C 입문서에서 첫머리에 C의 장점으로 '이식성이 있다'고 적어놓고 있는데, 이건 사실 프로그래머가 제대로 알고 코드를 작성했을 때에만 누릴 수 있는 장점이다. 다양한 하드웨어와 컴파일러들을 고려해서, C의 많은 부분은 '각자 알아서 정해서 명시하거나(implementation-defined)', '잘 돌아가게끔 네멋대로 알아서 정하거나(unspecified)', '죽이던 살리던 책임질 필요 없어(undefined)'로 되어 있다(표준문서 부록에 따로 Portability issues가 정리되어 있다). 그래서 프로그래머가 이식성 문제들을 모두 이해하고 코드를 작성해야 이식성을 보장받을 수 있다. 그래서 실질적으로는 이식성이 보장 안된다 가장 기본적인 정수형을 예로 들자면, int의 크기는 하드웨어에 따라 제각각이다. 따라서 각각의 환경에 맞는 가장 최적의 크기로 int를 정할 수 있는 반면에, int를 고정된 크기라고 생각하는 순간 코드의 이식성은 날아가 버린다. 또다른 예를 들자면, 많은 사람들이 c = a + b 에서 a와 b의 평가 순서가 정해져 있지 않다는 사실에 '분개한다'. 왜 당연히 앞부터 먼저 계산해야 하는데 그런 기본적인 것부터 '정해져 있지 않느냐'며 화를 내기 쉽상이다(...). 그러나 그덕분에 상당히 큰 폭의 최적화가 가능하다는걸 생각하면, 이렇게 선택의 자유를 주는 것은 당연한 것이다. 특정 기종(주로 x86혹은 AMD64)에서만 프로그래밍을 하는 많은 프로그래머들은 이식성 문제가 자신과는 전혀 상관 없다고 생각하기 쉬운데, 16->32비트, 혹은 32->64비트 전환기에 많은 사람들이 실제로 고생했던 부분이며, 단순히 컴파일러의 버젼업 만으로도 이런 이식성 문제들은 갑자기 튀어나올 수 있다.

C 언어에는 저런 함정들이 매우 많이 도사리고 있으며, C 언어를 배운다는것은 사실 '어떻게 프로그램을 만들것인가' 를 배우는게 아니라, '어떻게 저런 함정을 피해갈것인가' 를 배우는거라는 사람도 있다.

뭐 다 C로 과제 몇번하면서 두뇌개조 당하고 나면 자연스럽게 되는 것들이다. 정말로. 위에 나온 대부분의 단점들은 한마디로 인간이 자연스럽게 쓰기 힘들다는 것인데, C의 컨셉이 그런 것이니 당연한 것이다. 인간인 프로그래머와의 친화가 우선이 아니라 컴퓨터와의 친화가 우선인 언어인 것이다. C와 컴퓨터 및 운영체제의 이해가 깊어지다 보면 왜 이렇게 쓰기 불편하게 만들어진게 사실 당연한 선택이었는지 이해가 간다.[31]

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

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

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

2015년을 기준으로 C언어는 다음과 같은 분야에서 주로 쓰인다.

  • 운영체제 및 디바이스 드라이버
  • 임베디드 프로그래밍
  • 매우 빠른 계산속도가 필요한 프로그램이나 라이브러리
  • 암호학 라이브러리 [33]
  • 프로그래밍 언어 인터프리터(CPython 등)
  • 웹 서버(Apache 등)
  • 데이터베이스(Postgresql)

보면 알겠지만 어플리케이션 레벨 프로그래머에게는 어느 하나 쉬운 분야가 없다(..)

3.1.1 과거

C가 나올 당시 시점에서 당시 널리 쓰이던 포트란이나 코볼, 베이직과 비교해 보자면, C는 언어 자체에는 아주 기초적인 기능 만을 탑재하여 언어에 기본 탑재되는 명령어를 최소한도로 줄였다는 특징이 있다.

C에서는 START나 END같은 명령어도 배제하고, 대부분을 { } 같은 기호로 표현했기 때문에 코드가 매우 깔쌈하고 용량도 줄었다. C에서는 포트란에는 아직 남아 있던 천공카드 시대의 흔적이 거의 사라졌다. 그러나 테이프 프린터 시절의 유물이 남아있다는게 함정

3.1.2 현재

노인 학대
C로 짜여진 코드는 매우 속도가 빠르고 바이너리 크기가 작지만 수정 사항을 확인하려면 컴파일이 필요하며 디버깅도 어려워 생산성이 비교적 낮다. [34] 이러한 특성 때문에 속도가 다른 무엇보다 (심지어는 생산성보다도) 중요한 임베디드 혹은 모바일 계열, 또는 시스템 프로그래밍 등에서 주로 사용된다. 그러나 모바일의 경우도 이미 하드웨어가 발전하여 제약이 많이 풀린 상태이며 생산성을 중요시하는 추세로 가고 있기 때문에 C가 설 입지는 점점 좁아지고 있다. 실제로 현재 시중에 판매되는 휴대 전화에 WIPI-C의 컴파일러 및 링커 설정을 고쳐서 C++을 사용하게 할 수 있는데 문제가 될 만한 속도 저하는 없었으며 여러 나라의 BREW시스템에서도 C++로 인해 속도 문제가 생긴 적은 없다. 더군다나 최근 인기있는 안드로이드 운영체제 같은 경우 기본적으로 Java를 사용해서 개발하도록 되어 있다.[35]

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

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

안정성보다는 퍼포먼스를 골수까지 뽑아내야 하는 게임 프로그래밍 분야 또한 C/C++가 대세. 게임 프로그래머들이 C에서 C++로 넘어가기를 끝까지 싫어했던 것은 오로지 C(지금은 C++)가 다른 언어보다 속도를 빠르게 최적화할 수 있기 때문이며, 다른 분야에 비해 보수적이라는 소리를 듣는 편. 요즘에는 코어부분만 C/C++ 로 만들고, 게임 로직의 상당한 부분을 다른 고급 스크립트 언어에 의존하는 추세. 사실 게임 분야 뿐만이 아니고 대규모 프로젝트에서 중추는 당연히 C/C++이다.

속도와 시스템 제어가 필요한 코어 부분은 다들 C/C++로 제작하고 UI나 콘트롤러같은 건 고급 언어를 쓰는 게 현상황. 옛시절 어셈블리어가 차지했던 자리를 현재는 C가 차지하고 있다고 봐도 된다.

현시점에서 C의 가장 큰 의의는 사실상 모든 아키텍쳐와 운영체제에서 지원하는 언어라는 것이다. 일반적으로 C++은 지원하지 않더라도 C는 지원하는게 보통이다. 워낙 널리 쓰이다보니 CPU 디자이너들이 가장 먼저 하는 일은 C언어를 instruction set으로 포팅하는 것일정도. 심지어 C언어 설계 자체가 CPU 인스트럭션 설계에 영향을 주는 단계에 이르렀다.
그런 관계로 이식성이 중요한 경우는 대개 C를 사용한다. 자바의 멀티플랫폼과는 성격이 다르다. 자바는 각 플랫폼용으로 만들어진 가상머신 위에서 같은 소스가 실행되는 것이고, C의 경우는 각각의 시스템에 맞는 기계어로 컴파일 되는 것이다. 위에도 언급했던 자바 가상머신 자체가 C/C++로 만들어지므로 당연히 자바보다 범위가 넓다. 기존 C 프로그래머들은 진정한 멀티 플랫폼 언어는 자바가 아니라 C 라고 믿는 사람도 부지기수. 표준만 철저하게 지킨 C 코드는 C 컴파일러가 있는 어떤 플랫폼에서도 컴파일 - 실행이 가능하다.[37] 그게 쉽지 않아서 문제지. 요즘 C를 사용하는 이유는 위에도 쓰여있듯이 저수준의 제어가 필요하기 때문에 사용하는데, 이는 플랫폼에서 제공하는 API를 사용하지 않고는 불가능하기 때문.

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

다만, 요즘 대학에서는 신입생들 프로그래밍 입문 강의로 C 언어를 기피하는 현상이 있는데, 대학은 '특정 프로그래밍 언어'를 배우거나 '프로그래머'를 양성하는 기관이 아니기 때문이다. 실제로 해외대학들은 C 언어를 입문강의로 사용하는곳이 거의 씨가 말랐다. 그리고, 그 이후에도 모두가 C 언어를 배우는것이 아닌, 하드웨어쪽으로 방향을 정한 학생들만 C 언어를 배우는 경우가 많다. 사실 오늘날 컴퓨터는 수학(논리학)과 전자공학의 중간즈음에 위치한 분야라 할 수 있는데, 해외에서는 학풍에 따라 프로그래밍 입문 강의를 순수수학/논리학과 가까운 Programming Language Theory 의 입문으로 생각해서 가르치거나 혹은 보다 실용적인 전반적인 소프트웨어 개발경험정도로 생각해서 가르치는 경우가 많다. 그리고, 전자의 경우에는 언어적으로 볼때 너무나 심플해서(그냥 하드웨어를 그대로 노출시킨듯한 디자인의) 아무런 매력이 없는 C 언어를 사용할 이유는 거의 없으며, 후자쪽도 소프트웨어 개발 전반을 최대한 많이 경험할 수 있게 해주는것이 목적인데, C 언어의 경우 한학기동안 배워서 커맨드라인 입출력만 끄적이는게 고작이다보니 이 역시 한학기 배우는 과정중에 어느정도의 OOP 개념도 맛볼 수 있는 자바나, 한학기만 배우면 학생들 스스로 어느정도 괜찮은 프로젝트를 진행할 수 있게 되는 파이썬에 비하면 크게 밀린다.

실제로, 대학에서 C 언어를 프로그래밍 입문으로 배울 경우, 그 강의는 프로그래밍 강의라기보다 순수한 C 언어 강좌가 돼버리는 경향이 있다. 예를들어, char p[] = "aaa"; *p = 'b'; 는 문제가 없는데 char *p = "aaa"; *p = 'b'; 는 런타임 에러가 난다. 문자열, 포인터, 배열 관련해서 프로그래밍 개념적으로는 거의 의미없는 그냥 C 언어만의 저런 함정들이 상당히 많은데다가, 코딩시 자주 접하게 되는 결코 무시할 수 없는 함정들이기때문에 그냥 넘길수도 없고 거기에 낭비되는 시간도 은근히 많다. 대학 프로그래밍 강의의 목적을 생각하면 사실 무엇이 더 적합한지는 자명하기때문에, Java, Scheme, Python, ML, Haskell(!) 등 해외대학의 경우는 프로그래밍 언어 입문이 거의다 고수준 언어로 변경되었다. 한국의 경우는 순수수학이나 논리학같은 분야가돈이 안되다보니 기반이 매우 약한편이며, 반면 공학은 기형적으로 발달해있기때문에 전자공학쪽의 입김이 센편이라 여전히 대학에서 C 언어로 입문을 시키는 경우가 많다. 심지어 대학교에서도 고급 컴퓨팅 하면 로우레벨쪽부터 상상하는 사람들이 대부분이고, 입문강의를 C 언어에서 다른걸로 바꾸는 이유가 C 언어가 어려워서 잘 못따라오니 쉽게 가르치기 위해서라고 착각하는 사람도 많은편.

4 점유율과 플랫폼별 지원상황

2016년 3월을 기준으로 자바와 함께 몇년째 1, 2위를 다투고 있으며[41], 그 이외의 언어랑은 넘사벽의 비율을 보여준다. 그야말로 부동의 원투펀치. 다른 언어들이 3위 경쟁을 하는동안 C와 자바가 넘사벽 양대산맥을 보여 준다. 특이할점은 점유율이 하락세를 타다가 최근에 오히려 올랐다는것인데, 아마도 애플 붐에 힘입어 초고속 성장세를 타고있는 Objective-C 의 영향을 좀 받은듯 하다.[42]

가장 널리 쓰이는 PC 플랫폼인 윈도우에서는 안타깝지만 반쯤은 버려진 언어이기도 하다. MS 에서는 C를 Internal language로 규정하여 내부적으로 윈도우와 기타 MS 상품들을 만드는데는 사용하지만, C 프로그래밍 환경을 사용자에게 정식으로 제공하지는 않는다. 덕분에 윈도우가 자랑하는 비쥬얼 스튜디오 에도 C 프로젝트 항목은 없다.(C++ 프로젝트를 선택하여 소스파일 확장자를 .c 로 바꿔주거나 C 로 컴파일한다고 프로젝트 옵션을 설정해야 한다.) 게다가, 그런식으로 사용을 하더라도 MS 의 C 지원은 순수하게 C++ 에 묻어가는 정도라, 새로운 ISO 스탠다드인 C99/C11 의 기능들도 거의 지원하지 않으며, 앞으로의 계획도 C99/C11을 완전히 지원할 예정은 전혀 없고, C99/C11의 기능중 C++98/C++11에도 포함되는 것만 지원할 예정이다.

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

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

5 다른 언어에 미친 영향

  • { ... }을 이용한 블럭 (ALGOL/PASCAL 스타일의 begin ... end 보다 간결하다)
  • 대입을 뜻하는 연산자를 =로, 동일함을 뜻하는 연산기호를 ==로 사용한다. 농담 좀 섞어서 초심자의 C언어 코딩 에러의 90%는 여기서 나온다.[45]
  • 다르다를 뜻하는 연산기호를 !=로 사용한다.
  • or/and를 ||와 &&로 사용한다.
  • +=, -=, *=, /=등의 직관적인 복합연산자를 지원한다.
  • ++ 와 -- 라는 단항연산자를 사용한다.
  • 그외에 if, for, while 등 많은 예약어의 사용 방식

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

추후 C++로 발전되었으며, C++에서는 OOP 기능을 지원한다. 다만, C언어로 OOP를 구현할 수 없는 것은 아닌데, 객체지향은 개념일 뿐이며 C로도 그 개념을 구현할 수 있다. 일례로, Win32 API가 C로 구성된 객체지향 프레임워크이다. 또한, 리눅스의 VFS(가상 파일 시스템)가 '객체지향적'으로 코딩되어 있다. 다만 언어 차원에서의 지원이 없기 때문에 군더더기가 늘어날 수 있다는 점은 감안하여야 한다.

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

"라틴어를 익히면 영어 프랑스어 모두 쉽게 익힐 수 있어."

라틴어>>>>영어+프랑스어인것은 그냥 넘어가자.
라틴어는 어셈블리어에 비유해야 되는 거 아닌가?

5.1 C++과의 관계

그 이름의 유사성 때문에 C++을 C의 확장판 정도로 생각하는 사람들이 많다. 역사적으로 일단 시작은 그러했으니, 어느 정도는 사실이다. 그러나 시간이 지나면서, 두 언어가 서로 공유하는 부분에서도 차이가 생기기 시작했다. 즉 이제는, C는 C++의 부분집합이 아니게 되었다. 이것이 의미하는 바는, C로 짠 코드를 아무 생각없이 편하게 긁어다 C++에 붙일 수 없게 되었다는 것이다.[47] 자세한 것은 다음 문서를 참고하라. ISO C와 ISO C++의 차이 생각보다 까다로운 문제점들이 많다. 현재의 C11과 C++11에서는 더더욱 많은 차이점이 있을 것이다.

C++17에서는 다시 C가 C++의 부분집합이 될 것으로 보이기 때문에([1]) 표준이 갱신되고 C가 C++의 superset이 되면 수정바람.

참고로, Objective-C의 경우에는 C++과 달리 C를 완전히 포함한다.

6 C 언어용 컴파일러/개발 도구들

GPL 라이센스를 따르는 오픈소스 개발 툴. 2006년 이후로는 소식이 없다. 설치할때 기본적으로 같이 깔리는 컴파일러는 윈도우용 gcc인 mingw.
orwell이 Bloodshed Dev-C++ 4.9.9.2 소스로 개발하고 있는 dev-c++이다.
지원 페이지만 남아있을 뿐이고 구입하는 곳은 여기로 링크되어 있다.[48]
오픈 소스 개발툴이며 라이센스는 GPL 3.0 버전을 따르고 있다. Dev-C++처럼 컴파일러는 mingw를 사용.
MS 는 C 를 Internal language 로 규정하여, Windows 나 기타 MS 소프트웨어 개발에 사용하기는 하지만, 엔드유저[49]에 지원하지는 않는다. 다만, 비쥬얼 스튜디오는 C++ 을 지원하며, 이 언어가 C와 상당부분 유사성이 있기때문에, 여기 묻어가는 형식으로 이용은 가능하다. 즉, 제대로 사용하려면 다른 C 컴파일러를 따로 깔거나 그냥 유닉스 플랫폼을 따로 인스톨하는 것이 낫다.
윈도우 전용의 프리웨어 개발 환경이다. MS 와는 다르게 C99 는 물론이고, 현존 거의 유일한 C11 완벽지원 컴파일러를 제공한다. 게다가 툴이 가볍고 무료다!
Borland C와 마찬가지로 공식 페이지만 남아있다. 역시 판매는 여기에서 담당하는 듯하다.
공식 페이지가 위키위키 형식으로 되어 있다. Open Watcom Public License 라는 라이센스를 따른다고 한다.
관련 문서: WIPI
대표적인 C, C++ 컴파일러중 하나로 널리 사용되고 있음에도 불구하고 무료로 사용 가능. gcc는 윈도우용이 아니지만 윈도우 버전인 mingw은 윈도우에서 사용 가능. 따로 배포하고 있다. gcc든 mingw든 통합환경개발툴이 아니라 단지 컴파일러일뿐이기때문에 비주얼 스튜디오나 이클립스, 코드블럭스와 달리 글자를 쓰는 창같은 응용프로그램은 전혀 없고 그냥 컴파일러(즉 번역기) 하나만 설치된다. 물론 이것만 설치되어 있어도 메모장등으로 코딩 한 후에 직접 컴파일, 링크를 해서 프로그램을 만들 수 있다. #
애플에서 직접 제작하는 IDE. 예전에는 위의 GCC를 그대로 가져다[51] 썼지만, 요즘은 LLVM이라는 신세대 컴파일러셋으로 이주하는 중이다[52]. C, C++, Objective-C 등의 언어를 컴파일할 수 있다. OS X용 애플리케이션이나 iOS용 앱을 제작하는데는 필수.
위에서 LLVM 을 언급했는데, LLVM은 백엔드이고, 실제 C 컴파일러는 프론트엔드인 Clang이 담당한다. 신생 컴파일러이지만, 애플이라는 든든한 빽을 동반하고 급성장중이다. 컴파일속도는 gcc 보다 빠르지만, 런타임 속도는 아직 좀 떨어진다. 신생 컴파일러인만큼 gcc에 비해 구조가 깔끔하여 차후 발전가능성이 높다고 한다. 곧 gcc 와 맞짱뜰 수준으로 발전할 가능성도 있다. gcc의 라이센스를 마음에 안들어하는 FreeBSD 버전 10 에서 기존 구버전 gcc 4.2를 밀어내고 디폴트 컴파일러 자리를 차지하였고, 애플의 OS X 매버릭스에서도 마지막까지 남아있던 llvm-gcc를 제거하며 완전히 자리를 차지하였다. 더불어 현재 C++ 최신표준의 지원도 가장 빠르다. MSVC가 C++11의 지원도 지지부진한 마당에 Clang은 이미 C++14까지 feature complete 인 상황.
A Retargetable C Compiler: Design and Implementation라는 책에 소스코드가 실려있는 C컴파일러. 이를 바탕으로 한 윈도용 C 컴파일러인 lcc-win32도 존재한다.
인텔에서 자기 회사에서 만든 CPU에 최적화된 코드를 만들어 낼 수 있도록 직접 제작한 컴파일러 수트. 사용 설명서 시작 부분에 '인텔에서 제작하지 않은 CPU에서 구동 시 최적화된 성능을 보잘할 수 없습니다'라고 적혀있다#. C/C++ 컴파일러와 Fortran 컴파일러를 제공한다.
윈도우에서 설치 시, Visual Studio에 에드온 형태로 설치가 돼서 Visual Studio내에서 기존 컴파일러 대신에 사용할 수 있다.
컴파일러 이외에도, 어셈블리어 단위로 한땀한땀 손으로 최적화한 수치해석 라이브러리#와 다수의 컴퓨터가 동시에 컴퓨팅을 할 때 사용되는 MPI 라이브러리, 코드의 성능을 분석해주는 도구#, 코드 최적화를 도와주는 도구#, 성능에 악영향을 주는 에러를 찾아주는 도구#, 등을 하나로 묶어서 Intel Parallel Studio#라는 이름으로 판매한다. 인텔 CPU와 인텔 가속기를 수 만개씩 사용하는 슈퍼컴퓨터 등에서는 매우 자주 쓰이는 유용한 도구.
Intel Parallel Studio는 학생 대상으로 무료로 사용 가능(윈도우는 Visual Studio 통합 포함)하고, 수치해석 라이브러리는 일반인 대상으로 무료 사용 가능하다.

7 여담

7.1 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로 짜

추종자들은 항상 다음과 같은 소리를 한다.

전산과 신입생은 CPU부터 시작해서 C를 활용하는 데까지 차곡차곡 기초를 닦아야 합니다. 저는 솔직히 너무나도 많은 컴퓨터 관련 교육과정들이 자바가 가장 좋은 초보자용 언어라고 선전하는 현실에 질려 버렸습니다. 흔히 자바는 쉽고, 따분한 문자열이나 malloc과 같은 골칫덩어리를 다루는 과정에서 혼란을 겪지 않으며, 아주 큰 프로그램을 모듈로 나눠서 만들 수 있는 근사한 객체지향 프로그래밍 기법을 배울 수 있다는 화려한 이유들이 따라 나옵니다. 하지만 여기에는 교육적인 재앙이 있습니다. 졸업생들은 하향 평준화돼 러시아 페인트공 알고리즘[53]을 여기저기에 만들어내며, 심지어 자신의 잘못을 인식조차 못할 겁니다. 펄 스크립트에서 이런 사실을 결코 볼 수 없을지라도, (물론 어렵지만) 기본적으로 문자열이 무엇인지 아주 깊은 단계에서 이해하지 못하기 때문입니다. 다른 이들이 뭔가를 잘하도록 가르치길 원한다면, 기초부터 시작해야 합니다. 이는 마치 태권소년(Karate Kid)과 비슷합니다. 마루바닥을 쓸고 닦고 쓸고 닦고, 이렇게 3주만 하면, 자연스럽게 목표물을 향해 발이 쭉쭉 뻗어나갑니다.

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

어이쿠 그 싫어하시는 SICP[54]스러운 발언을 하고 자빠졌네
근데 java나 c나 제대로 배우면 러시아 페인트공 알고리즘 같은 것은 안나오잖아.

7.2 포인터

C의 알파이자 오메가
C에서 지원하는 유도형[55]식의 자료형 중 하나.

C를 배우는 사람들은 포인터에서 한번쯤은 골머리를 앓아보았을것이다. 대부분 입문자들은 컴퓨터 구조에 대해 전혀 모르는 상황에서 C를 배우기 때문.[56] 하지만 포인터를 제대로 사용할 수 있게 되면, 그시점에서 컴퓨터 위주로 생각할 수 있게 된다고 볼 수 있다. 그렇기 때문에 사실상 컴퓨터 구조와 함께 배우는 셈이 되는데 기본지식이 없는 일반인의 머리로는 이게 제대로 될리가 없다. 포인터 덕분에 메모리어셈블리어 수준으로 정밀하게 제어할 수 있지만 그 반작용으로 에러의 90%는 궁극적으로 포인터 문제다.[57] 때문에 C#이나 자바 등은 포인터를 지원안한다.[58][59](결국 구조체와 배열 참조도 전부 포인터 참조로 이루어 진다. 다만 사용자에게 제공하지 않을 뿐이다.)

포인터는 정말 어렵다! 하는 인식은 과장되어 있는 측면이 크다. 개념자체는 주소를 저장하는 변수이지만, 입문자에게 포인터가 벽으로 느껴지는 것은 첫번째로 맞닥뜨리는 생소한 개념이기 때문이다. 그 전까지 간단한 논리 체계를 배우는 수준이었다면 포인터는 본격적으로 컴퓨터의 구조를 공부하게 되는 시점. 게다가 문제는 그 개념이 아니라 응용에 있어 허들이 높다. 제대로 관리하지 않으면 메모리 누수가 일어나고 잘못된 주소를 가리키거나 하는 등 본격적인 디버깅 지옥문그대신 등가교환으로 디버깅 스킬업. 게다가 여러가지 비정상적인 방식으로 포인터를 사용하는 스킬들도 많기때문에 사실상 끝이 없다. 사실 프로그래밍에 있어서는 필수적인 개념이다[60]. 다른 객체지향언어를 놔두고 C를 익히는 이유 중 하나는 궁극적으로 포인터를 사용하여 쉽게 개발을 하겠다는 의도이다.[61] 하지만 일반인 사이에서는 이러한 C의 장단점을 무시한채 그냥 프로그래밍 입문으로 적절하다는 식으로 주입하고 있다무슨지거리야.

포인터의 악명은 DOS 시절로 거슬러 올라간다. DOS 시절은 16비트 CPU[62]의 한계로 포인터도 near 포인터(2바이트), far 포인터(4바이트)로 나뉘어져 있었는데 이 시절에는 저 포인터들을 다 컨트롤하기가 쉽지 않았다(주소 계산 방식부터 다르다). tiny, small, medium, compact, large 등등의 메모리 모델이 있었고 각각의 모델마다 사용되는 포인터가 달랐다. data segment 가 한개뿐인 모델에는 near 포인터를 사용하였고[63], 그 외의 보다 큰 메모리 모델에는 far pointer 가 사용되었는데 당연히 near pointer 가 오버헤드가 적기때문에 가급적이면 작은 메모리 모델을 골라서 사용하였다. 게다가 포인터가 깨지기라도 하면 아예 컴퓨터가 맛가는 경우도 많았기에 디버깅 지옥이었다. 본격적으로 32비트 시대가 열리고 난 이후[64]에 near, far 개념이 사라지고 메모리 관리를 OS차원에서 어느정도 관리해주면서 포인터 지옥에서 상당히 해방되었음에도 불구하고 과거의 포인터의 악명이 그대로 이어져 오고 있는 것이다. [65] 물론 드라이버와 같은 Low-Level 프로그램을 짠다면 포인터 한번 잘못 사용하는것으로 블루스크린 을 심심찮게 맛볼 수 있다. 블루스크린이라도 나오면 이는 매우 양호한 경우이고, 조금 안 좋으면 시스템 정지, 최악의 경우는 어떠한 반응도 없이 바로 재부팅이 되는 경우이다. 사실 시스템 정지가 일어나는 경우는 동기화 객체를 잘못 다루었을 때에 발생하는 경우가 많다만

C에 입문하는 대학생들에게 필요이상의 심리적 부담을 안겨주어 시작하기도 전에 지레 겁부터 먹게 만드는 악효과를 가져왔다. 여기에 교수들의 실력 부족과 잘못된 커리큘럼은 포인터를 더욱 큰 벽으로 느끼게 만들고 있다. 이 문제는 십여년이 흘렀어도 개선되지 않고 결국 난이도가 좀 더 낮은 언어인 자바로 프로그래밍 교육 커리큘럼이 이동하는 추세다.[66] 자바를 새로 배우기 귀찮아하는 교수들은 이마저도 못하지. 아 대학원생을 족치면 되겠구나! 자바를 비롯한 현대 언어들은 포인터를 아예 없애 메모리에 직접적인 접근을 막고 그 자리에 참조자를 넣어서 자동으로 관리를 한다.[67] 자세한 건 자바 항목 참조.

어렵다는 단점이 있지만 포인터는 강력한 도구임을 알수 있다. 시스템 내부에서도 굉장히 많이 사용되며 크고 긴 문자열 값을 일일이 넘겨줄 필요 없이 포인터 하나만으로 대체 할수 있으며 알고리즘, 자료구조에도 많이 사용된다. 배열이나 구조체 참조에도 포인터를 사용하게 된다.

만약, 아무리 생각해도 당신이 포인터에 대한 이해가 어렵다고 생각된다면 다음과 같이 생각해보도록 하자. 우리가 컴퓨터를 켜면 바탕화면이 나오고, 거기에는 수많은 바로가기 아이콘이 존재하며 바로가기 아이콘은 반드시 어떠한 원본 파일을 가리키고 있으며 바로가기 아이콘이 가지는 데이터는 그 원본파일의 실제 절대경로이다. 포인터도 마찬가지로, 포인터변수(바로가기 아이콘)는 어떠한 원본 데이터(원본 파일)를 가리키고 있으며 포인터변수가 가지고있는 데이터는 원본 데이터의 위치를 가리키는 메모리 어드레스(원본파일의 절대경로)이다.[68]

그래도 포인터가 너무나도 블랙홀처럼 느껴진다면 그냥 어셈블리어를 아주 기본적인 부분만 어느정도 공부해보는것이 낫다. C 언어에 비해 훨씬 직접적으로 메모리를 다루고 포인터 개념도 그와중에 많이 사용하기때문에, 대충 사용법만 짚고 넘어가는 경우가 많은 C 언어 교재들에 비해 설명도 하드웨어와 더불어 훨씬 자세한 경우가 많고, 어셈블리어와 어느정도 씨름하다보면 C 언어의 포인터는 그냥 저절로 이해가 된다. 어셈블리어까지 보는게 거부감이 들 수도 있겠지만, 고급기능들은 배제하고 기본적인 부분으로 한정하면 사실 아주 단순하고 시간도 얼마 걸리지도 않는다. 어셈블리어를 보다보면 C 언어가 얼마나 하드웨어와 가까운 언어인지도 실감이 날것이다. 참고로 포인터 변수의 포인터도 있고 포인터 변수의 포인터의 포인터도 있다.

파이썬에서는 ctypes 모듈에서 포인터를 지원한다. c 자료형밖에 되지는 않지만 숫자, 문자열은 메모리 공유가 되지 않는 파이썬 특성상 필요하기도 하다. 다만 멀티프로세싱에서는 불가능.

그래도 포인터 못 다루는 사람은 정말 못 다룬다

7.2.1 포인터가 어려운 이유 / 다른 의견

포인터 자체는 그리 어려운 개념이 아니다. 실제 객체에 주소값을 부여하여 접근한다는 개념이 그리 어려운가? 포인터가 어려운 이유는 그 자체가 어려워서라기 보다는, 실제 사용에 있어서 다음의 주제들에 대한 이해가 필요하기 때문이다. 포인터 모른다고 입문서의 포인터 단원 들여다봤자 크게 도움이 되지 않는 이유이기도 하다. 다른 부분을 다 둘러보고 포인터를 다시 보거나 아니면 아예 컴퓨터 과학 개론서 등으로 컴퓨터에 대한 전반적인 이해를 넓히고 보는 편이 나을수도 있다.

  • C언어의 타입 시스템과 암묵적인 형변환
  • C언어의 복잡한 선언(declaration)과 수식(expression)을 읽는 방법
  • C언어 특유의 문자열과 배열과 포인터의 혼용
  • 하드웨어의 메모리 정렬(memory alignment)
  • 객체의 기억수명(storage duration)

하나같이 쉬운 주제들이 아니고, 깊이 있게 파고들면 머리가 터지는 부분이다. C++보다는 덜하지만(C++쪽은 객체지향과 상속, 템플릿이 겹쳐서 정말 끔찍하다!), 어떤 형태의 포인터들이 서로 상호 호환되고 또 암묵적으로 변환될 수 있는가는 사실 완전히 알고 쓰는 사람이 드물다. 그냥 경험적으로 자주 쓰이는 형태만을 어렴풋이 익혀서 쓸 뿐이다.

게다가 C언어의 선언은 연산자들의 조합으로 이루어지기에 각 연산자의 우선순위와 결합방식을 다 외우지 않고서는 이해하기가 극히 어렵다. 오죽하면 이걸 잘 읽는 테크닉이 따로 있을까. C언어에서 복잡한 선언 읽는 방법 일단 C언어의 수식과 연산자 개념에 대해서 제대로 공부할 필요가 있으며, 수식을 계산하면서 각각의 결과값들이 가지는 타입을 같이 생각할 수 있을 정도는 되어야 한다. (일반 수학 계산에는 타입이라는 개념이 없기에, 새로 익혀야 하는 부분이다.)

또한 수식이나 함수 매개변수 선언 내에서 배열이 포인터로 자동 변환되는 동작방식은, 배열이 포인터라고 착각하기 쉽게 만들며, 포인터 상수라던가 하는 실제하지 않는 요상한 오개념을 프로그래머에게 가지도록 유도한다. 심지어 입문서에서 이런 방식으로 설명하는 경우도 있는데 곧은 길을 빙 돌아가는 방식이나 다름없다.

한편, C언어 입문서에서 잘 설명하지 않는, 메모리 정렬이란 개념 또한 포인터를 오류 없이 사용하는 데 있어서 대단히 중요하다. 최대한 간단하게 설명하자면, 데이터가 메모리에 저장될 때 각각의 타입에 맞는 n의 배수의 주소값을 가져야 한다는 개념이다. 예를 들어 4바이트 int형은 주소값이 4의 배수가 되어야 하고 8바이트 long형은 주소값이 8의 배수가 되어야 한다는 식이다(실제로는 하드웨어에 따라 각기 다르며, 더 복잡하다). 이걸 제대로 맞춰주지 않으면 프로그램이 그대로 뻗어버리므로 고급 포인터 테크닉을 쓸때 특히 유념해야 하는 부분이며, 구조체의 패딩과도 연관되는 중요한 개념이다. 그런데 C언어 책에 잘 안나와

마지막으로, 포인터가 가리키는 객체의 기억수명(storage duration)[69]에 대한 관리가 있다. 기억수명이란 해당 객체가 언제 생성되고 언제 소멸되는가에 대한 개념이다. 이것이 포인터와 어떻게 연결되는가 하면, 어떤 A라는 객체가 있고, 이를 가리키는 B라는 포인터를 만들었는데, A가 소멸되었다고 치자. 그러면 B를 통해 이루어지는 모든 동작들이 잘못된 동작이 된다.(dangling pointer) 따라서 이런 일이 생기지 않도록 프로그래머가 처리해 주어야 한다. 기억 수명은 특별히 어려운 개념은 아니지만, 이를 관리하기 위해서는 수많은 객체들의 생성과 소멸 시점을 프로그래머가 혼동 없이 파악해야 하기 때문에 실수가 생기기 쉬우며, C언어는 여기에 대해 안전장치가 전혀 없기로 악명이 높다.

하나같이 입문자가 하루아침에 배우기는 극히 어려운 내용들이고, 사실 입문서에서 자세히 설명하는 부분들도 아니다. 하지만 포인터를 사고 치지 않고 쓰기 위해서는 반드시 알아야 하는 부분들이다. 즉 포인터가 어려운 이유는, 그 자체보다는 그것을 실제로 사용하기 위해 필요한 기반 지식들이 어렵기 때문이다. 그냥 C언어 자체가 불친절하고 어렵다

7.2.2 배열과 포인터 사이의 관계

굳이 이 항목을 따로 만든 이유는, 배열과 포인터 사이의 관계를 올바르게 이해하는 것이 포인터를 이해하는 데 있어 큰 비중을 차지하며, 한편으로 포인터 상수라는 부적절한 설명이 C언어 프로그래머들에게 널리 퍼져있기 때문이다. 포인터 상수라고 좀 부르지 마라 배열과 포인터 사이에 성립하는 정확한 규칙은 다음과 같다.

C99 6.3.2.1 p3.

Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type "array of type" is converted to an expression with type "pointer to type" that points to the initial element of the array object and is not an lvalue.

배열 타입을 갖는 수식(배열 이름 또한 여기에 해당한다)은 자동 변환되어, 그 배열의 첫번째 원소를 가리키는 포인터 주소값을 반환한다. 이 결과값은 lvalue가 아니다. 여기에는 아래의 3가지 예외상황이 존재한다.

  • 예외1 : sizeof 연산자의 피연산자로 쓰인 경우. 예) sizeof(arr)
  • 예외2 : & 연산자의 피연산자로 쓰인 경우. 예) &arr
  • 예외3 : char형 배열의 초기화에 쓰이는 문자열 상수인 경우. 예) char str[] = "hello";
    • 해설 : 문자열 상수(string literal)도 배열이므로, 해당 규칙의 적용을 받아 배열처럼 사용이 가능하다. "abcdefg"[3] 라던가 3["abcdefg"] 라던가... char *str = "hello"; 의 경우에도 "hello"의 첫번째 원소를 가리키는 주소값이 대입된다. 그러나 char str[] = "hello"; 와 같이 문자형 배열을 초기화할 때에는 이 예외3이 적용되어 위에서 설명한 변환 규칙이 적용되지 않는다.

위에서 말하는 수식은 일반적 의미의 수식이 아니라 C언어 문법에서의 수식을 의미한다.

배열 참조 연산자 arr[1] 은 * ( arr + 1 ) 과 동치이며, 후자의 수식은 위의 규칙을 적용받아 해석된다. arr은 그 배열의 첫번째 원소를 가리키는 포인터 주소값이고, 여기에 + 1을 하면 두번째 원소를 가리키는 포인터 주소값이 되며, 여기에 참조 연산자 * 를 씌우면 배열의 두번째 원소 그 자체가 된다.

변수 선언부에서는 위의 규칙은 적용되지 않는다. 오로지 수식에서만 적용되는 규칙이다. 즉 포인터로 선언하면 포인터가 되고, 배열로 선언하면 배열이 된다.

다차원 배열의 경우에는 연쇄적으로 적용되지 않고 딱 한번만 적용된다. array of array of array of int는 pointer to array of array of int로 변환되지, pointer to pointer to pointer to int와 같이 연쇄적으로 변환되지는 않는다는 뜻이다.

실제 예를 들자면, 수식 내에서 쓰인 int arr[3][4][5] 타입은 int (*arr)[4][5] 타입으로 변환되며, int (**arr)[5] 나 int ***arr 타입으로 변환되는 것은 아니다.

arr1 = arr2; 와 같이 배열 끼리의 복사가 되지 않는 것도 이 규칙 때문이다. 이 규칙 때문에 대입 연산자 =의 좌변이 lvalue가 아니게 된다.

한편, 함수 선언시 매개변수 목록에서도 이와 비슷한 규칙이 하나 존재한다.

C99 6.7.5.3 p7

A declaration of a parameter as "array of type" shall be adjusted to "qualified pointer to type", where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation.

함수 선언 시 매개변수로 배열 타입이 쓰일 경우, 이것은 qualified pointer 타입으로 변환된다. 이때 붙이는 type qualifier(const, restrict, volatile를 의미)는 [ ] 안에 적힌 것을 사용한다. (이때 static 도 [ ] 안에 들어갈 수 있지만 이건 또다른 주제라 생략한다)


<syntaxhighlight lang="cpp" line="1">
int f1(int arr[3]);
int f2(int (*arr)[]); // 위의 함수 f1과와 같다

int f3(int arr[const 3]);
int f4(int * const arr); // 위의 함수 f3과 같다
</syntaxhighlight>

이것 또한 다차원 배열의 경우에는 연쇄적으로 적용되지 않고 딱 한번만 적용된다.

<syntaxhighlight lang="cpp" line="1">
int f5(int arr[3][4][5]);
int f6(int (*arr)[4][5]); // 위의 함수 f5 같다.
int f7(int (**arr)[5]); // 위의 함수 f5, f6과 다르다.
</syntaxhighlight>

이 규칙으로 인해서, C에서는 함수의 매개변수로 배열을 직접 복사해서 넘길 수 없다. 다르게 표현하자면, 함수의 매개변수로 배열을 사용할 수 없다.

7.3 중괄호 스타일

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

소스로 보자면 다음과 같다.

  • K&R 스타일

아래와 같이 while, if 문 등의 옆에 여는 괄호가 붙어 있다.

<syntaxhighlight lang="cpp" line="1">
int main()
{
while (x == y) {something();somethingelse();
if (some_error) {/* the curly braces around this code block could be omitted */do_correct();
} elsecontinue_as_usual();

}finalthing();
}
</syntaxhighlight>

  • Allman 스타일

아래와 같이 while, if문 밑에 여는 괄호가 있다.

<syntaxhighlight lang="cpp" line="1">
int main ()
{
while (x == y){something();somethingelse();
if (some_error){/* the curly braces around this code block could be omitted */do_correct();
} elsecontinue_as_usual();

}finalthing();...
}
</syntaxhighlight>

각각의 스타일은 장단점이 있는데, K&R 스타일은 여는 중괄호에 엔터를 치지 않기 때문에 적은 줄 수에 더 많은 내용을 담을 수 있는 반면 중괄호 블록이 눈에 잘 안 들어오는 단점이 있다. 반면에 Allman 스타일은 여는 위치와 닫는 위치가 같기 때문에 중괄호 블록이 명료한 반면에 수직적으로 많은 공간을 차지하는 단점이 있다. 그래서 지문을 많이 넣고 싶지 않은(=되도록 얇게 만들고 싶은) C언어계 책의 9할쯤은 전부 다 K&R 스타일이다. 반면 실제 코드의 스타일은 4할-4할 정도. 예제는 K&R 스타일로 쓰고서도 본문 중에 실제 작업에서는 Allman 스타일을 쓴다고 밝히는 저자도 있다. 2할은 기타 스타일.[72]

그외 다양한 스타일은 위키 문서를 참조하자.

8 관련 문서

  1. 과거 새로운 표준이 200x 년에 나올줄 알고 C0x 로 불렸으나, 2011년에 등장하여 C11 이 되었다. C++ 역시 같은 이유로 C++0x 로 불리다가 2011 년에 등장하여 C++11 되었다.
  2. 이들은 다른 언어가 아니라 버전이 다르다.
  3. Al Stevens의 책에 나오는 말이다.
  4. 물론, 금기라 해서 완전히 없던것은 아니다. 당장 유닉스의 전신이던 MULTICS 부터 PL/1 라는 고수준 언어로 작성되었다. 단, 그렇게 만들어진 MULTICS는 망했다. 유닉스는 멀틱스에 대한 반성에서 단순하게 만드는 방향을 추구했으며, 그렇기에 이름부터가 UNI-로 시작하도록 지어졌다.
  5. 유닉스는 처음에는 어셈블러로 만들어졌지만 점차 C로 대체되었다. 그리고 그덕분에 이식성을 확보하여 여러 기계로 퍼져나갔다.
  6. 물론 믿으면 곤란. 이쪽은 추상화느님으로 마치 자연어처럼 읽히는 코드를 써서 주석 자체를 코드로 최대한 대체해서 주석이 코드의 변화를 못따라가는 불상사를 방지하는 것이다.
  7. 주의해야할것은, 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) 교재이다. 그리고 K&Rea라고 쓰면 곤란하다.
  8. 사실 ANSI C 다음 버전부터는 MS 에서 표준안을 제대로 지키는 컴파일러를 내놓지 않은 영향도 크다.
  9. 여기에 관해서도 혼동이 있을 수 있는데, 이후 ANSI가 C 표준 제정에서 손을 떼고 ISO를 받아들였기 때문에, 공식적으로 ANSI C 는 최신표준인 C11 을 가리킨다. 하지만, 실제 사람들이 ANSI C라 지칭할 때는 ANSI가 표준을 직접 제정한 C89/90를 의미한다. 이 페이지에서도 ANSI C는 C89/90를 의미한다.
  10. 최근 C++11로 업데이트된 4차 개정판에서 드디어 1000 페이지의 벽을 깨고 1300 페이지를 넘겼다.
  11. 실제로 아주 중요한 내용이 구석탱이에 딱 한줄 써 있는 경우가 많다.
  12. 그렇기에 더더욱 초심자에게는 어울리지 않는다.
  13. 어차피 저런걸 모두 담고있는 교재가 없긴 하지만, 더하고 덜하고 정도의 차이는 있게 마련이고, 그 차이는 나중의 삽질과 시간낭비로 반드시 뿌린대로 거두게 된다.(...) 물론, 웹에 널려있는 정보들을 복붙하는식의 습득도 가능하지만, 잘못된 정보인 경우도 많고 그 설명이 제대로된 교재 수준을 넘어가는 경우도 드물다. 또한, 같은수준의 지식이라면 웹서핑보다 교재로 배우는편이 효율도 훨씬 높다.
  14. 초기 C 언어는 커다란 규모의 프로그램을 거의 염두에 두지 않고 개발되었다. 당시 IBM 메인프레임에 사용되던 System/360 이 수천명의 프로그래머가 달라붙어 어셈블리어로 수백만줄이었는데, C 언어로 만들어진 가장 큰 프로그램인 초창기 유닉스의 커널은 고작 만줄정도였다.
  15. 인력도 인력이지만, 당시 머신은 초고도로 최적화를 해야 만족할만한 속도를 보여줬다. 덤으로 C언어 그 자체도 컴파일러를 거쳐 기계어 파일이 나오면 그걸 다시 사람이 직접 최적화를 해 줘야 했다고 한다. 당시 컴파일러의 최적화 성능이 떨어졌던 이유도 있겠지만
  16. 현재 쓰이는 고수준 개념들 자체는 의외로 오래된 경우가 많다. 예를 들어서 쓰레기 수집은 59년도에 최초로 구상되었고, 타입에러를 컴파일 타임에 모두 잡아낼 수 있는 Hindley-Milner 타입 인터페이스가 70년대 후반에 나와서 Haskell등지에서 쓰이고 있다. 클로저 개념도 60년대~70년대에 나왔다.
  17. C99 에서는 struct 마지막 어레이 멤버에 [0] 대신에 그냥 [] 로 써주면 된다.
  18. 배열 경계 좀 넘는다고 뭐 그리 대단한 일이 일어나겠냐고 물으신다면, 배열 경계를 넘기면 그 위에 선언한 변수들을 하나하나 덮어쓰기가 되고, 최종적으로 리턴 어드레스(함수의 실행을 끝낸 후 다시 함수를 호출한곳으로 돌아가는 주소값)까지 덮어쓰는게 가능해진다. 기본적으로 이 취약점을 가진 프로그램을 네트워크에 물리면 원하는 동작을 원격에서 실행시킬 수 있게 되며, 최악의 경우는 루트 권한으로 실행되는 프로그램에서 리턴 어드레스를 쉘을 실행하는 코드가 있는곳으로 덮어쓰면 루트 계정을 원격으로 탈취당할 수도 있다. 1998 년 전세계 서버를 감염시킨 모리스 웜도, finger 라는 유닉스 유틸리티의 버퍼 오버플로우 취약점을 이용하였다. 이 웜은 쉽게 들통나지 않도록 여러가지 카모플라쥬 전략이 많이 포함되어있었고, 코넬대학은 결국 서버를 다운시키는등의 조치를 취했지만 전세계로 퍼져나갔다. 당시 이 웜을 만든 23세의 모리스는 벨 연구소에서 유닉스의 로그인 암호화를 담당했던 사람의 아들이었고, 현재 MIT 대학의 교수이다. 2001년 한국에도 사이버 대란을 일으킨 코드레드 의 경우 전세계의 서버 몇십만개를 감염시킨 사례가 있다. 이 취약점 때문에 경계 체크를 안하는 gets 같은 표준 함수 여러 개를 depreciated(사용 자제) 시켜야 했고, printf의 포맷 문자(%n) 하나가 날라갔다. 심지어 운영체제 자체에서 buffer overflow 방지 메커니즘을 제공하기까지 하는 데도 간간히 뚫리는게 이 취약점이다(..). 물론 공돌이들이 손 놓고 있는게 아니니 방지책도 많이 마련되어있긴 하지만 그럼에도 뚫릴 취약점은 뚫린다. 2014년 OpenSSL에 생긴 재양적인 버그인 하트블리드도 근본적으로는 C언어가 배열의 경계를 체크하지 않는다는 점에서 생겼다.
  19. visual studio 2012 버젼부터 함수에 _s 가 붙은 (printf_s, scanf_s 등) 보안을 위한 함수가 추가 되었다. 기존의 printf 같은 함수는 그냥 쓸 수 있지만 scanf와 같은 함수를 쓰려고 하면 보안에 따른 에러 메시지가 뜬다(...)
  20. 포인터에 익숙하면 오히려 쉽게 느껴질 수도 있다. 포인터 자체가 iterator 패턴과 유사하게 사용 가능하며, 증가, 배열, 참조 연산자를 통해서 짧은 수식으로 여러가지 연산을 한번에 처리할 수 있기 때문이다. 예를 들면 while ( *dst++ = *src++ ) ; 와 같은 짧은 코드만으로 문자열 복사가 가능하다.
  21. String literal. "str" 같은 따옴표로 둘러싸인 부분을 뜻함
  22. 정확하게는, 문자열 상수는 변경 불가능한 char형 배열이다. 따라서 마치 배열이름처럼 "abcdefg"[3]와 같이 쓸 수 있으며, 이 수식의 결과값은 'd'이다.
  23. "배열 타입의 결과값을 갖는 모든 수식은, 몇몇 예외상황을 제외하고는 그 배열의 첫번째 원소를 가리키는 포인터 주소값으로 변환된다." 이것이 흔히 말하는 '포인터 상수'의 진짜 모습이다.
  24. 수정하기 전 문서에서는 '문자열'의 주소라고 설명하고 있었는데, 정확히는 그 '문자열의 첫번째 원소'의 주소값이다. 같은 말 아니냐고? &arr과 &arr[0]이 다른 것처럼 이 두가지도 서로 다르다(주소값은 같게 나오지만 그 타입은 서로 다르다).
  25. gcc 4.8 기준으로 'suggest parentheses around assignment used as truth value'(값을 진리값으로 사용하는 assignment는 괄호를 씌우는 것을 권장함)라는 워닝 메시지가 뜬다.
  26. 보통 if 문 내부에 의도된 대입 연산자의 경우에는 if ((result = fn(...)) != NULL) { ... } 같은 식으로 대입 표현식을 괄호로 감싼다.
  27. C99 에서 _Bool타입이 생기긴 했다. stdbool.h 를 포함할 경우, 그냥 bool로 사용도 가능하긴 하다. 왜 그냥 bool로 안넣었냐면, 기존 프로그램중에 bool 이름을 사용한 코드가 있을 가능성이 있기때문. 반면, 언더스코어 _ 뒤에 대문자가 오는 이름은 C 표준에서 사용하지 말라고 먼저 명시해놨기 때문에 이것을 사용한 _Bool 이 되었다.
  28. 음수도 true로 처리된단 뜻이다. 없으면 define 문으로 선언해서 쓰면 된다.
  29. int i='a'; char c='b'+i 같은것들이 매우 자연스럽다.
  30. 따라서 char c = 0; 이라고 하면 c에는 '\0'이 대입된다. 1과 '\1'과 '\x1'이 같은 값인 것과 같은 이치.
  31. 간단한 예를 들자면 타입과 배열과 포인터가 혼돈의 카오스가 되는 이유인데, 사실 데이터를 실제 저장장치에 저장할 때 이게 정수인지 문자인지 배열 주소인지 포인터 주소인지는 기재되지 않기 때문이다.(그걸 일일히 기재하는 건 시간과 공간의 낭비고, 쓰잘데기도 없는데다가, 데이터가 그런 단순데이터만 있는게 아니기 때문에 거의 불가능하기까지 하다.) 마치 메모장에는 32871647200이라고 적어놓고 나중에 메모장을 펼쳐서 이 숫자가 무슨 숫자였지.... 하고 해석하는 것과 같다. 그 데이터가 무슨 데이터였는지 정확히 기억하고 알맞게 사용하는 것은 어셈블리에서는 순전히 사용자의 몫이고, 그 때의 전통이 살아있던 C에서는 기본적인 타입같은 건 컴파일러가 처리해주되 사용자가 그런 세세한 부분을 컨트롤할 수 있도록 허용해주는 것이다.
  32. 소켓 프로그래밍이 보통 이렇다.
  33. 비트 수준 연산이 많이 사용되고 속도도 빨라야하기 때문에 많은 언어의 암호학 라이브러리는 거진 C로 구현되어있다.
  34. 사실 C++이나 다른 컴파일 언어에 비해서 C언어 자체는 컴파일 속도는 굉장히 빠른 편이다.
  35. 정확히는 자바는 구현 언어로만 사용하는 것이며, 라이브러리는 전부 안드로이드 SDK를 사용해야 한다. 따라서 JVM 바이트코드를 생성하는 스칼라클로져같은 언어로 코딩하는 것도 불가능한 것은 아니다.
  36. 쉽게 말해서, OS의 특정 기능이 느리면 그 기능을 사용하는 어플리케이션들 전부가 덩달아 느려진다.
  37. 다만, C 표준 자체가 많은 부분을 '모든 컴파일러에 동일하게'가 아니라 구현체에 따라(implementation-dependent) 정의하게 하기 때문에 이런 부분들에 대해서는 전부 각 플랫폼마다의 특성을 따로 반영하여 코딩해주어야 한다.
  38. 실제로 요즘 나오는 C 교재중에는 후반부에 OOP 챕터도 넣어놓은 경우가 가끔 있다.
  39. 바퀴부터 만든다는것으로 일반적으로는,이미 다 존재해서 갖다쓰기만 하면 되는것을 괜히 고민하면서 또 만들어내는걸 비판하는데 쓰이는 문구이다. 다만 실전 개발에 있어서는 안좋은 습성이지만 교육용으로는 의도적으로 한번씩은 거치게 하는 편이다. 재발명하면서 기반 시스템의 구조에 대해서 알아갈 수 있기 때문이다.
  40. 유닉스의 경우 POSIX API, 윈도우의 경우 Windows API
  41. 출처 *
  42. Objective-C 는 C 의 완전한 superset이기때문에 언어 자체로만 보면 사실상 C에 객체지향을 얹은정도의 언어이다.
  43. C는 애초에 유닉스 운영체제를 만들기 위해 탄생한 언어이기때문에 유닉스 운영체제와 시스템 궁합이 좋은편이다.
  44. 사실, Clang은 OSX 뿐만 아니라, GCC를 대체하는 것이 목표기 때문에 OSX외에 유닉스 계열 운영체제에도 사용된다.
  45. 때문에 대부분 언어에서는 if에 bool값이 아닌 int값 등이 들어오면 에러를 내며, 일부 언어에서는 if같이 비교연산이 필요한 곳에서 대입연산을 쓰면 컴파일 에러를 낸다.
  46. Python과 그에 영향을 받은 언어들은 이런 영향에서 약간 자유롭다. 이런 언어들은 C와 중괄호가 아닌 들여쓰기로 블럭을 구분하는 특징이 있다.그리고 Delphi는 중괄호가 주석이어서 C 사용자가 멋모르고 코드를 짜다가 혼돈의 카오스에 빠지는 경우가 많다
  47. 단, 소스 단위가 아닌 라이브러리 수준에서의 호환성은 여전히 좋다.
  48. 관계가 복잡한데, 볼랜드가 개발언어쪽만 전담하는 코드기어라는 자회사를 설립하면서 모든 권한을 넘겼는데, 코드기어가 엠바카데로사와 합병되면서 이렇게 된 것. 그래서 델파이의 개발도 볼랜드->코드기어->엠바카데로의 순서로 넘어갔다.
  49. 프로그래머 포함
  50. 다만 C를 본격적으로 배우는 목적에서 VS는 추천되지 않는다. 비주얼 스튜디오는 어디까지나 C++ 개발툴이다. C++언어 내에서 C를 쓸 수 있기 때문에 묻어가는 것 뿐. C99조차도 완전히 지원이 안된다!
  51. 사실은 애플이 GCC를 가져다가 사용하는 대신, 자체적으로 GCC에 추가한 기능 일부를 다시 GCC측에 돌려주기로 약속했었다.
  52. 게다가 LLVM은 이것을 처음으로 고안한 사람을 애플이 스카우트하면서 거의 애플 소유의 프로젝트가 되었다. 하지만 스티브 잡스의 애플 복귀 이후 애플이 진행하는 대부분의 소프트웨어 프로젝트가 오픈소스인지라 이것도 역시 오픈 소스로 계속 진행중.오라클 보고있나?
  53. 처리할 데이터의 양이 커지면 처리 시간이 지나치게 증가하는 알고리즘.
  54. Structure And Interpretation Of Computer Programs. MIT에서 만든 계산기과학의 고전과도 같은 책이다.
  55. char, int, float, double과 같은 기본 자료형에서 유도되는 형태의 자료형으로, 다른 유도 자료형으로는 배열이 존재한다.
  56. 예를 들어 포인터를 버그 없이 쓰려면 적어도 메모리의 정렬 제한 정도는 이해를 하고 있어야 한다. 그렇지 않으면 잘못된 포인터 테크닉을 남발하는 경우가 생긴다.
  57. 특히 링크드 리스트를 배우다 보면 내가 무슨 짓을 하고 있는건지 스스로를 자책할 때가 많다.
  58. C#이나 자바의 경우 기본적으로 객체 참조 기반으로 돌아간다. 하지만 참조 관련된 일은 저단계에서 알아서 해주기 때문에 좌우당간 쌩 포인터잡고 낑낑거리기 보다는 쉽다.
  59. C#은 unsafe 키워드로 정말정말 간절하게 필요한 경우 유사하게 만들어 사용할 수 있음. Java에도 Unsafe 클래스가 있긴 하지만 팩토리 메소드가 막혀있어 리플렉션을 사용해야만 이용할 수 있다. 게다가 자바에서 굳이 포인터를 직접 관리해줄 필요는 없다.
  60. OOP 같은것도 밑바닥은 결국 포인터다
  61. 코어 임베디드 개발 영역에서는 포인터 없이는 아예 못하는 것도 많다.
  62. 사실 8비트 CPU도 마찬가지지만.
  63. near pointer 는 offset 만 저장한다. data segment 가 한개뿐인 상황에서는 offset 이상의 정보가 필요가 없으니.
  64. 사실 32비트 시대에서도 DOS는 호환성 문제로 기본적으로 16비트 프로그래밍을 이용했기 때문에 큰 의미가 없었다. 이 부분은 16비트 버전 윈도우도 마찬가지.
  65. Intel CPU에서 16비트 모드의 경우, seg:ofs로 나뉘는데, 이가 가리키는 주소는 addr = (seg << 4) | ofs; 로 표현되며 seg, ofs 둘다 16비트이기 때문에 이들의 값이 달라도 같은 주소를 가리키게 되는 경우가 많다. 예를 들어서, 22eeh:0000h = 22e0h:00e0h = 2200h:0ee0h = ...
  66. 몇몇 학교는 아예 MS의 지원을 받아서 C#을 입문 단계에서 강의한다.
  67. 하지만 포인터를 배울 필요가 없다는 건 아니다. 이를테면 자바의 클래스 변수는 참조변수인데, 포인터 혹은 적어도 비슷한 개념이라도 잡고 있어야 얕은 복사/깊은 복사 등을 이해할 수 있다. 물론 입문자들을 위한 자바 강의에서는 포인터 자체를 언급하지 않고 참조변수 그 자체를 상세히 가르쳐준다. 그러나 참조변수를 상세히 이해하고 있다면, 사실 포인터의 개념은 자신도 모르는 사이에 대부분 배웠다고 할 수 있다.
  68. 그냥 눈 딱 감고 8비트때 책꺼내서 Z80 핸드어셈블 3일만 해보자. 명령도 레지스터도 적어서 장난감으로 좋다.
  69. scope와 혼동하는 사람이 많은데 서로 구분되는 개념이다.
  70. 한때 메일서버계의 IE 로 불리던 sendmail 의 저자이며, 게이 프로그래머로 유명한 Eric Allman이라는 사람의 이름에서 따옴. BSD style이라고도 한다.
  71. Kernighan&Ritchie의 <The C Programming Language> 에서 쓰여졌기 때문
  72. for(i=0;i<n;i++) { work(); } 이런 식으로 쓰는 사람들도 있기는 있다.for과 if등은 한 함수를 실행시킬거면, 중괄호를 안쳐도 되긴하다. 다만, 들여쓰기 잘못하면 피바람분다.