컴퓨터 프로그램

[섬네일|[자바스크립트 언어로 작성된 컴퓨터 프로그램의 소스 코드. appendChild 메서드를 보여준다. 이 메서드는 기존 부모 노드에 새 자식 노드를 추가한다. HTML 문서의 구조를 동적으로 수정하는 데 일반적으로 사용된다.]]

**컴퓨터 프로그램**(, )은 컴퓨터가 실행하도록 프로그래밍 언어로 작성된 수열 또는 명령어 세트이다. 이는 문서 및 기타 무형 구성 요소를 포함하는 소프트웨어의 한 구성 요소이다.

인간이 읽을 수 있는 형태의 컴퓨터 프로그램은 소스 코드라고 불린다. 컴퓨터는 고유한 기계어 명령어만 실행할 수 있기 때문에 소스 코드를 실행하려면 다른 컴퓨터 프로그램이 필요하다. 따라서 소스 코드는 해당 언어로 작성된 컴파일러를 사용하여 기계어 명령어로 번역될 수 있다. (어셈블리어 프로그램은 어셈블러를 사용하여 번역된다.) 결과 파일은 실행 파일이라고 불린다. 또는 소스 코드는 해당 언어로 작성된 인터프리터 내에서 실행될 수 있다.

실행 파일의 실행이 요청되면 운영체제가 이를 메모리에 로드하고 프로세스를 시작한다. 중앙 처리 장치는 곧 이 프로세스로 전환하여 각 기계어 명령어를 가져오고, 디코딩하고, 실행할 수 있다.

소스 코드의 실행이 요청되면 운영체제가 해당 인터프리터를 메모리에 로드하고 프로세스를 시작한다. 그런 다음 인터프리터는 소스 코드를 메모리에 로드하여 각 문을 번역하고 실행한다. 소스 코드를 실행하는 것은 실행 파일을 실행하는 것보다 느리다. 또한 인터프리터는 컴퓨터에 설치되어 있어야 한다.

## 예시 컴퓨터 프로그램

""Hello, World!" 프로그램"은 언어의 기본 구문을 설명하는 데 사용된다. BASIC (1964) 언어의 구문은 언어를 배우기 쉽게 하기 위해 의도적으로 제한되었다. 예를 들어, 변수는 사용하기 전에 선언되지 않는다. 또한 변수는 자동으로 0으로 초기화된다. 다음은 Basic으로 작성된 숫자 목록의 평균을 구하는 예시 컴퓨터 프로그램이다.

```basic
10 INPUT "How many numbers to average?", A
20 FOR I = 1 TO A
30 INPUT "Enter number:", B
40 LET C = C + B
50 NEXT I
60 LET D = C/A
70 PRINT "The average is", D
80 END
```


기본 컴퓨터 프로그래밍의 메커니즘을 배우면, 대규모 컴퓨터 시스템을 구축하는 데 사용할 수 있는 더 정교하고 강력한 언어들이 있다.

## 역사

소프트웨어 개발의 개선은 컴퓨터 하드웨어의 개선의 결과이다. 하드웨어 역사의 각 단계에서 컴퓨터 프로그래밍의 작업은 극적으로 변화했다.

### 해석 기관
섬네일|right|노트 G에 있는 러브레이스의 설명
1837년, 자카르 직기는 찰스 배비지에게 해석 기관을 만들도록 영감을 주었다.
계산 장치의 구성 요소 이름은 섬유 산업에서 차용되었다. 섬유 산업에서는 실을 보관소에서 가져와 가공했다. 이 장치에는 각각 50자리 십진수 1,000개를 저장할 수 있는 메모리인 보관소가 있었다. 보관소의 숫자들은 처리하기 위해 공장으로 옮겨졌다. 이 엔진은 두 세트의 천공 카드를 사용하여 프로그래밍되었다. 한 세트는 작업을 지시했고, 다른 세트는 변수를 입력했다. 그러나 수천 개의 톱니바퀴와 기어는 완전히 작동하지 않았다.

에이다 러브레이스는 찰스 배비지를 위해 해석 기관 (1843)에 대한 설명을 만들었다. 이 설명에는 해석 기관을 사용하여 베르누이 수를 계산하는 방법을 완전히 상세히 설명한 노트 G가 포함되어 있었다. 이 노트는 일부 역사가들에 의해 세계 최초의 컴퓨터 프로그램으로 인정된다.

### 보편 튜링 기계
350px|right
1936년, 앨런 튜링은 모든 계산을 모델링할 수 있는 이론적 장치인 보편 튜링 기계를 소개했다.
이것은 무한히 긴 읽기/쓰기 테이프를 가진 유한 상태 기계이다. 이 기계는 알고리즘을 수행하면서 테이프를 앞뒤로 움직여 내용을 변경할 수 있다. 기계는 초기 상태에서 시작하여 일련의 단계를 거쳐 정지 상태를 만나면 멈춘다. 오늘날의 모든 컴퓨터는 튜링 완전하다.

### 에니악
[섬네일|right|[글렌 A. 벡이 에니악의 진공관을 교체하고 있다.]]
에니악 (ENIAC)은 1943년 7월부터 1945년 가을 사이에 제작되었다. 이것은 진공관 17,468개를 사용하여 전자 회로를 만든 튜링 완전 범용 컴퓨터였다. 핵심적으로, 이것은 일련의 파스칼 계산기들이 서로 연결된 것이었다. 40개의 장치는 30톤이었고, 의 공간을 차지했으며, 유휴 상태일 때 시간당 $650 (1940년대 통화로)의 전기를 소비했다. 20개의 십진법 누산기를 가지고 있었다. 에니악을 프로그래밍하는 데는 최대 두 달이 걸렸다. 세 개의 기능 테이블은 바퀴에 장착되어 고정된 기능 패널로 굴려야 했다. 기능 테이블은 무거운 검은색 케이블을 플러그판에 연결하여 기능 패널에 연결되었다. 각 기능 테이블에는 728개의 회전 노브가 있었다. 에니악을 프로그래밍하는 것은 또한 3,000개의 스위치 중 일부를 설정하는 것을 포함했다. 프로그램 디버깅은 일주일이 걸렸다. 1947년부터 1955년까지 애버딘 시험장에서 수소폭탄 매개변수 계산, 날씨 패턴 예측, 포병 조준을 위한 사격표 제작 등의 작업을 수행했다.

### 프로그램 내장식 컴퓨터
코드를 연결하고 스위치를 돌리는 대신, 프로그램 내장식 컴퓨터는 데이터를 메모리에 로드하는 것처럼 명령어를 메모리에 로드한다. 결과적으로 컴퓨터는 빠르게 프로그래밍될 수 있었고 매우 빠른 속도로 계산을 수행할 수 있었다. 프레스퍼 에커트와 존 모클리는 에니악을 만들었다. 이 두 엔지니어는 1944년 2월 3페이지 분량의 메모에서 프로그램 내장 개념을 소개했다. 나중에 1944년 9월, 존 폰 노이만은 에니악 프로젝트에 참여하기 시작했다. 1945년 6월 30일, 폰 노이만은 에드박의 보고서 최초 초안을 발표했는데, 여기서 컴퓨터의 구조를 인간 두뇌의 구조와 동일시했다. 이 설계는 폰 노이만 구조로 알려지게 되었다. 이 구조는 1949년 에드박과 에드삭 컴퓨터 제작에 동시에 적용되었다.

IBM 시스템/360 (1964)은 동일한 명령어 집합 아키텍처를 가진 컴퓨터 제품군이었다. 모델 20은 가장 작고 저렴했다. 고객들은 동일한 응용 소프트웨어를 유지하면서 업그레이드할 수 있었다. 모델 195는 가장 프리미엄 모델이었다. 각 System/360 모델은 멀티프로그래밍을 특징으로 했다. 즉, 한 번에 여러 프로세스가 메모리에 있었다. 한 프로세스가 입출력을 기다리는 동안 다른 프로세스가 계산할 수 있었다.

IBM은 각 모델이 PL/I를 사용하여 프로그래밍될 것으로 계획했다. 코볼, 포트란 및 알골 프로그래머를 포함하는 위원회가 구성되었다. 목적은 포괄적이고 사용하기 쉽고 확장 가능하며 코볼과 포트란을 대체할 언어를 개발하는 것이었다. 그 결과는 크고 복잡하며 컴파일하는 데 오랜 시간이 걸리는 언어였다.

[섬네일|1970년대 중반에 제조된 [데이터 제너럴 노바 3의 수동 입력을 위한 스위치]]
1970년대까지 제조된 컴퓨터에는 수동 프로그래밍을 위한 전면 패널 스위치가 있었다. 컴퓨터 프로그램은 참조를 위해 종이에 작성되었다. 명령어는 온/오프 설정의 구성으로 표현되었다. 구성을 설정한 후 실행 버튼을 눌렀다. 이 과정은 반복되었다. 컴퓨터 프로그램은 또한 종이 테이프, 천공 카드 또는 자기 테이프를 통해 자동으로 입력되었다. 매체가 로드된 후 스위치를 통해 시작 주소가 설정되고 실행 버튼이 눌렸다.

### 초고밀도 집적 회로
[섬네일|right|초고밀도 집적 회로 [다이 (집적 회로)|다이]]
소프트웨어 개발의 주요 이정표는 초고밀도 집적 회로(VLSI, 1964)의 발명이었다.

페어차일드 반도체 (1957) 및 인텔 (1968)의 공동 창립자인 로버트 노이스는 장효과 트랜지스터의 생산을 개선하기 위한 기술적 개선을 달성했다 (1963). 목표는 반도체 접합의 전기 저항률과 전도도를 변경하는 것이다. 먼저, 자연 발생 규산염 광물은 지멘스 공정을 사용하여 다결정 실리콘 봉으로 전환된다. 그 다음 초크랄스키 공정은 봉을 단결정 실리콘, 불 결정으로 전환한다. 이 결정은 얇게 슬라이스되어 웨이퍼, 기판을 형성한다. 평면 공정의 포토리소그래피는 단극 트랜지스터, 축전기, 다이오드, 저항기를 웨이퍼에 통합하여 금속 산화물 반도체 (MOS) 트랜지스터 매트릭스를 구축한다. MOS 트랜지스터는 집적 회로 칩의 주요 구성 요소이다.

