C 언어는 명령형-절차형 프로그래밍 언어이다. 현재 프로그래밍 언어 중에 가장 많이 사용되고 있는 언어 3위 안에 들어가는 언어이다. 고급 프로그래밍 언어이며, C 계열 언어의 문법 구조의 기초가 되는 언어이다. 창시자는 데니스 리치.

1 역사

C 언어는 유닉스 운영체제의 이식성을 높이기 위해 만들어졌다. 전체적인 특징이나 언어 이름은 동료인 켄 톰슨의 B 언어에서 유래되었다.

초기 개발은 1969년에 시작되어 1973년까지 진행되었다. 이렇게 만들어진 C 언어의 초기 버전은 K&R C 언어로 불리는데, 데니스 리치와 브라이언 커니핸이 집필한 서적인 The C Programming Language라는 책에서 유래되었다.

미국 국가 표준 협회에서 C 언어를 ANSI 표준으로 등록하면서 이후 버전은 ANSI C라고 불린다. ANSI C 표준이 재정되면서 데니스 리치와 브라이언 커니핸의 The C Programming Language도 2판이 출판되었다.

ANSI C 표준 재정 이후 오랫동안 새 표준이 재정되지 않다가, 1990년도에 몇 번의 수정안이 출판되다가 1999년에 C99 표준이 재정되었다.

이후 2011년에 C11 표준이 재정되었다.

2 특징

어셈블리 언어와 가까운 고급 프로그래밍 언어라는 말을 들을 정도로 고급 프로그래밍 언어의 특징인 생산성과 가독성을 유지하면서도 저급 프로그래밍 언어의 특징인 빠른 실행 속도와 자유도를 가진다.

컴파일러에 따라 다르지만 대체로 인라인 어셈블리 기능을 제공하는 경우가 많다. 이로 인해 고급 프로그래밍 언어 코드 내에 저급 프로그래밍 언어 코드가 들어가 있는 경우도 종종 볼 수 있다.

C 언어의 가장 큰 특징은 포인터 문법. 저급 프로그래밍 언어의 특징인 포인터 문법이 존재하는 몇 안 되는 고급 프로그래밍 언어이다.

문법이 매우 자유로워서 사람에 따라 코드 스타일이 모두 제각각이다. 때문에 개발에 앞서서 다른 언어들에 비해 코딩 스타일을 좀 더 세세하게 잡는 편이다.

2.1 기본 자료구조

C는 기본 자료구조 타입체계를 제공하며, 타입체계는 변수명(식별자)과 독립적이다.[주 1] C 개발자가 핵심적으로 다루어야 하는 주요 타입은 int, char, float, double이 있다. 각 타입은 컴파일러에 따라 용량이 다르지만, 보통 int 타입은 4바이트이다.

C는 확장적인 자료구조도 제공하는데, 대표적으로 구조체(struct), 공용체(union), 열거형(enum), 그리고 배열(array)과 배열에 기반한 문자열 타입이 있다. 모든 자료구조에는 대응하는 포인터 타입이 존재하며, 포인터 타입 데이터의 크기는 컴파일러가 인식한 메모리의 단위 워드의 용량과 같다.

구조체는 COBOL에서 쓰던 record 구조에서 많은 특징을 가져왔다. 어떤 구조체의 내부에 그 구조체 자신과 동일한 타입을 가리키는 포인터를 선언할 수 있다. 이러한 재귀 참조가 가능한 이유는 재귀 참조를 허용하더라도 구조체의 용량이 변하지 않기 때문이다.

공용체는 같은 공간에 다른 타입의 정보들을 할당할 수 있는 기능을 제공해준다. 이러한 기능은 메모리를 절약할 수 있게 해주어 옛날의 경우 사용하였으나, 공용체 사용 시 타입 체계를 무너뜨릴 위험이 크기 때문에 오늘날에는 사용 권장되지 않는다. 공용체의 존재를 비롯한 연산 자체의 자유분방함으로 인해, C는 약 타입 언어라고 평가받는다.

