C (프로그래밍 언어)

내위키
Dennis (토론 | 기여)님의 2023년 2월 9일 (목) 05:28 판
(차이) ← 이전 판 | 최신판 (차이) | 다음 판 → (차이)
  • 프로그래밍 언어 C#을 찾으셨다면 여기로.
#include <stdio.h>

int main()
{
    printf("You\'re watching NeWiki.\n");

    return 0;
}

고급 프로그래밍 언어의 하나. 우리나라에서는 보통 'C언어'라고 부른다. 1972-73년 사이에 벨연구소에 근무하던 데니스 리치가 개발한 언어로, 원래는 유닉스에서 쓸 유틸리티를 만들기 위해서 개발했으나 이후 아예 유닉스 커널 자체를 C로 작성했다. 이는 역사상 최초로 운영체제의 커널을 고급 언어로 작성한 것이기도 하다. 70년대에 나온 어찌 보면 구닥다리 언어라고 볼 수 있지만 지금까지 수많은 언어들이 나온 와중에도 지존의 자리를 놓치지 않고 항상 정점에 군림해 온 언어이기도 하고, 이후 나온 언어들에 지대한 영향을 미친 언어이기도 하다. C 언어 자체는 B 언어에서 나온 것이고, B 언어는 1960년에 발표된 알골이라는 언어에서 나온 것이다. C를 비롯해서 알골에서 파생된 프로그래밍 언어들을 '알골계 언어'로 묶어 부르기도 한다. 데니스 리치는 원래 같은 벨연구소에 근무하던 켄 톰슨[1]과 함께 B 언어라는 것을 만들었는데, 이를 바탕으로 후계자 격인 C가 탄생했다. 이름 역시 B의 뒤를 잇는 언어라는 뜻으로 C가 된 것이다. 2001년에 C++ 언어를 대체하는 것을 목표로 D 언어라는 게 나오긴 했지만 사용층은 미미한 수준.

이전에도 포트란, 코볼, 알골, 파스칼과 같은 고급 언어들은 있었지만 이들은 대체로 어떤 분야에 특화된 경우가 많았고, 특히 하드웨어를 직접 건드리는 운영체제나 하드웨어 드라이버와 같은 프로그램을 고급 언어로 만든다는 건 말도 안 되는 소리였다. 일단 이들 언어가 하드웨어에 직접 접근할 수 있는 기능이 빈약했다. 그러니 하드웨어를 저수준으로 조작하는 프로그램을 만들기 위해서는 기계어를 그나마 사람이 읽을만하게 만들어주는 어셈블리어를 쓸 수밖에 없었고, 어셈블리어는 하드웨어에 따라 다르기 때문에 이식성도 영 꽝이었다. 그에 반해 C는 포인터를 통해 하드웨어 메모리를 직접 조작하는 기능을 제공했다. 고급 언어와 하드웨어의 밀착성이 확 좋아진 것이다. 한술 더 떠서 볼랜드의 터보 C는 아예 C 코드 안에 어셈블리어를 쓸 수 있는 인라인 어셈블리 기능까지 지원했다. C로도 안 되는 부분은 아예 그냥 어셈블리어를 집어넣어서 해결할 수 있는 건데 이는 그만큼 C 언어가 기계어와 밀착성이 좋다는 뜻이기도 하다.

컴파일러를 사용하며, 먼저 소스 코드(.c 파일)를 컴파일해서 목적 파일(.obj)을 만든 뒤, 링커로 목적 파일과 라이브러리를 합쳐서 최종 실행 파일을 만든다. 라이브러리에 있는 기능을 가져다 쓰기 위해서는 이를 사용하는 소스 코드 파일의 첫머리에 해당 라이브러리의 헤더 파일(.h)을 포함시켜야 하며, 이렇게 하면 컴파일과 링크 과정에서 필요한 라이브러리를 합쳐서 실행 파일을 만든다. 프로그램의 규모가 커질수록 소스 코드로 여러 파일로 나뉘고 컴파일과 링크 과정이 복잡해지는데, 하나의 결과물을 내는 데 필요한 소스 코드 파일을 묶어 프로젝트로 다루고, 이들 파일의 컴파일과 링크를 일괄해서 해주는 메이크(make), 빌드(build)와 같은 도구를 많이 사용한다.