원래, 집적 회로 칩은 제조 중에 기능이 설정되었다. 1960년대 동안, 전기 흐름 제어는 매트릭스 형태의 고정 기억 장치 (ROM)를 프로그래밍하는 것으로 바뀌었다. 이 매트릭스는 2차원 퓨즈 배열과 유사했다. 명령어를 매트릭스에 내장하는 과정은 불필요한 연결을 태워 없애는 것이었다. 연결이 너무 많아서, 펌웨어 프로그래머들은 다른 칩에 컴퓨터 프로그램을 작성하여 소각 과정을 감독했다. 이 기술은 PROM으로 알려지게 되었다. 1971년, 인텔은 컴퓨터 프로그램을 칩에 설치하고 이를 인텔 4004 마이크로프로세서라고 명명했다.

섬네일|right|IBM의 System/360 (1964) CPU는 마이크로프로세서가 아니었다.
마이크로프로세서와 중앙 처리 장치 (CPU)라는 용어는 이제 서로 바꿔 사용할 수 있다. 그러나 CPU는 마이크로프로세서보다 먼저 존재했다. 예를 들어, IBM 시스템/360 (1964)에는 세라믹 기판 위에 개별 부품이 포함된 회로 보드로 만들어진 CPU가 있었다.

### X86 시리즈
[섬네일|right|최초의 [IBM PC (1981)는 인텔 8088 마이크로프로세서를 사용했다.]]
1978년, 인텔이 인텔 8080을 인텔 8086으로 업그레이드하면서 현대적인 소프트웨어 개발 환경이 시작되었다. 인텔은 더 저렴한 인텔 8088을 제조하기 위해 인텔 8086을 단순화했다. IBM은 개인용 컴퓨터 시장에 진출할 때 (1981) 인텔 8088을 채택했다. 소비자의 개인용 컴퓨터 수요가 증가함에 따라 인텔의 마이크로프로세서 개발도 증가했다. 이 개발의 연속은 x86 시리즈로 알려져 있다. X86 어셈블리어는 하위 호환성을 가진 기계어 명령어 제품군이다. 이전 마이크로프로세서에서 생성된 기계어 명령어는 마이크로프로세서 업그레이드 내내 유지되었다. 이를 통해 소비자는 새로운 응용 소프트웨어를 구매할 필요 없이 새로운 컴퓨터를 구매할 수 있었다. 주요 명령어 범주는 다음과 같다.
* 랜덤 액세스 메모리에 숫자와 문자열을 설정하고 접근하는 메모리 명령어.
* 정수에 대한 기본 산술 연산을 수행하는 정수 산술 논리 장치 (ALU) 명령어.
* 실수에 대한 기본 산술 연산을 수행하는 부동 소수점 ALU 명령어.
* 메모리를 할당하고 함수와 인터페이스하는 데 필요한 워드를 푸시하고 팝하는 콜 스택 명령어.
* 여러 프로세서가 데이터 배열에 대해 동일한 알고리즘을 수행할 수 있을 때 속도를 높이는 SIMD 명령어

### 변화하는 프로그래밍 환경
[섬네일|right|[디지털 이큅먼트 코퍼레이션|DEC의 VT100 (1978)은 널리 사용되는 단말기였다.]]
VLSI 회로는 프로그래밍 환경을 단말기 (1990년대까지)에서 그래픽 사용자 인터페이스 (GUI) 컴퓨터로 발전시켰다. 컴퓨터 단말기는 프로그래머를 명령줄 환경에서 실행되는 단일 셸로 제한했다. 1970년대 동안, 텍스트 사용자 인터페이스를 통해 전체 화면 소스 코드 편집이 가능해졌다. 사용 가능한 기술에 관계없이, 목표는 프로그래밍 언어로 프로그래밍하는 것이다.

## 프로그래밍 패러다임과 언어

프로그래밍 언어 기능은 프로그래밍 이상을 표현하기 위해 결합될 수 있는 빌딩 블록을 제공하기 위해 존재한다. 이상적으로, 프로그래밍 언어는 다음을 수행해야 한다.
* 코드에서 아이디어를 직접 표현한다.
* 독립적인 아이디어를 독립적으로 표현한다.
* 코드에서 아이디어 간의 관계를 직접 표현한다.
* 아이디어를 자유롭게 결합한다.
* 아이디어의 결합이 의미가 있는 경우에만 결합한다.
* 간단한 아이디어를 간단하게 표현한다.

이러한 빌딩 블록을 제공하는 프로그래밍 언어의 프로그래밍 스타일은 프로그래밍 패러다임으로 분류될 수 있다. 예를 들어, 다른 패러다임은 다음을 구별할 수 있다.
* 절차적 언어, 함수형 언어, 논리형 언어.
* 다양한 수준의 추상 자료형.
* 다양한 수준의 클래스 계층.
* 컨테이너 유형 및 제네릭 프로그래밍과 같은 다양한 수준의 입력 자료형.
이러한 각 프로그래밍 스타일은 다양한 프로그래밍 언어의 합성에 기여했다.

프로그래밍 언어는 프로그래머가 컴퓨터에 명령어를 전달할 수 있도록 하는 예약어, 기호, 식별자, 그리고 규칙들의 집합이다. 이들은 구문이라는 규칙 집합을 따른다.

* 키워드는 선언과 문을 형성하기 위한 예약어이다.
* 기호는 연산, 대입, 제어 흐름, 그리고 구분 문자를 형성하기 위한 문자이다.
* 식별자는 프로그래머가 상수, 변수 이름, 구조 이름, 그리고 함수 이름을 형성하기 위해 만든 단어이다.
* 구문 규칙은 배커스-나우르 표기법으로 정의된다.

프로그래밍 언어는 형식 언어를 기반으로 한다. 형식 언어의 관점에서 해법을 정의하는 목적은 근본적인 문제를 해결하기 위한 알고리즘을 생성하는 것이다. 알고리즘은 문제를 해결하는 간단한 명령어들의 수열이다.

### 프로그래밍 언어의 세대

[섬네일|[W65C816S 마이크로프로세서의 기계어 모니터]]
프로그래밍 언어의 진화는 에드삭 (1949)이 폰 노이만 구조에 최초의 프로그램 내장 컴퓨터 프로그램을 사용하면서 시작되었다. 에드삭 프로그래밍은 1세대 프로그래밍 언어에 속했다.

* 1세대 프로그래밍 언어는 기계어이다. 기계어는 프로그래머가 기계 코드라고 불리는 명령어 숫자를 사용하여 명령어를 입력하도록 요구한다. 예를 들어, PDP-11의 ADD 연산은 명령어 숫자 24576을 갖는다.

* 2세대 프로그래밍 언어는 어셈블리어이다. 어셈블리어는 프로그래머가 명령어 숫자를 기억하는 대신 니모닉 명령어를 사용할 수 있도록 한다. 어셈블러는 각 어셈블리어 니모닉을 해당 기계어 숫자로 번역한다. 예를 들어, PDP-11에서 연산 24576은 소스 코드에서 ADD R0,R0으로 참조될 수 있다. 네 가지 기본 산술 연산에는 ADD, SUB, MUL, DIV와 같은 어셈블리 명령어가 있다. 컴퓨터는 또한 메모리 셀을 예약하기 위해 DW (워드 정의)와 같은 명령어도 가지고 있다. 그런 다음 MOV 명령어는 정수를 레지스터와 메모리 사이에 복사할 수 있다.

:* 어셈블리어 문의 기본 구조는 레이블, 연산, 피연산자, 그리고 주석이다.
::* 레이블은 프로그래머가 변수 이름을 사용하여 작업할 수 있도록 한다. 어셈블러는 나중에 레이블을 물리적 메모리 주소로 번역한다.
::* 연산은 프로그래머가 니모닉을 사용하여 작업할 수 있도록 한다. 어셈블러는 나중에 니모닉을 명령어 숫자로 번역한다.
::* 피연산자는 어셈블러에게 연산이 처리할 데이터를 알려준다.
::* 주석은 명령어만으로는 모호하기 때문에 프로그래머가 설명을 작성할 수 있도록 한다.
:: 어셈블리어 프로그램의 핵심 특징은 해당 기계어 대상에 대한 일대일 매핑을 형성한다는 것이다.

* 3세대 프로그래밍 언어는 컴파일러와 인터프리터를 사용하여 컴퓨터 프로그램을 실행한다. 3세대 언어의 특징은 특정 하드웨어로부터의 독립성이다. 초기 언어에는 포트란 (1958), 코볼 (1959), 알골 (1960), 베이직 (1964)이 포함된다. 1973년, C 프로그래밍 언어는 효율적인 기계어 명령어를 생성하는 고급 프로그래밍 언어로 등장했다. 3세대 언어는 역사적으로 각 문에 대해 많은 기계어 명령어를 생성했지만, C는 단일 기계어 명령어를 생성할 수 있는 문을 가지고 있다. 더욱이, 최적화 컴파일러는 프로그래머의 의도를 무시하고 문보다 적은 기계어 명령어를 생성할 수도 있다. 오늘날, 전체 프로그래밍 패러다임의 언어들이 명령형 프로그래밍, 3세대 스펙트럼을 채운다.

* 4세대 프로그래밍 언어는 프로그래밍 문이 어떻게 구성되어야 하는지보다는 어떤 출력 결과가 원하는지를 강조한다. 선언형 언어는 부작용을 제한하고 프로그래머가 비교적 적은 오류로 코드를 작성할 수 있도록 한다. 인기 있는 4세대 언어 중 하나는 구조화 질의 언어 (SQL)이다. 데이터베이스 개발자들은 더 이상 각 데이터베이스 레코드를 한 번에 하나씩 처리할 필요가 없다. 또한, 간단한 선택문은 검색 방법을 이해할 필요 없이 출력 레코드를 생성할 수 있다.

### 명령형 언어