열거형은 서로 다른 상태를 직관적으로 표현할 수 있다.

배열은 기본적으로, 할당된 공간을 가리키는 상수 포인터(즉, const type* )이며 배열 연산은 포인터 연산이다. 배열은 반드시 선언할 때 그 크기가 정해져야 한다. 즉, 배열 선언 시 입력되는 첨자는 상수여야 한다. 예를 들면, int arr[3][3][3]라는 텐서를 C에서 배열로 선언하면, 그 배열이 가리키는 공간의 크기는 3*3*3*word, 즉 27워드로 고정되며 이는 프로그램이 종료될 때까지 불변적이다. int dim = 3; int arr[dim][dim][dim]; 꼴 선언은 허용하지 않는다.

문자열은 문자의 배열이며, 기본적으로 표현하고자 하는 문자열의 크기 + 1워드만큼의 용량을 차지한다. 왜냐하면 C에서 배열은 그냥 할당된 공간일 뿐이기 때문에(!) 배열의 크기를 배열 객체로부터 뽑아 쓸 수 없다.[주 2] 따라서 하나의 문자열을 출력하려 할 때도, 문자열의 끝을 표현하는 제어 문자 \0을 문자열 배열의 끝에 붙여야 한다. 즉, char x[] = "Hello, World!\n";라는 문자열 선언은 본질적으로 char x[14] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\n', '\0'};과 다르지 않다.

2.1.1 포인터와 메모리 관리 체계

C에서는 메모리 관리를 바로 당신이 해야 한다. 자바나 파이썬을 비롯한 고급 언어의 주된 특징은 더이상 쓰지 않게 된 메모리를 상황에 따라 해방시켜주는 가비지 컬렉터가 존재한다. 그러나 C와 C++은 그 가비지 컬렉터가 바로 당신이기 때문에, 제때제때 사용한 메모리를 해방시켜주지 않거나, 실수로 메모리를 정확하게 해방하지 않을 경우, 당신의 컴퓨터에 참조할 수 없는 메모리가 넘쳐난다.[주 3]

그렇다면 C의 기본적인 메모리 관리 체계는 어떻게 되는가? 함수 안에서 선언하고, 함수 호출이 이루어질 때마다 한 방향으로 메모리를 할당하여 쌓아놓고, 호출이 끝나면 그 함수에 할당된 메모리를 해방시키는 메모리 영역이 존재하는데, 이를 스택 영역이라고 부른다. 왜냐하면 기본적으로 스택 영역을 스택으로 구현하기 때문이다. 반면 함수 호출이 끝나도 사라지지 않는 변수를 모아둔, 가변 데이터나 전역 변수들을 할당하는 메모리 영역이 존재하는데, 이를 힙 영역이라고 부른다.

당신이 현대적인 동적 프로그래밍을 하기 위해서, 즉 동적 배열이나 가변 길이 문자열을 사용하고 싶다면, 기본 제공 라이브러리를 통해 직접 메모리를 할당해야 한다. 이러한 할당을 동적 메모리 할당이라고 부르며, C의 stdlib 라이브러리의 malloc(size)/free(m) 함수를 통해 힙 영역에 메모리를 동적으로 할당하고 제거할 수 있다.

3 컴파일러의 종류

4 파생 언어

5 부연 설명

  1. 구 Fortran의 경우 변수의 이름에 따라 숫자형 변수가 정수형인지 부동소수점실수형인지 바뀌었지만, C는 그렇지 않고 원하는대로 변수명을 선언할 수 있다.
  2. 이러한 단순성이 C로 짜인 프로그램을 빠르게 만드는 이유이기도 하다. 역으로 말하면, 이러한 단순성을 이해하지 못한다면 C로 짠다고 하더라도 충분히 빠르고 강건한 프로그램을 작성할 수 없다.
  3. 해방되지 않지만 참조할 수 없는 메모리를 고아(orphan), 참조 대상이 없는 포인터를 과부(widow) 포인터라고 부른다.