C에 객체지향 개념을 집어넣은 C++, 마이크로소프트가비지 컬렉션을 비롯하여 현대 프로그래밍 언어의 개념을 다수 집어넣은 C#[2]처럼 아예 대놓고 C의 확장형을 표방한 언어가 있는가 하면, 자바, 자바스크립트 를 비롯한 많은 인기 언어들도 C를 기반으로 한 것을 충분히 확인할 수 있다. 나름 C의 한계나 단점을 극복한다고 여러 가지 대안들이 나와 있지만 시스템 프로그래밍 쪽에서는 여전히 C/C++가 압도적이며, 리눅스의 아버지 리누스 토르발스는 C++ 같은 건 난잡하다고 까면서 여전히 C를 최고로 인정하고 있다.

일반 응용프로그램을 만들 때는 별로 쓰이지 않지만 하드웨어와 많이 밀착되어 있는 운영체제 커널, 시스템 유틸리티, 닥치고 최대한의 속도를 추구하는 프로그램, 임베디드 시스템처럼 아주 작은 시스템 자원을 쥐어짜야 하는 환경에서 굴리는 소프트웨어는 여전히 C가 막강한 지위를 가지고 있다.[3] 아직까지도 운영체제나 시스템 프로그래밍 쪽으로는 C를 대체할 마땅한 대안이 없는 실정이다.[4][5] 현대 언어들에 비하면 진입장벽이 높지만 일단 잘 배워놓으면 수요도 많고 급여 조건도 높다. 따라서 대학교의 컴퓨터 관련 학과에서는 여전히 C를 필수로 가르치고 있다.

이식성도 좋은 편. 지금이야 자바처럼 바이트코드로 컴파일하고 가상머신 위에서 돌아가도록 해서 컴파일조차도 다시 할 필요가 없는 언어들도 있었지만 C 언어는 하드웨어에 직접 접근하고 커널까지 만들 수 있을 정도로 시스템과 가깝게 붙어 있으면서도 이식성이 좋은 편이어서, C로 작성한 유닉스 운영체제는 여러 가지 하드웨어에서 돌아갈 수 있는 범용 운영제체로 입지를 다질 수 있었다. 리눅스가 모바일과 일반 컴퓨터, 슈퍼컴퓨터까지 돌릴 수 있을 정도로 엄청난 범용성을 자랑하는 것 역시 뒷배경에는 C가 있다.

C는 기본적으로 프로그래머에 높은 자유도를 제공하는 반면, 그 자유에 따른 책임도 프로그래머가 다 져야 한다. 현대 언어들이 많이 채용하는 가비지 컬렉션의 경우, 더 이상 사용하지 않는 메모리를 자동으로 해제해 주므로 프로그래머는 메모리 반납에 신경 쓸 필요가 없이 코딩을 할 수 있는 반면, C는 동적으로 받은 메모리는 빼놓지 않고 반납해야 한다. 그렇지 않으면 메모리 누수 현상이 발생하고, 이것이 누적되면 사용 가능한 메모리가 바닥나서 아예 시스템 전체가 마비되는 사태에 이를 수 있다. C++을 사용한다면 클래스를 이용해서 파괴자에서 메모리를 해제하도록 하거나, 스마트 포인터를 활용해서 어느 정도 문제를 해결할 수는 있다. 특히 포인터 실수로 온갖 심각한 버그가 발생하는데, 어떤 변수의 포인터를 잘못 사용하면 그 변수가 사용하는 메모리 공간을 넘어가서 다른 변수를 건드리거나, 심지어는 시스템이 사용하는 메모리를 건드려서 시스템 전체의 오류를 일으키기도 한다. 요즈음의 운영체제는 권한 없는 프로그램이 시스템 영역으로 쳐들어올 가능성을 원천봉쇄하지만 MS-DOS처럼 격리 기능이 부실한 운영체제에서는 포인터를 잘못 쓰면 그런 일이 발생할 수 있다.