섬네일|명령형 언어로 작성된 컴퓨터 프로그램
명령형 언어는 선언, 표현식, 그리고 문을 사용하여 순차적인 알고리즘을 지정한다.
* 선언은 컴퓨터 프로그램에 변수 이름을 도입하고 자료형에 할당한다. – 예: `var x: integer;`
* 표현식은 값을 산출한다 – 예: `2 + 2`는 4를 산출한다.
* 문은 표현식을 변수에 할당하거나 변수의 값을 사용하여 프로그램의 제어 흐름을 변경할 수 있다 – 예: `x := 2 + 2; [[조건문#If-then(-else)|if]] x = 4 then do_something();`

#### 포트란
포트란 (1958)은 "IBM Mathematical FORmula TRANslating system"으로 공개되었다. 이것은 문자열 처리 기능 없이 과학적 계산을 위해 설계되었다. 선언, 표현식, 그리고 문과 함께 다음을 지원했다.
* 배열.
* 서브루틴.
* "do" 루프.

이것이 성공한 이유는 다음과 같다.
* 프로그래밍 및 디버깅 비용이 컴퓨터 실행 비용보다 낮았다.
* IBM의 지원을 받았다.
* 당시의 응용 분야는 과학적이었다.

그러나 비IBM 공급업체들도 포트란 컴파일러를 작성했지만, IBM의 컴파일러에서는 실패할 가능성이 있는 구문을 사용했다. 미국 국가표준 협회 (ANSI)는 1966년에 첫 번째 포트란 표준을 개발했다. 1978년, 포트란 77은 1991년까지 표준으로 사용되었다. 포트란 90은 다음을 지원한다.
* 레코드.
* 배열에 대한 포인터.

#### 코볼
코볼 (1959)은 "COmmon Business Oriented Language"의 약자이다. 포트란은 기호를 조작했다. 곧 기호가 숫자가 될 필요가 없다는 것이 인식되어 문자열이 도입되었다. 미국 국방부는 코볼 개발에 영향을 미쳤으며, 그레이스 호퍼가 주요 기여자였다. 문은 영어와 같고 장황했다. 목표는 관리자들이 프로그램을 읽을 수 있도록 언어를 설계하는 것이었다. 그러나 구조화된 문이 부족하여 이 목표가 방해받았다.

코볼의 개발은 엄격하게 통제되었으므로, ANSI 표준을 요구하는 방언이 나타나지 않았다. 결과적으로 1974년까지 15년 동안 변경되지 않았다. 1990년대 버전은 객체 지향 프로그래밍과 같은 중요한 변경 사항을 적용했다.

#### 알골
ALGOL (1960)은 "ALGOrithmic Language"의 약자이다. 이는 프로그래밍 언어 설계에 지대한 영향을 미쳤다. 유럽과 미국의 프로그래밍 언어 전문가 위원회에서 탄생했으며, 표준 수학적 표기법을 사용하고 읽기 쉽고 구조화된 설계를 가졌다. 알골은 배커스-나우르 표기법을 사용하여 구문을 정의한 최초의 언어였다. 이는 구문 지향 컴파일러로 이어졌다. 다음 기능이 추가되었다.
* 블록 구조, 여기서 변수는 해당 블록에 지역적이었다.
* 가변 경계를 가진 배열.
* "for" 루프.
* 함수.
* 재귀.

알골의 직접적인 후손으로는 한 가지 갈래로 파스칼, 모듈라-2, 에이다, 델파이, 오베론이 있다. 다른 갈래로는 후손에 C, C++, 자바가 포함된다.

#### 베이직
베이직 (1964)은 "Beginner's All-Purpose Symbolic Instruction Code"의 약자이다. 모든 학생들이 배울 수 있도록 다트머스 대학교에서 개발되었다. 만약 학생이 더 강력한 언어로 나아가지 않더라도, 그 학생은 베이직을 기억할 것이다. 1970년대 후반에 제조된 마이크로컴퓨터에는 베이직 인터프리터가 설치되었다. 마이크로컴퓨터 산업이 성장하면서 언어도 성장했다.

베이직은 대화형 세션을 개척했다. 환경 내에서 운영체제 명령을 제공했다.
* 'new' 명령은 빈 공간을 생성했다.
* 문은 즉시 평가되었다.
* 문은 줄 번호 앞에 붙여 프로그래밍할 수 있었다.
* 'list' 명령은 프로그램을 표시했다.
* 'run' 명령은 프로그램을 실행했다.

그러나 베이직 구문은 대규모 프로그램에는 너무 간단했다. 최근 방언들은 구조와 객체 지향 확장 기능을 추가했다. 마이크로소프트의 비주얼 베이직은 여전히 널리 사용되며 그래픽 사용자 인터페이스를 생성한다.

#### C
C 프로그래밍 언어 (1973)는 BCPL 언어가 B로 대체되었고, AT&T 벨 연구소가 다음 버전을 "C"라고 불렀기 때문에 그 이름을 얻었다. 그 목적은 유닉스 운영체제를 작성하는 것이었다. C는 비교적 작은 언어로, 컴파일러를 작성하기 쉽다. C의 성장은 1980년대 하드웨어 성장과 일치했다. C의 성장은 또한 어셈블리어와 같은 기능을 가지고 있지만, 고급 구문을 사용하기 때문이기도 하다. 다음 고급 기능을 추가했다.
* 인라인 어셈블러
* 포인터 연산
* 함수 포인터
* 비트 연산
* 복잡한 연산자 자유롭게 결합

섬네일|right|컴퓨터 메모리 맵
C는 프로그래머가 데이터가 저장될 메모리 영역을 제어할 수 있도록 한다. 전역 변수와 정적 변수는 저장하는 데 가장 적은 클럭 사이클을 필요로 한다. 스택은 표준 변수 선언에 자동으로 사용된다. 힙 메모리는 <code>malloc()</code> 함수에서 포인터 변수로 반환된다.

* 전역 및 정적 데이터 영역은 프로그램 영역 바로 위에 위치한다. (프로그램 영역은 기술적으로 텍스트 영역이라고 불린다. 기계어 명령어가 저장되는 곳이다.)
:* 전역 및 정적 데이터 영역은 기술적으로 두 개의 영역이다. 한 영역은 기본값으로 선언된 변수가 저장되는 초기화된 데이터 세그먼트라고 불린다. 다른 영역은 기본값 없이 선언된 변수가 저장되는 세그먼트에 의해 시작된 블록이라고 불린다.
:* 전역 및 정적 데이터 영역에 저장된 변수는 컴파일 시간에 주소가 설정된다. 이들은 프로세스 수명 동안 값을 유지한다.

:* 전역 및 정적 영역은 `main()` 함수 상단 (외부)에 선언된 전역 변수를 저장한다. 전역 변수는 `main()`과 소스 코드의 다른 모든 함수에 대해 볼 수 있다.

: 반면에, `main()` 내부, 다른 함수 내부, 또는 `{` `}` 블록 구분자 내부에 선언된 변수는 지역 변수이다. 지역 변수에는 형식 매개변수 변수도 포함된다. 매개변수 변수는 함수 정의의 괄호 안에 둘러싸여 있다. 매개변수는 함수에 대한 인터페이스를 제공한다.

:* `static` 접두사 없이 선언된 지역 변수는 형식 매개변수 변수를 포함하여 자동 변수라고 불리며 스택에 저장된다. 이들은 함수 또는 블록 내에서만 볼 수 있으며, 함수 또는 블록을 종료하면 스코프를 잃는다.

* 스택 영역은 최상위 메모리 주소 근처에 위치한 연속적인 메모리 블록이다. 스택에 배치된 변수는 위에서 아래로 채워진다. 스택 포인터는 마지막으로 채워진 메모리 주소를 추적하는 특수 목적의 레지스터이다. 변수는 어셈블리어 PUSH 명령어를 통해 스택에 배치된다. 따라서 이 변수들의 주소는 런타임 중에 설정된다. 스택 변수가 스코프를 잃는 방법은 POP 명령어를 통해서이다.

:* `static` 접두사 없이 선언된 지역 변수는 형식 매개변수 변수를 포함하여 자동 변수라고 불리며 스택에 저장된다. 이들은 함수 또는 블록 내에서만 볼 수 있으며, 함수 또는 블록을 종료하면 스코프를 잃는다.

* 힙 영역은 스택 아래에 위치한다. 아래에서 위로 채워진다. 운영체제는 힙 포인터와 할당된 메모리 블록 목록을 사용하여 힙을 관리한다. 스택과 마찬가지로 힙 변수의 주소는 런타임 중에 설정된다. 힙 포인터와 스택 포인터가 만날 때 메모리 부족 오류가 발생한다.

:* C는 힙 메모리를 할당하기 위해 `malloc()` 라이브러리 함수를 제공한다. 힙에 데이터를 채우는 것은 추가적인 복사 함수이다. 힙에 저장된 변수는 포인터를 사용하여 함수에 경제적으로 전달된다. 포인터가 없으면 전체 데이터 블록이 스택을 통해 함수에 전달되어야 한다.

#### C++
1970년대, 소프트웨어 엔지니어들은 대규모 프로젝트를 모듈로 분해하기 위한 언어 지원이 필요했다. 한 가지 분명한 특징은 대규모 프로젝트를 물리적으로 별도의 컴퓨터 파일로 분해하는 것이었다. 덜 분명한 특징은 대규모 프로젝트를 논리적으로 추상 자료형으로 분해하는 것이었다. 당시 언어들은 구체적인 (스칼라) 자료형인 정수, 부동소수점 수, 문자 문자열을 지원했다. 추상 자료형은 새로운 이름이 할당된 구체적인 자료형의 구조이다. 예를 들어, 정수 목록은 `integer_list`라고 불릴 수 있다.

객체 지향 용어에서 추상 자료형은 클래스라고 불린다. 그러나 클래스는 정의일 뿐이며, 메모리는 할당되지 않는다. 클래스에 메모리가 할당되고 식별자에 바인딩되면 객체라고 불린다.

객체 지향 명령형 언어는 클래스에 대한 필요성과 안전한 함수형 프로그래밍에 대한 필요성을 결합하여 개발되었다. 객체 지향 언어에서 함수는 클래스에 할당된다. 할당된 함수는 메서드, 멤버 함수, 또는 연산이라고 불린다. 객체 지향 프로그래밍은 객체에 대한 연산을 실행하는 것이다.

