Довольно сложно коротко описать длинную историю развития вычислительной техники и языков программирования. Далее делается попытка отметить ключевые особенности устройства компьютеров, программ и языков программирования. Нужно понимать, что реальность, безусловно, намного сложнее.

Архитектура ЭВМ

Компьютер является устойством преобразования данных. Каким-то образом ему на вход поступает входная информация, а на выходе получается результат. Природа этой информации может быть любой. Например, на вход поступают натуральные числа, а на выходе получается их произведение. Или же на вход подается текст на русском языке, а на выходе – звуковой файл с начитанным переводом на японский. Входная и выходная информация должна быть представлена в удобном для обработки виде, а сложность преобразования определяется программой, которая выполняется на компьютере.

Если не вдаваться в (действительно сложные) технические детали, то архитектура современных компьютеров мало чем отличаются от архитектуры вычислительных машин 70-х годов. Когда вы выбираете ноутбук, то видите его описание типа

Core i5 520M 2400 Mhz/14"/1600x900/2048Mb/250 Gb/DVD-RW/Wi-Fi/Bluetooth/Win 7 Prof 64bit.
Здесь укзан тип процессора (Core i5 520М) и его характеристики (тактовая частота 2400 мегагерц), свойства устройства ввода-вывода (экран 14 дюймов с разрешением 1600 на 900 точек, наличие клавиатуры предполагается), размер оперативной памяти (2048 мегабайт) и долговременной памяти (жесткий диск 250 гигабайт), наличие перефирийных устройств (привод для чтения оптических дисков, устройства для беспроводной передачи данных) и операционная система. Все это присутсвовало и в старых компьютерах.

Простейшая модель компьютера состоит только из двух элементов: процессора и памяти (в реальности эта память больше всего соответствует оперативной памяти). Ввод и вывод данных счититаются второстепенными задачами и в модели вычислений не рассматриваются.

Количество регистров процессора очень ограничено (в современных процессорах доступны десятки регистров) и они используются для хранения временных результатов вычислений. Основные данные храняться в памяти.

Процесс выполнения программы можно представить следующим образом. Сначала в память загружается программа и входные данные. Условно, программа всегда начинается с адреса 1, а данные, которые она должна обрабатывать – с адреса 100 000 (если программа очень большая, то данные будут начинаться с большего адреса). После этого последовательно начинают выполняться команды программы. Если встречается команда изменения хода выполнения программы, то управление передается на указанный в ней адрес. Программа записывает результат своей работы в область памяти с заранее оговоренными адресами.

Тактовая частота процессора, грубо говоря, означает сколько команд процессор выполняет в секунду. Каждая команда выполняется определенное количество тактов (сложение и умножение может выполняться за 2-3 такта, деление за 6-10 тактов). Если частота процессора составляет 2 гигагерца, то такой процессор теоретически может выполнять порядка 1 миллиарда сложений в секунду. К сожалению, самая медленная операция – это чтение и запись ячеек памяти (от десятков до сотен тактов), что значительно снижает производительность.

Машинный код

Каждый тип процессора реализует свой набор команд. Разные команды могут иметь разное количество операндов. Например, для команды сложения требуется указать где находятся слагаемые (в каких регистрах) и куда нужно записать результат, а для команды умножения на -1 нужно указать только один регистр. Команды определяются числовым кодом. Например, умножение – 1, сложение – 2, изменение знака – 3 и т.д. Номера регистров и адреса памяти также могут быть представлены натуральнми числами. Это означает, что программа для конкретного процессора – это просто последовательность чисел. Например, последовательность

1 3 5 4 3 4 43 4 10234
может означать "сложить значения регистров 3 и 5, результат записать в регистр 4 (команда 1, операнды 3 5 4), умножить содержимое регистра 4 на -1 (команда 3, операнд 4), записать содержимое регистра 4 в ячейку памяти с адресом 10234 (команда 43, операнды 4 10234)".

Коды команд, количество и смысл их операндов меняются от процессора к процессору. Если взять программу для одного процессора и выполнить ее на другом, то она может не заработать. Первые программы составляли непосредственно в виде машинного кода, однако этот процесс крайне трудаемкий, а вносить изменения в готовую программу практически невозможно.

Ассемблер

Следующим этапом развития стало появление языков ассемблера. По сути было сделано две вещи:

  1. коды команд и регистров получили символьные имена, то есть программы стали составлять в виде, пригодном для человека;
  2. преобразование программы в машинный код, то есть вид, пригодный для компьютера, стали выполнять специальные программы.

Приведенный выше пример может выглядеть так (после точки с запятой указаны машинные коды, соответсвующиее командам):

ADD C, E, D             ; 1 3 5 4
NEG D                   ; 3 4
STORE D, 10234          ; 43 4 10234
Нетрудно заметить, что коды процесора получаются простой подстановкой числовых значений вместо символьных констант. Буквы A, B, C, D, E обозначают регистры, ADD, NEG, STORE – имена команд. Каждый процессор определяет свой язык ассемблера.

Языки программирования высокого уровня

Языки программирования высокого уровня, такие как C или C++, позволяют писать программы, которые не зависят от конкретного процессора. Программа сначала (автоматически) преобразуется в программу на ассемблере (как правило, для того процессора, на котором выполняется это преобразование), а потом и в соответсвующий машинный код. Преобразование из языка высокого уровня в машинный код называется компиляцией, а программа, выполняющая это преобразование – компилятором. Компилятор языка высокого уровня выполняет много рутинных действий и преобразований программы: для переменных выделяются ячейки памяти, операторы языка преобразуются в последовательность команд и т.п.

Рассмторим такой пример:

int main() {              // Функция main - начало программы
    int k = 0, m = 1;     // Выделяются две ячейки памяти. Одна называется m, другая - k.
                          //   По этим адресам (пусть 123456 и 123458) записываются 0 и 1, соответственно.
    k = (m + k) / m;      // Этот оператор преобразуется в последовательность команд вида
                          //   LOAD A, 123458     ; загрузить m
                          //   LOAD B, 123456     ; загрузить k
                          //   ADD A, B, C        ; сложить
                          //   DIV C, A, B        ; разделить
                          //   STORE B, 123456    ; записать результат по адресу переменной k
    // ...
}

Языки высокого уровня развивают идею составления программ в виде, понятным для человека. Одна команда такого языка может соответствовать нескольким командам на ассемблере, но в конечном итоге процессор исполняет длинную последовательность машинных команд, каждая из которых выполняет элементарное действие.