거의 대부분 프로그래밍 언어 기초 책이 첫 프로그램 소스 코드로 "hello, world"를 출력하는 코드를 싣고 있는데, 이것도 C가 원조격. 1978년에 C의 아버지 데니스 리치가 브라이언 커니핸과 함께 쓴 C 학습의 바이블이라 할 수 있는 <The C Programming Language>교재의 첫 번째 예제가 바로 그것이었고, C의 인기가 폭풍 성장하면서 다른 프로그래밍 언어들도 첫 번째 예제 프로그램으로 이를 사용한다.

문서 앞에 있는 코드에 관해 아주 간단하게 설명하면, #include 문은 전처리기(pre-processor)라는 것으로 다른 소스 파일을 포함시키거나, 매크로를 확장시키거나 하는 명령을 넣으며 이는 컴파일 전에 이루어진다. #include <stdio.h> 문은 stdio.h[6]라는 헤더 파일을 포함시키라는 명령이다. 그 다음 main()은 함수를 정의하는 것으로 C로 만든 실행 가능한 프로그램은 반드시 main() 함수가 있어야 하며, main()에서 실행이 시작된다. 코드 블록(여기서는 함수 안에서 실행할 코드)은 중괄호로 열고 닫는다. printf()는 괄호 안에 전달된 인자를 표준 출력장치에 출력하는 함수로, 이 함수를 쓰기 위해서는 stdio.h 헤더 파일을 #include로 포함시켜야 한다. 문자열 안의 따옴표는 그냥 쓸 수 없고 이스케이프 처리(\')를 해 줘야 하며, 문자열 뒤에 있는 '\n'은 줄바꿈을 뜻하는 특수문자다. 마지막으로 exit 명령어는 오류 코드 0, 즉 아무 오류 없이 프로그램이 정상으로 끝났다는 것을 운영체제 또는 이 프로그램을 실행시킨 다른 프로그램에 알리면서 프로그램을 끝낸다.

각주

  1. 구글에서 Go 언어를 만드는 데 참여했다.
  2. C#이라는 이름이 가지는 의미는 악보 기호처럼 C를 반올림했다는 뜻도 있고, C++++라는 뜻도 있다. + 기호를 2행 2열로 배치하면 # 모양이 되기 때문.
  3. 초소형 기기들은 C로 만든 프로그램을 돌리기에도 버거운, 그야말로 바이트 단위로 절약해야 할 만큼 시스템 자원도 초소형인 경우가 많아 이 때에는 어셈블리어 말고는 답이 없다. 그래서 여전히 어셈블리어 개발자의 수요도 있다.
  4. 다만 최근에는 모질라재단이 만든 Rust가 한 가지 대안으로 떠오르고 있다. 성능 저하를 일으키는 주요한 원인인 가비지 컬렉터를 쓰지 않으면서도 메모리 누수 위험을 해소한 것이 가장 큰 장점으로 꼽힌다. 동적 메모리 할당을 받는 변수의 할당과 해제 시점을 컴파일러가 추적할 수 있기 때문에 컴파일 타임에 메모리 누수 버그를 잡아낼 수 있다.
  5. 구글이 만든 Go도 나름 C의 역할을 대체하려는 목표를 가지고 있으며, 시스템 프로그래밍이 가능은 하지만 가비지 컬렉터에 따른 성능 문제로 시스템 프로그래밍의 영역에서 C를 대체할 것으로는 보고 있지는 않다.
  6. studio.h가 아니다. 'Standard input/output'을 줄인 말이다. 초보들은 물론이고 좀 경험이 있는 프로그래머도 정말 저 오타를 많이 낸다.