객체 지향 언어는 부분집합/상위집합 관계를 모델링하는 구문을 지원한다. 집합론에서 부분집합의 원소는 상위집합에 포함된 모든 속성을 상속한다. 예를 들어, 학생은 사람이다. 따라서 학생들의 집합은 사람들의 집합의 부분집합이다. 결과적으로 학생들은 모든 사람에게 공통된 모든 속성을 상속한다. 또한 학생들은 다른 사람들이 가지지 않는 고유한 속성을 가진다. 객체 지향 언어는 상속을 사용하여 부분집합/상위집합 관계를 모델링한다. 객체 지향 프로그래밍은 1990년대 후반에 지배적인 언어 패러다임이 되었다.

C++ (1985)는 원래 "C with Classes"라고 불렸다. 이것은 언어 시뮬라의 객체 지향 기능을 추가하여 C의 기능을 확장하도록 설계되었다.

객체 지향 모듈은 두 개의 파일로 구성된다. 정의 파일은 헤더 파일이라고 불린다. 다음은 간단한 학교 응용 프로그램의 GRADE 클래스에 대한 C++ 헤더 파일이다.


```cpp
// grade.h
// -------

// Used to allow multiple source files to include
// this header file without duplication errors.
// ----------------------------------------------
#ifndef GRADE_H
#define GRADE_H

class GRADE {
public:
    // This is the constructor operation.
    // ----------------------------------
    GRADE ( const char letter );

    // This is a class variable.
    // -------------------------
    char letter;

    // This is a member operation.
    // ---------------------------
    int grade_numeric( const char letter );

    // This is a class variable.
    // -------------------------
    int numeric;
};
#endif
```


생성자 연산은 클래스 이름과 동일한 이름을 가진 함수이다. 이는 호출 연산이 `[[New와 delete (C++)|new]]` 문을 실행할 때 실행된다.

모듈의 다른 파일은 소스 파일이다. 다음은 간단한 학교 응용 프로그램의 GRADE 클래스에 대한 C++ 소스 파일이다.


```cpp
// grade.cpp
// ---------
#include "grade.h"

GRADE::GRADE( const char letter )
{
    // Reference the object using the keyword 'this'.
    // ----------------------------------------------
    this->letter = letter;

    // This is Temporal Cohesion
    // -------------------------
    this->numeric = grade_numeric( letter );
}

int GRADE::grade_numeric( const char letter )
{
    if ( ( letter == 'A' || letter == 'a' ) )
        return 4;
    else
    if ( ( letter == 'B' || letter == 'b' ) )
        return 3;
    else
    if ( ( letter == 'C' || letter == 'c' ) )
        return 2;
    else
    if ( ( letter == 'D' || letter == 'd' ) )
        return 1;
    else
    if ( ( letter == 'F' || letter == 'f' ) )
        return 0;
    else
        return -1;
}
```


다음은 간단한 학교 응용 프로그램의 PERSON 클래스에 대한 C++ 헤더 파일이다.


```cpp
// person.h
// --------
#ifndef PERSON_H
#define PERSON_H

class PERSON {
public:
    PERSON ( const char *name );
    const char *name;
};
#endif
```


다음은 간단한 학교 응용 프로그램의 PERSON 클래스에 대한 C++ 소스 파일이다.


```cpp
// person.cpp
// ----------
#include "person.h"

PERSON::PERSON ( const char *name )
{
    this->name = name;
}
```


다음은 간단한 학교 응용 프로그램의 STUDENT 클래스에 대한 C++ 헤더 파일이다.


```cpp
// student.h
// ---------
#ifndef STUDENT_H
#define STUDENT_H

#include "person.h"
#include "grade.h"

// A STUDENT is a subset of PERSON.
// --------------------------------
class STUDENT : public PERSON{
public:
    STUDENT ( const char *name );
    GRADE *grade;
};
#endif
```


다음은 간단한 학교 응용 프로그램의 STUDENT 클래스에 대한 C++ 소스 파일이다.


```cpp
// student.cpp
// -----------
#include "student.h"
#include "person.h"

STUDENT::STUDENT ( const char *name ):
    // Execute the constructor of the PERSON superclass.
    // -------------------------------------------------
    PERSON( name )
{
    // Nothing else to do.
    // -------------------
}
```


다음은 시연을 위한 드라이버 프로그램이다.


```cpp
// student_dvr.cpp
// ---------------
#include <iostream>
#include "student.h"

int main( void )
{
    STUDENT *student = new STUDENT( "The Student" );
    student->grade = new GRADE( 'a' );

    std::cout
        // Notice student inherits PERSON's name
        << student->name
        << ": Numeric grade = "
        << student->grade->numeric
        << "\n";
	return 0;
}
```


다음은 모든 것을 컴파일하는 메이크파일이다.


```make
# makefile
# --------
all: student_dvr

clean:
    rm student_dvr *.o

student_dvr: student_dvr.cpp grade.o student.o person.o
    c++ student_dvr.cpp grade.o student.o person.o -o student_dvr

grade.o: grade.cpp grade.h
    c++ -c grade.cpp

student.o: student.cpp student.h
    c++ -c student.cpp

person.o: person.cpp person.h
    c++ -c person.cpp
```


### 선언형 언어

명령형 언어에는 한 가지 주요 비판이 있다. 비지역 변수에 표현식을 할당하면 의도하지 않은 부작용이 발생할 수 있다는 것이다. 선언형 언어는 일반적으로 할당문과 제어 흐름을 생략한다. 이들은 수행되어야 할 계산을 설명하고, 계산 방법을 설명하지 않는다. 선언형 언어의 두 가지 광범위한 범주는 함수형 언어와 논리형 언어이다.

함수형 언어의 원리는 람다 대수를 잘 정의된 의미론의 지침으로 사용하는 것이다. 수학에서 함수는 표현식의 원소를 값의 범위에 매핑하는 규칙이다. 다음 함수를 고려해보자.

`times_10(x) = 10 * x`

표현식 `10 * x`는 함수 `times_10()`에 의해 값의 범위에 매핑된다. 한 값은 20이다. 이는 x가 2일 때 발생한다. 따라서 함수의 적용은 수학적으로 다음과 같이 작성된다.

`times_10(2) = 20`

함수형 언어 컴파일러는 이 값을 변수에 저장하지 않는다. 대신, 프로그램 카운터를 호출 함수로 다시 설정하기 전에 값을 컴퓨터의 스택에 푸시한다. 그런 다음 호출 함수는 스택에서 값을 팝한다.

명령형 언어는 함수를 지원한다. 따라서 프로그래머가 규율을 지킨다면 명령형 언어에서도 함수형 프로그래밍을 구현할 수 있다. 그러나 함수형 언어는 그 구문을 통해 프로그래머에게 이러한 규율을 강제한다. 함수형 언어는 '무엇'을 강조하도록 맞춤화된 구문을 가지고 있다.

함수형 프로그램은 한 세트의 기본 함수와 하나의 드라이버 함수로 개발된다. 다음 스니펫을 고려해보자.

`function max( a, b ){/* code omitted */}`

`function min( a, b ){/* code omitted */}`

`function range( a, b, c ) {`
:`return max( a, max( b, c ) ) - min( a, min( b, c ) );`
`}`

기본 함수는 `max()`와 `min()`이다. 드라이버 함수는 `range()`이다. `put( range( 10, 4, 7) );`를 실행하면 6이 출력된다.

함수형 언어는 컴퓨터 과학 연구에서 새로운 언어 기능을 탐색하는 데 사용된다. 또한, 부작용이 없기 때문에 병렬 컴퓨팅 및 동시 프로그래밍에서 인기를 얻었다. 그러나 응용 프로그램 개발자들은 명령형 언어의 객체 지향 기능을 선호한다.

#### 리스프
리스프 (1958)는 "LISt Processor"의 약자이다. 이 언어는 리스트를 처리하는 데 특화되어 있다. 데이터의 완전한 구조는 리스트들의 리스트를 구축함으로써 형성된다. 메모리에는 트리 자료 구조가 구축된다. 내부적으로 트리 구조는 재귀 함수에 잘 어울린다. 트리를 구축하는 구문은 공백으로 구분된 원소를 괄호 안에 넣는 것이다. 다음은 세 개의 원소로 구성된 리스트이다. 처음 두 원소는 그 자체로 두 개의 원소로 구성된 리스트이다.

`((A B) (HELLO WORLD) 94)`

리스프에는 원소를 추출하고 재구성하는 함수가 있다. `head()` 함수는 목록의 첫 번째 요소를 포함하는 목록을 반환한다. `tail()` 함수는 첫 번째 요소를 제외한 모든 것을 포함하는 목록을 반환한다. `cons()` 함수는 다른 목록들의 연결인 목록을 반환한다. 따라서 다음 표현식은 목록 `x`를 반환한다.

`cons(head(x), tail(x))`

리스프의 한 가지 단점은 많은 함수가 중첩될 때 괄호가 혼란스러워 보일 수 있다는 것이다. 최신 리스프 통합 개발 환경은 괄호가 일치하는지 확인하는 데 도움이 된다. 여담으로, 리스프는 할당문과 goto 루프와 같은 명령형 언어 연산을 지원한다. 또한 리스프는 컴파일 시간에 요소의 자료형에 신경 쓰지 않는다. 대신, 런타임에 자료형을 할당 (그리고 재할당)할 수 있다. 런타임에 자료형을 할당하는 것을 동적 바인딩이라고 한다. 동적 바인딩은 언어의 유연성을 증가시키지만, 프로그래밍 오류가 소프트웨어 개발 프로세스 후반까지 남아 있을 수 있다.

크고 신뢰할 수 있으며 읽기 쉬운 리스프 프로그램을 작성하려면 사전 숙고가 필요하다. 적절하게 계획하면 프로그램이 동등한 명령형 언어 프로그램보다 훨씬 짧을 수 있다. 리스프는 인공지능에 널리 사용된다. 그러나 의도하지 않은 부작용을 가능하게 하는 명령형 언어 연산이 있기 때문에 그 사용이 받아들여졌다.

#### ML
ML (1973)은 "Meta Language"의 약자이다. ML은 같은 유형의 데이터만 서로 비교되도록 확인한다. 예를 들어, 이 함수는 하나의 입력 매개변수 (정수)를 가지고 정수를 반환한다.

ML은 리스프처럼 괄호 중심적이지 않다. 다음은 `times_10()`의 적용이다.

 times_10 2

"20 : int"를 반환한다. (결과와 자료형이 모두 반환된다.)

리스프처럼 ML은 리스트를 처리하도록 맞춤화되어 있다. 리스프와 달리 각 요소는 동일한 자료형이다. 더욱이 ML은 컴파일 타임에 요소의 자료형을 할당한다. 컴파일 시간에 자료형을 할당하는 것을 정적 바인딩이라고 한다. 정적 바인딩은 컴파일러가 변수가 사용되기 전에 컨텍스트를 확인하므로 신뢰성을 높인다.

#### 프롤로그
프롤로그 (1972)는 "PROgramming in LOGic"의 약자이다. 형식 논리학을 기반으로 한 논리형 프로그래밍 언어이다. 이 언어는 프랑스 마르세유에서 알랭 콜메라우어와 필립 루셀이 개발했다. 에든버러 대학교의 로버트 코왈스키 등이 개척한 선택적 선형 정의 절 해상도의 구현이다.

프롤로그 프로그램의 빌딩 블록은 사실과 규칙이다. 간단한 예시는 다음과 같다.

```prolog
cat(tom).                        % tom은 고양이다
mouse(jerry).                    % jerry는 쥐다

animal(X) :- cat(X).             % 모든 고양이는 동물이다
animal(X) :- mouse(X).           % 모든 쥐는 동물이다

big(X)   :- cat(X).              % 모든 고양이는 크다
small(X) :- mouse(X).            % 모든 쥐는 작다

eat(X,Y) :- mouse(X), cheese(Y). % 모든 쥐는 모든 치즈를 먹는다
eat(X,Y) :- big(X),   small(Y).  % 모든 큰 동물은 모든 작은 동물을 먹는다
```


모든 사실과 규칙이 입력된 후 질문을 할 수 있다.
: 톰은 제리를 먹을까?

```prolog
?- eat(tom,jerry).
true
```


다음 예시는 프롤로그가 문자 등급을 숫자 값으로 변환하는 방법을 보여준다.

```prolog
numeric_grade('A', 4).
numeric_grade('B', 3).
numeric_grade('C', 2).
numeric_grade('D', 1).
numeric_grade('F', 0).
numeric_grade(X, -1) :- not X = 'A', not X = 'B', not X = 'C', not X = 'D', not X = 'F'.
grade('The Student', 'A').
```


```prolog
?- grade('The Student', X), numeric_grade(X, Y).
X = 'A',
Y = 4
```


다음은 포괄적인 예시이다.

1) 모든 용은 불을 내뿜는다. 또는 동등하게, 어떤 것이 용이면 불을 내뿜는다.

```prolog
billows_fire(X) :-
    is_a_dragon(X).
```

2) 어떤 생명체가 불을 내뿜는 것은 그 부모 중 한 명이 불을 내뿜는 경우이다.

```prolog
billows_fire(X) :-
    is_a_creature(X),
    is_a_parent_of(Y,X),
    billows_fire(Y).
```

3) 어떤 X는 어떤 Y의 부모이다. 만약 X가 Y의 어머니이거나 X가 Y의 아버지인 경우이다.

```prolog
is_a_parent_of(X, Y):- is_the_mother_of(X, Y).
is_a_parent_of(X, Y):- is_the_father_of(X, Y).
```


4) 어떤 것은 용이면 생명체이다.

```prolog
is_a_creature(X) :-
    is_a_dragon(X).
```


5) 노르베르타는 용이고, 퍼프는 생명체이다. 노르베르타는 퍼프의 어머니다.


```prolog
is_a_dragon(norberta).
is_a_creature(puff).
is_the_mother_of(norberta, puff).
```


규칙 (2)는 재귀적 (귀납적) 정의이다. 실행 방법을 이해할 필요 없이 선언적으로 이해할 수 있다.

규칙 (3)은 함수가 관계를 사용하여 표현되는 방법을 보여준다. 여기서 어머니와 아버지 함수는 모든 개체가 단 하나의 어머니와 단 하나의 아버지를 가지도록 보장한다.

프롤로그는 자료형이 없는 언어이다. 그럼에도 불구하고, 상속은 술어를 사용하여 표현될 수 있다. 규칙 (4)는 생명체가 용의 상위 클래스임을 주장한다.

질문은 역추론을 사용하여 답변된다. 다음 질문이 주어지면:


```prolog
?- billows_fire(X).
```

프롤로그는 두 가지 답변을 생성한다.

```prolog
X = norberta
X = puff
```


프롤로그의 실제 적용 분야는 인공지능의 지식 표현과 문제 해결이다.

### 객체 지향 프로그래밍
객체 지향 프로그래밍은 객체에 연산 (함수)을 실행하는 프로그래밍 방식이다. 기본 아이디어는 현상의 특성을 객체 컨테이너로 묶고 컨테이너에 이름을 부여하는 것이다. 현상에 대한 연산도 컨테이너에 묶인다. 객체 지향 프로그래밍은 컨테이너에 대한 필요성과 안전한 함수형 프로그래밍에 대한 필요성을 결합하여 개발되었다. 이 프로그래밍 방식은 객체 지향 언어에만 국한될 필요는 없다. 객체 지향 언어에서 객체 컨테이너는 클래스라고 불린다. 비객체 지향 언어에서 자료 구조 (또는 레코드)는 객체 컨테이너가 될 수 있다. 자료 구조를 객체 컨테이너로 바꾸려면 구조에 특화된 연산을 작성해야 한다. 결과 구조는 추상 자료형이라고 불린다. 그러나 상속은 빠지게 될 것이다. 그럼에도 불구하고, 이 단점은 극복될 수 있다.

다음은 간단한 학교 응용 프로그램에서 GRADE 추상 자료형에 대한 C 프로그래밍 언어 헤더 파일이다.


```c
/* grade.h */
/* ------- */

/* Used to allow multiple source files to include */
/* this header file without duplication errors.   */
/* ---------------------------------------------- */
#ifndef GRADE_H
#define GRADE_H

typedef struct
{
    char letter;
} GRADE;

/* Constructor */
/* ----------- */
GRADE *grade_new( char letter );

int grade_numeric( char letter );
#endif
```


`grade_new()` 함수는 C++ 생성자 연산과 동일한 알고리즘을 수행한다.

다음은 간단한 학교 응용 프로그램에서 GRADE 추상 자료형에 대한 C 프로그래밍 언어 소스 파일이다.


```c
/* grade.c */
/* ------- */
#include "grade.h"

GRADE *grade_new( char letter )
{
    GRADE *grade;

    /* Allocate heap memory */
    /* -------------------- */
    if ( ! ( grade = calloc( 1, sizeof ( GRADE ) ) ) )
    {
        fprintf(stderr,
                "ERROR in %s/%s/%d: calloc() returned empty.\n",
                __FILE__,
                __FUNCTION__,
                __LINE__ );
        exit( 1 );
    }

    grade->letter = letter;
    return grade;
}

int grade_numeric( char letter )
{
    if ( ( letter == 'A' || letter == 'a' ) )
        return 4;
    else
    if ( ( letter == 'B' || letter == 'b' ) )
        return 3;
    else
    if ( ( letter == 'C' || letter == 'c' ) )
        return 2;
    else
    if ( ( letter == 'D' || letter == 'd' ) )
        return 1;
    else
    if ( ( letter == 'F' || letter == 'f' ) )
        return 0;
    else
        return -1;
}
```


생성자에서는 `malloc()` 대신 `calloc()` 함수가 사용되는데, 이는 각 메모리 셀이 0으로 설정되기 때문이다.

다음은 간단한 학교 응용 프로그램에서 PERSON 추상 자료형에 대한 C 프로그래밍 언어 헤더 파일이다.


```cpp
/* person.h */
/* -------- */
#ifndef PERSON_H
#define PERSON_H

typedef struct
{
    char *name;
} PERSON;

/* Constructor */
/* ----------- */
PERSON *person_new( char *name );
#endif
```


다음은 간단한 학교 응용 프로그램에서 PERSON 추상 자료형에 대한 C 프로그래밍 언어 소스 파일이다.


```cpp
/* person.c */
/* -------- */
#include "person.h"

PERSON *person_new( char *name )
{
    PERSON *person;

    if ( ! ( person = calloc( 1, sizeof ( PERSON ) ) ) )
    {
        fprintf(stderr,
                "ERROR in %s/%s/%d: calloc() returned empty.\n",
                __FILE__,
                __FUNCTION__,
                __LINE__ );
        exit( 1 );
    }

    person->name = name;
    return person;
}
```


다음은 간단한 학교 응용 프로그램에서 STUDENT 추상 자료형에 대한 C 프로그래밍 언어 헤더 파일이다.


```c
/* student.h */
/* --------- */
#ifndef STUDENT_H
#define STUDENT_H

#include "person.h"
#include "grade.h"

typedef struct
{
    /* A STUDENT is a subset of PERSON. */
    /* -------------------------------- */
    PERSON *person;

    GRADE *grade;
} STUDENT;

/* Constructor */
/* ----------- */
STUDENT *student_new( char *name );
#endif
```


다음은 간단한 학교 응용 프로그램에서 STUDENT 추상 자료형에 대한 C 프로그래밍 언어 소스 파일이다.


```cpp
/* student.c */
/* --------- */
#include "student.h"
#include "person.h"

STUDENT *student_new( char *name )
{
    STUDENT *student;

    if ( ! ( student = calloc( 1, sizeof ( STUDENT ) ) ) )
    {
        fprintf(stderr,
                "ERROR in %s/%s/%d: calloc() returned empty.\n",
                __FILE__,
                __FUNCTION__,
                __LINE__ );
        exit( 1 );
    }

    /* Execute the constructor of the PERSON superclass. */
    /* ------------------------------------------------- */
    student->person = person_new( name );
    return student;
}
```


다음은 시연을 위한 드라이버 프로그램이다.


```c
/* student_dvr.c */
/* ------------- */
#include <stdio.h>
#include "student.h"

int main( void )
{
    STUDENT *student = student_new( "The Student" );
    student->grade = grade_new( 'a' );

    printf( "%s: Numeric grade = %d\n",
            /* Whereas a subset exists, inheritance does not. */
            student->person->name,
            /* Functional programming is executing functions just-in-time (JIT) */
            grade_numeric( student->grade->letter ) );

	return 0;
}
```


다음은 모든 것을 컴파일하는 메이크파일이다.


```make
# makefile
# --------
all: student_dvr

clean:
    rm student_dvr *.o

student_dvr: student_dvr.c grade.o student.o person.o
    gcc student_dvr.c grade.o student.o person.o -o student_dvr

grade.o: grade.c grade.h
    gcc -c grade.c

student.o: student.c student.h
    gcc -c student.c

person.o: person.c person.h
    gcc -c person.c
```


객체 지향 객체를 구축하는 공식적인 전략은 다음과 같다.
* 객체를 식별한다. 이들은 명사일 가능성이 높다.
* 각 객체의 속성을 식별한다. 객체를 설명하는 데 무엇이 도움이 되는가?
* 각 객체의 행동을 식별한다. 이들은 동사일 가능성이 높다.
* 객체 간의 관계를 식별한다. 이들은 동사일 가능성이 높다.

예를 들어:
* 사람은 이름으로 식별되는 인간이다.
* 성적은 문자로 식별되는 성취이다.
* 학생은 성적을 받는 사람이다.

### 구문과 의미론
300px|섬네일|right|생성 규칙은 터미널과 비터미널의 집합으로 구성된다.

컴퓨터 프로그램의 구문은 형식 문법을 형성하는 생성 규칙의 목록이다. 프로그래밍 언어의 문법은 선언, 표현식, 그리고 문을 올바르게 배치한다. 언어의 구문을 보완하는 것은 의미론이다. 의미론은 다양한 구문 구성에 부여된 의미를 설명한다. 생성 규칙이 유효하지 않은 해석을 가질 수 있기 때문에 구문 구성에는 의미론적 설명이 필요할 수 있다. 또한 다른 언어들이 동일한 구문을 가질 수 있지만, 그들의 동작은 다를 수 있다.

언어의 구문은 생성 규칙을 나열하여 공식적으로 설명된다. 자연어의 구문은 극히 복잡하지만, 영어의 부분집합은 다음과 같은 생성 규칙 목록을 가질 수 있다.
# **문장**은 **명사구** 다음에 **동사구**로 구성된다.
# **명사구**는 **관사** 다음에 **형용사** 다음에 **명사**로 구성된다.
# **동사구**는 **동사** 다음에 **명사구**로 구성된다.
# **관사**는 'the'이다.
# **형용사**는 'big' 또는
# **형용사**는 'small'이다.
# **명사**는 'cat' 또는
# **명사**는 'mouse'이다.
# **동사**는 'eats'이다.
**굵은 글씨**로 표시된 단어는 비터미널이라고 알려져 있다. '작은 따옴표'로 표시된 단어는 터미널이라고 알려져 있다.

이 생성 규칙 목록에서 일련의 대체를 사용하여 완전한 문장을 형성할 수 있다. 이 과정은 비터미널을 유효한 비터미널 또는 유효한 터미널로 대체하는 것이다. 대체 과정은 터미널만 남을 때까지 반복된다. 유효한 문장 중 하나는 다음과 같다.
* **문장**
* **명사구** **동사구**
* **관사** **형용사** **명사** **동사구**
* the **형용사** **명사** **동사구**
* the big **명사** **동사구**
* the big cat **동사구**
* the big cat **동사** **명사구**
* the big cat eats **명사구**
* the big cat eats **관사** **형용사** **명사**
* the big cat eats the **형용사** **명사**
* the big cat eats the small **명사**
* the big cat eats the small mouse

그러나 다른 조합은 유효하지 않은 문장을 초래한다.
* the small mouse eats the big cat
따라서 '먹는' 활동의 의미를 정확하게 설명하려면 의미론이 필요하다.

한 가지 생성 규칙 나열 방법은 배커스-나우르 표기법 (BNF)이라고 불린다. BNF는 언어의 구문을 설명하며, 그 자체로 구문을 가지고 있다. 이 재귀적 정의는 메타언어의 예시이다. BNF의 구문에는 다음이 포함된다.
* `::=` 비터미널이 오른쪽에 있을 때 "는 ~으로 구성된다"로 번역된다. 터미널이 오른쪽에 있을 때 "는 ~이다"로 번역된다.
* `|` "또는"으로 번역된다.
* `<`와 `>`는 **비터미널**을 감싸는 데 사용된다.

BNF를 사용하여 영어의 부분집합은 다음과 같은 생성 규칙 목록을 가질 수 있다.

```bnf
<sentence> ::= <noun-phrase><verb-phrase>
<noun-phrase> ::= <article><adjective><noun>
<verb-phrase> ::= <verb><noun-phrase>
<article> ::= the
<adjective> ::= big | small
<noun> ::= cat | mouse
<verb> ::= eats
```


BNF를 사용하여 부호 있는 정수는 다음과 같은 생성 규칙 목록을 가진다.

```bnf
<signed-integer> ::= <sign><integer>
<sign> ::= + | -
<integer> ::= <digit> | <digit><integer>
<digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
```


재귀적 생성 규칙에 주목하자.

```bnf
<integer> ::= <digit> | <digit><integer>
```

이는 무한한 수의 가능성을 허용한다. 따라서 자릿수의 제한을 설명하기 위한 의미론이 필요하다.

생성 규칙에서 선행 0의 가능성에 주목하자.

```bnf
<integer> ::= <digit> | <digit><integer>
<digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
```

따라서 선행 0을 무시해야 함을 설명하는 의미론이 필요하다.

의미론을 설명하는 데 사용할 수 있는 두 가지 공식적인 방법은 표시적 의미론과 공리적 의미론이다.

## 소프트웨어 공학과 컴퓨터 프로그래밍
[섬네일|right|프로그래밍 언어가 있기 전, [진 바틱과 프랜 빌라스는 케이블을 옮기고 스위치를 설정하여 에니악을 프로그래밍했다.]]

소프트웨어 공학은 품질 컴퓨터 프로그램을 생산하기 위한 다양한 기술이다. 컴퓨터 프로그래밍은 소스 코드를 작성하거나 편집하는 과정이다. 공식적인 환경에서 시스템 분석가는 자동화할 조직의 모든 프로세스에 대한 정보를 관리자로부터 수집한다. 이 전문가는 새로운 시스템 또는 수정된 시스템에 대한 상세 계획을 준비한다. 이 계획은 건축가의 청사진과 유사하다.

### 성능 목표
시스템 분석가는 올바른 정보를 올바른 사람에게 올바른 시간에 전달하는 것을 목표로 한다. 이 목표를 달성하기 위한 중요한 요소는 다음과 같다.
# 출력의 품질. 출력이 의사 결정에 유용한가?
# 출력의 정확성. 실제 상황을 반영하는가?
# 출력의 형식. 출력이 쉽게 이해되는가?
# 출력의 속도. 실시간으로 고객과 소통할 때 시간에 민감한 정보는 중요하다.

### 비용 목표
성능 목표 달성은 다음을 포함한 모든 비용과 균형을 이루어야 한다.
# 개발 비용.
# 고유성 비용. 재사용 가능한 시스템은 비쌀 수 있지만, 제한된 용도의 시스템보다 선호될 수 있다.
# 하드웨어 비용.
# 운영 비용.

시스템 개발 프로세스를 적용하면 프로세스 후반에 오류가 감지될수록 수정 비용이 더 많이 든다는 격언을 완화할 수 있다.

### 폭포수 모델
폭포수 모델은 시스템 개발 프로세스의 구현이다. 폭포수라는 이름이 암시하듯이, 기본 단계는 서로 겹친다.
# 조사 단계는 근본적인 문제를 이해하는 것이다.
# 분석 단계는 가능한 해결책을 이해하는 것이다.
# 설계 단계는 최상의 해결책을 계획하는 것이다.
# 구현 단계는 최상의 해결책을 프로그래밍하는 것이다.
# 유지보수 단계는 시스템 수명 내내 지속된다. 시스템 배포 후 시스템 변경이 필요할 수 있다. 사양 오류, 설계 오류 또는 코딩 오류를 포함한 결함이 존재할 수 있다. 개선이 필요할 수 있다. 변화하는 환경에 반응하기 위해 적응이 필요할 수 있다.

### 컴퓨터 프로그래머
프로그래머는 상세 계획을 구현하기 위해 소스 코드를 작성하거나 수정하는 전문가이다. 대부분의 시스템은 단일 프로그래머가 완료하기에는 너무 크기 때문에 프로그래밍 팀이 필요할 가능성이 높다. 그러나 프로젝트에 프로그래머를 추가하는 것이 완료 시간을 단축시키지 않을 수도 있다. 대신, 시스템의 품질을 낮출 수도 있다. 효과적이려면 프로그램 모듈을 정의하고 팀원들에게 배포해야 한다. 또한 팀원들은 의미 있고 효과적인 방식으로 서로 상호작용해야 한다.

컴퓨터 프로그래머는 단일 모듈 내에서 프로그래밍하는 작게 프로그래밍할 수 있다. 모듈은 다른 소스 코드 파일에 있는 모듈을 실행할 가능성이 높다. 따라서 컴퓨터 프로그래머는 모듈들이 서로 효과적으로 연결되도록 프로그래밍하는 크게 프로그래밍할 수 있다. 크게 프로그래밍하는 것은 응용 프로그래밍 인터페이스 (API)에 기여하는 것을 포함한다.

### 프로그램 모듈
모듈형 프로그래밍은 명령형 언어 프로그램을 개선하는 기술이다. 개선된 프로그램은 소프트웨어 크기를 줄이고, 책임을 분리하며, 따라서 소프트웨어 노화를 완화할 수 있다. 프로그램 모듈은 블록 내에 경계가 정해져 있고 함께 이름으로 식별되는 일련의 문들이다. 모듈은 기능, 컨텍스트, 논리를 가진다.

* 모듈의 기능은 무엇을 하는가이다.
* 모듈의 컨텍스트는 수행되는 요소들이다.
* 모듈의 논리는 기능을 어떻게 수행하는가이다.

모듈의 이름은 먼저 기능에서, 그 다음 컨텍스트에서 파생되어야 한다. 논리는 이름의 일부가 되어서는 안 된다. 예를 들어, `function compute_square_root( x )` 또는 `function compute_square_root_integer( i : integer )`는 적절한 모듈 이름이다. 그러나 `function compute_square_root_by_division( x )`는 적절하지 않다.

모듈 내 상호 작용의 정도는 응집도 수준이다. 응집도는 모듈 이름과 기능 간의 관계에 대한 판단이다. 모듈 간 상호 작용의 정도는 결합도 수준이다. 결합도는 모듈의 컨텍스트와 수행되는 요소 간의 관계에 대한 판단이다.

### 응집도
최악에서 최상까지의 응집도 수준은 다음과 같다.

* 우연적 응집 (Coincidental Cohesion): 모듈이 여러 기능을 수행하고, 그 기능들이 완전히 관련이 없는 경우 우연적 응집을 가진다. 예를 들어, `function read_sales_record_print_next_line_convert_to_float()`. 우연적 응집은 경영진이 "모든 모듈은 35개에서 50개의 실행 가능한 문을 가져야 한다"와 같은 어리석은 규칙을 강제할 경우 실제로 발생한다.
* 논리적 응집 (Logical Cohesion): 모듈이 일련의 사용 가능한 함수를 가지고 있지만, 그 중 하나만 실행되는 경우 논리적 응집을 가진다. 예를 들어, `function perform_arithmetic( perform_addition, a, b )`.
* 시간적 응집 (Temporal Cohesion): 모듈이 시간과 관련된 기능을 수행하는 경우 시간적 응집을 가진다. 한 가지 예시는 `function initialize_variables_and_open_files()`이다. 다른 예시는 `stage_one()`, `stage_two()`, ...이다.
* 절차적 응집 (Procedural Cohesion): 모듈이 여러 개의 느슨하게 관련된 기능을 수행하는 경우 절차적 응집을 가진다. 예를 들어, `function read_part_number_update_employee_record()`.
* 통신적 응집 (Communicational Cohesion): 모듈이 여러 개의 밀접하게 관련된 기능을 수행하는 경우 통신적 응집을 가진다. 예를 들어, `function read_part_number_update_sales_record()`.
* 정보적 응집 (Informational Cohesion): 모듈이 여러 기능을 수행하지만, 각 기능은 자체 진입 및 종료 지점을 가진다. 또한, 기능들은 동일한 자료 구조를 공유한다. 객체 지향 클래스는 이 수준에서 작동한다.
* 기능적 응집 (Functional Cohesion): 모듈이 지역 변수만 사용하여 단일 목표를 달성하는 경우 기능적 응집을 가진다. 또한, 다른 컨텍스트에서 재사용될 수 있다.

### 결합도
최악에서 최상까지의 결합도 수준은 다음과 같다.

* 내용 결합 (Content Coupling): 모듈이 다른 함수의 지역 변수를 수정하는 경우 내용 결합을 가진다. 코볼은 'alter' 동사를 사용하여 이렇게 했다.
* 공통 결합 (Common Coupling): 모듈이 전역 변수를 수정하는 경우 공통 결합을 가진다.
* 제어 결합 (Control Coupling): 모듈이 다른 모듈의 제어 흐름을 수정할 수 있는 경우 제어 결합을 가진다. 예를 들어, `perform_arithmetic( perform_addition, a, b )`. 대신, 제어는 반환된 객체의 구성에 있어야 한다.
* 스탬프 결합 (Stamp Coupling): 매개변수로 전달된 자료 구조의 요소가 수정되는 경우 스탬프 결합을 가진다. 객체 지향 클래스는 이 수준에서 작동한다.
* 데이터 결합 (Data Coupling): 모듈이 모든 입력 매개변수를 필요로 하며 그 중 어느 것도 수정되지 않는 경우 데이터 결합을 가진다. 또한, 함수의 결과는 단일 객체로 반환된다.

### 데이터 흐름 분석
섬네일|샘플 함수 수준 데이터 흐름도
데이터 흐름 분석은 기능적 응집과 데이터 결합을 가진 모듈을 달성하는 데 사용되는 설계 방법이다. 이 방법의 입력은 데이터 흐름도이다. 데이터 흐름도는 모듈을 나타내는 일련의 타원형이다. 각 모듈의 이름은 타원형 내부에 표시된다. 모듈은 실행 가능한 수준 또는 함수 수준일 수 있다.

다이어그램에는 모듈들을 서로 연결하는 화살표도 있다. 모듈로 향하는 화살표는 입력 세트를 나타낸다. 각 모듈은 단일 출력 객체를 나타내기 위해 단 하나의 화살표만 외부로 향해야 한다. (선택적으로 추가 예외 화살표가 외부로 향한다.) 타원형의 데이지 체인은 전체 알고리즘을 전달한다. 입력 모듈이 다이어그램을 시작해야 한다. 입력 모듈은 변환 모듈에 연결되어야 한다. 변환 모듈은 출력 모듈에 연결되어야 한다.

## 기능별 분류
[섬네일|upright|[사용자 (컴퓨팅)|사용자가 응용 소프트웨어와 상호 작용함을 보여주는 다이어그램이다. 응용 소프트웨어는 운영체제와 상호 작용하며, 운영체제는 컴퓨터 하드웨어와 상호 작용한다.]]

컴퓨터 프로그램은 기능적 분류에 따라 나눌 수 있다. 주요 기능적 분류는 응용 소프트웨어와 시스템 소프트웨어이다. 시스템 소프트웨어에는 컴퓨터 하드웨어와 응용 소프트웨어를 연결하는 운영체제가 포함된다. 운영체제의 목적은 응용 소프트웨어가 편리하고 효율적인 방식으로 실행되는 환경을 제공하는 것이다. 응용 소프트웨어와 시스템 소프트웨어 모두 유틸리티 프로그램을 실행한다. 하드웨어 수준에서는 마이크로코드 프로그램이 중앙 처리 장치 전체의 회로를 제어한다.

### 응용 소프트웨어

응용 소프트웨어는 컴퓨터 시스템의 잠재력을 해제하는 열쇠이다. 전사적 응용 소프트웨어는 회계, 인사, 고객, 공급업체 응용 프로그램을 묶는다. 예를 들어, 전사적 자원 관리, 고객 관계 관리, 공급망 관리 소프트웨어가 있다.

기업 응용 프로그램은 사내에서 독특한 사유 소프트웨어로 개발될 수 있다. 또는 상용 소프트웨어로 구매할 수 있다. 구매한 소프트웨어는 맞춤 소프트웨어를 제공하기 위해 수정될 수 있다. 응용 프로그램이 사용자 정의되는 경우 회사 자원을 사용하거나 자원을 외주한다. 외주 소프트웨어 개발은 원래 소프트웨어 공급업체 또는 제3자 개발자로부터 이루어질 수 있다.

사내 소프트웨어의 잠재적인 장점은 기능과 보고서를 사양에 정확히 맞춰 개발할 수 있다는 것이다. 관리진도 개발 과정에 참여하여 어느 정도의 통제력을 가질 수 있다. 관리진은 경쟁사의 새로운 이니셔티브에 대응하거나 고객 또는 공급업체의 요구 사항을 구현하기로 결정할 수 있다. 합병 또는 인수는 기업 소프트웨어 변경을 필요하게 할 수 있다. 사내 소프트웨어의 잠재적인 단점은 시간 및 자원 비용이 막대할 수 있다는 것이다. 또한, 기능 및 성능과 관련된 위험이 도사리고 있을 수 있다.

상용 소프트웨어의 잠재적 장점은 선행 비용을 식별할 수 있고, 기본적인 요구 사항이 충족되며, 성능 및 신뢰성에 대한 검증된 기록이 있다는 것이다. 상용 소프트웨어의 잠재적 단점은 최종 사용자를 혼란스럽게 하는 불필요한 기능이 있을 수 있고, 기업에 필요한 기능이 부족할 수 있으며, 데이터 흐름이 기업의 업무 프로세스와 일치하지 않을 수 있다는 것이다.

#### 애플리케이션 서비스 제공자
맞춤형 엔터프라이즈 애플리케이션을 경제적으로 얻는 한 가지 방법은 애플리케이션 서비스 제공자를 이용하는 것이다. 전문 기업들은 하드웨어, 맞춤 소프트웨어, 최종 사용자 지원을 제공한다. 이들은 숙련된 정보 시스템 직원을 보유하고 있기 때문에 새로운 애플리케이션 개발 속도를 높일 수 있다. 가장 큰 장점은 사내 자원이 복잡한 컴퓨터 프로젝트의 인력 배치 및 관리에 얽매이지 않도록 해준다는 것이다. 많은 애플리케이션 서비스 제공업체는 정보 시스템 자원이 제한적인 소규모의 빠르게 성장하는 기업을 대상으로 한다. 반면에, 주요 시스템을 가진 대기업은 이미 기술 인프라를 갖추고 있을 가능성이 높다. 한 가지 위험은 민감한 정보를 외부 조직에 맡겨야 한다는 것이다. 또 다른 위험은 제공업체의 인프라 신뢰성을 신뢰해야 한다는 것이다.

### 운영체제

[섬네일|프로그램 vs. [프로세스 vs. 스레드 스케줄링, 선점, 문맥 교환|upright=1.8]]
운영체제는 컴퓨터의 기본 기능, 즉 프로세스 스케줄링 및 주변기기 제어를 지원하는 저수준 소프트웨어이다.

1950년대에는 프로그래머이자 운영자였던 사람이 프로그램을 작성하고 실행했다. 프로그램 실행이 끝나면 출력물이 인쇄되거나 나중에 처리하기 위해 종이 테이프나 카드에 펀치되었을 수 있다. 종종 프로그램이 작동하지 않았다. 그러면 프로그래머는 콘솔 불빛을 보고 콘솔 스위치를 조작했다. 운이 좋지 않으면 추가 연구를 위해 메모리 덤프가 만들어졌다. 1960년대에는 프로그래머들이 운영자의 작업을 자동화하여 낭비되는 시간을 줄였다. 운영체제라는 프로그램은 항상 컴퓨터에 유지되었다.

운영체제라는 용어는 두 가지 소프트웨어 수준을 지칭할 수 있다. 운영체제는 커널 프로그램을 지칭할 수 있는데, 이는 프로세스, 메모리, 장치를 관리한다. 더 넓게는 운영체제가 중앙 소프트웨어의 전체 패키지를 지칭할 수 있다. 이 패키지에는 커널 프로그램, 명령줄 인터페이스, 그래픽 사용자 인터페이스, 유틸리티 프로그램, 편집기가 포함된다.

#### 커널 프로그램
섬네일|커널은 응용 소프트웨어를 컴퓨터 하드웨어에 연결한다.
커널의 주요 목적은 컴퓨터의 제한된 자원을 관리하는 것이다.
* 커널 프로그램은 프로세스 스케줄링을 수행해야 한다. 이는 문맥 교환이라고도 알려져 있다. 커널은 컴퓨터 프로그램이 실행을 위해 선택될 때 프로세스 제어 블록을 생성한다. 그러나 실행 중인 프로그램은 중앙 처리 장치에 대한 독점적인 접근 권한을 시간 할당량 동안만 얻는다. 각 사용자에게 연속적인 접근의 모습을 제공하기 위해 커널은 각 프로세스 제어 블록을 빠르게 선점하여 다른 것을 실행한다. 시스템 프로그래밍 개발자의 목표는 디스패치 대기 시간을 최소화하는 것이다.
섬네일|250px|물리 메모리는 RAM과 하드 디스크에 분산되어 있다. 가상 메모리는 하나의 연속적인 블록이다.
* 커널 프로그램은 메모리 관리를 수행해야 한다.
:* 커널이 처음에 실행 파일을 메모리에 로드할 때, 주소 공간을 논리적으로 영역으로 나눈다. 커널은 마스터 영역 테이블과 많은 프로세스별 영역(pregion) 테이블을 유지한다. 각 실행 중인 프로세스에 하나씩이다. 이 테이블들은 가상 주소 공간을 구성한다. 마스터 영역 테이블은 그 내용이 물리 메모리에 어디에 위치하는지 결정하는 데 사용된다. pregion 테이블은 각 프로세스가 자체 프로그램 (텍스트) pregion, 데이터 pregion, 스택 pregion을 가질 수 있도록 한다.
:* 프로그램 pregion은 기계어 명령어를 저장한다. 기계어 명령어는 변경되지 않으므로, 프로그램 pregion은 동일한 실행 파일의 여러 프로세스에서 공유될 수 있다.
:* 시간과 메모리를 절약하기 위해 커널은 전체 실행 파일을 완전히 로드하는 것이 아니라 디스크 드라이브에서 실행 명령어 블록만 로드할 수 있다.
:* 커널은 가상 주소를 물리 주소로 변환하는 책임이 있다. 커널은 메모리 컨트롤러로부터 데이터를 요청하고, 대신 페이지 부재를 받을 수 있다. 이 경우 커널은 메모리 관리 장치에 접근하여 물리 데이터 영역을 채우고 주소를 변환한다.
:* 커널은 프로세스의 요청에 따라 힙에서 메모리를 할당한다. 프로세스가 메모리 사용을 마치면 해제를 요청할 수 있다. 프로세스가 할당된 모든 메모리 해제를 요청하지 않고 종료되면 커널은 쓰레기 수집을 수행하여 메모리를 해제한다.
:* 커널은 또한 프로세스가 자체 메모리에만 접근하고 커널이나 다른 프로세스의 메모리에는 접근하지 않도록 보장한다.
* 커널 프로그램은 파일 시스템 관리를 수행해야 한다. 커널은 파일을 생성, 검색, 업데이트, 삭제하는 명령어를 가지고 있다.
* 커널 프로그램은 장치 관리를 수행해야 한다. 커널은 마우스, 키보드, 디스크 드라이브, 프린터 및 기타 장치에 대한 인터페이스를 표준화하고 단순화하는 프로그램을 제공한다. 또한, 두 프로세스가 동시에 장치를 요청할 경우 커널은 장치에 대한 접근을 중재해야 한다.
* 커널 프로그램은 네트워크 관리를 수행해야 한다. 커널은 프로세스를 대신하여 네트워크 패킷을 전송하고 수신한다. 핵심 서비스 중 하나는 대상 시스템에 대한 효율적인 경로를 찾는 것이다.
* 커널 프로그램은 프로그래머가 사용할 수 있는 시스템 수준 함수를 제공해야 한다.
** 프로그래머는 상대적으로 간단한 인터페이스를 통해 파일에 접근하며, 이 인터페이스는 파일 생성, 파일 서술자, 파일 탐색, 물리적 읽기, 물리적 쓰기 등을 포함하는 상대적으로 복잡한 저수준 I/O 인터페이스를 실행한다.
** 프로그래머는 상대적으로 간단한 인터페이스를 통해 프로세스를 생성하며, 이 인터페이스는 상대적으로 복잡한 저수준 인터페이스를 실행한다.
** 프로그래머는 상대적으로 간단한 인터페이스를 통해 날짜/시간 연산을 수행하며, 이 인터페이스는 상대적으로 복잡한 저수준 시간 인터페이스를 실행한다.
* 커널 프로그램은 실행 중인 프로세스 간의 통신 채널을 제공해야 한다. 대규모 소프트웨어 시스템의 경우 시스템을 더 작은 프로세스로 설계하는 것이 바람직할 수 있다. 프로세스는 신호를 주고받음으로써 서로 통신할 수 있다.

원래 운영체제는 어셈블리어로 프로그래밍되었지만, 현대 운영체제는 일반적으로 C, 오브젝티브-C, 스위프트와 같은 고급 언어로 작성된다.

### 유틸리티 프로그램
유틸리티는 시스템 관리 및 소프트웨어 실행을 돕는 프로그램이다. 운영체제는 일반적으로 저장 장치, 메모리, 스피커, 프린터와 같은 하드웨어를 확인하는 유틸리티를 제공한다. 유틸리티는 저장 장치의 성능을 최적화할 수 있다. 시스템 유틸리티는 하드웨어 및 네트워크 성능을 모니터링하고, 측정값이 정상 범위를 벗어날 때 경고를 트리거할 수 있다. 유틸리티는 파일을 압축하여 저장 공간과 네트워크 전송 시간을 줄일 수 있다. 유틸리티는 데이터 세트를 정렬하고 병합하거나 컴퓨터 바이러스를 탐지할 수 있다.

### 마이크로코드 프로그램

섬네일|96px|right|NOT 게이트
섬네일|96px|right|NAND 게이트
섬네일|96px|right|NOR 게이트
섬네일|96px|right|AND 게이트
섬네일|96px|right|OR 게이트
마이크로코드 프로그램은 소프트웨어 구동 컴퓨터의 데이터패스를 제어하는 최하위 인터프리터이다.
(하드웨어의 발전으로 이러한 연산은 하드웨어 실행 회로로 이동했다.) 마이크로코드 명령어를 통해 프로그래머는 컴퓨터의 실제 하드웨어인 디지털 논리 수준을 더 쉽게 구현할 수 있다. 디지털 논리 수준은 컴퓨터 과학과 컴퓨터 공학의 경계이다.

논리 회로는 온 또는 오프 두 가지 신호 중 하나를 반환할 수 있는 작은 장효과 트랜지스터이다.

* 트랜지스터 하나를 가지면 NOT 게이트를 형성한다.
* 두 개의 트랜지스터를 직렬로 연결하면 NAND 게이트를 형성한다.
* 두 개의 트랜지스터를 병렬로 연결하면 NOR 게이트를 형성한다.
* NOT 게이트를 NAND 게이트에 연결하면 AND 게이트를 형성한다.
* NOT 게이트를 NOR 게이트에 연결하면 OR 게이트를 형성한다.

이 다섯 개의 게이트는 이진 대수의 빌딩 블록, 즉 컴퓨터의 디지털 논리 기능을 형성한다.

마이크로코드 명령어는 프로그래머가 이진 대수 형태로 디지털 논리 기능을 형성하는 대신 사용할 수 있는 니모닉이다. 이들은 중앙 처리 장치(CPU)의 컨트롤 스토어에 저장된다.
이러한 하드웨어 수준 명령어는 데이터패스 전체에 걸쳐 데이터를 이동시킨다.

마이크로 명령어 사이클은 마이크로시퀀서가 마이크로프로그램 카운터를 사용하여 랜덤 액세스 메모리에서 다음 기계어 명령어를 가져올 때 시작된다. 다음 단계는 하드웨어 모듈에 적절한 출력 라인을 선택하여 기계어 명령어를 디코딩하는 것이다.
마지막 단계는 하드웨어 모듈의 게이트 집합을 사용하여 명령어를 실행하는 것이다.

섬네일|right|ALU의 기호적 표현
산술을 수행하는 명령어는 산술 논리 장치 (ALU)를 통과한다. ALU에는 정수를 더하고, 시프트하고, 비교하는 기본 연산을 수행하는 회로가 있다. ALU를 통해 기본 연산을 결합하고 반복함으로써 CPU는 복잡한 산술을 수행한다.

마이크로코드 명령어는 CPU와 메모리 컨트롤러 사이에 데이터를 이동시킨다. 메모리 컨트롤러 마이크로코드 명령어는 두 개의 레지스터를 조작한다. 기억 장치 주소 레지스터는 각 메모리 셀의 주소에 접근하는 데 사용된다. 기억 장치 데이터 레지스터는 각 셀의 내용을 설정하고 읽는 데 사용된다.

## 내용주

## 각주

분류:소프트웨어
분류:컴퓨터 프로그래밍