Table Of ContentС++14: устройство и применение
(рабочая версия)
Лебедев П.А.
13 января 2016 г.
ОГЛАВЛЕНИЕ
Оглавление
Предисловие v
I Общие сведения о языках программирования 1
1 Введение в языки программирования 2
1.1 Определение языка программирования и его аспектов . . . . . . . . . . . . . . . 3
1.2 История языков программирования . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.3 Парадигмы программирования . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.4 Инструментальные средства . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.4.1 Трансляторы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.4.2 Другие инструментальные средства . . . . . . . . . . . . . . . . . . . . . 9
2 Основные понятия архитектуры ЭВМ 11
II Язык программирования C++ 14
3 Обзор и основные понятия языка C++ 15
3.1 Стандарт ISO/IEC 14882:2014(E) . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.2 Обзор процесса трансляции . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.3 Лексический состав языка . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.4 Типизация . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.5 Обзор структуры программы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.6 Пример первой программы. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4 От теории к первой программе 26
4.1 Основные арифметические типы данных . . . . . . . . . . . . . . . . . . . . . . 26
4.1.1 Стандартные целые типы данных. . . . . . . . . . . . . . . . . . . . . . . 26
4.1.2 Стандартные типы с плавающей точкой . . . . . . . . . . . . . . . . . . . 32
4.1.3 Стандартные преобразования арифметических типов . . . . . . . . . . . 34
4.1.4 Операция приведения типов static_cast . . . . . . . . . . . . . . . . . . . 35
4.2 Основные выражения с арифметическими операндами . . . . . . . . . . . . . . 35
4.2.1 Арифметические операции . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
4.2.2 Операции с логическими значениями. . . . . . . . . . . . . . . . . . . . . 37
4.2.3 Порядок вычисления операций в выражениях . . . . . . . . . . . . . . . 38
4.3 Объекты и доступ к ним . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
4.3.1 Простые определения объектов . . . . . . . . . . . . . . . . . . . . . . . . 40
4.3.2 Категории значений. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
4.3.3 Чтение и запись объектов. Операция простого присваивания. Оператор-
выражение. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
4.3.4 Операции составного присваивания, инкремента и декремента . . . . . . 44
4.3.5 Пример компиляции выражения . . . . . . . . . . . . . . . . . . . . . . . 46
4.4 Введение в функции . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
4.4.1 Определения функций . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
4.4.2 Операция вызова функции . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
4.5 Введение в форматированный ввод/вывод . . . . . . . . . . . . . . . . . . . . . . 52
4.5.1 Форматированный вывод. . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
4.5.2 Форматированный ввод . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
4.6 Использование среды Qt Creator . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
i
ОГЛАВЛЕНИЕ
4.6.1 Настройка компилятора . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
5 Структурное программирование на языке C++ 59
5.1 Использование функций . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
5.2 Ветвления . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
5.2.1 Условный оператор if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
5.2.2 Условная операция ?: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
5.3 Операторы цикла . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
5.3.1 Оператор цикла с предусловием while . . . . . . . . . . . . . . . . . . . . 65
5.3.2 Оператор цикла с постусловием do . . . . . . . . . . . . . . . . . . . . . . 66
5.3.3 Оператор цикла for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
5.4 Изменение порядка выполнения команд в машинном коде . . . . . . . . . . . . 69
5.5 Операторы перехода break, continue, goto . . . . . . . . . . . . . . . . . . . . . . 70
5.5.1 Оператор break в телах циклов . . . . . . . . . . . . . . . . . . . . . . . . 71
5.5.2 Оператор continue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
5.5.3 Оператор goto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
5.6 Константные выражения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
5.7 Оператор switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
5.8 Стили оформления программ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
5.9 Примеры реализации простых алгоритмов . . . . . . . . . . . . . . . . . . . . . 83
5.9.1 Алгоритмы над фиксированным числом объектов . . . . . . . . . . . . . 83
5.9.2 Алгоритмы обработки последовательностей . . . . . . . . . . . . . . . . . 84
5.10 Задания на структурное программирование . . . . . . . . . . . . . . . . . . . . . 85
6 От описания до единиц трансляции 87
6.1 Описания и определения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
6.1.1 Видимость идентификаторов меток . . . . . . . . . . . . . . . . . . . . . 87
6.1.2 Описания и определения объектов и функций. . . . . . . . . . . . . . . . 88
6.1.3 Производные типы. Категория типов «функция». . . . . . . . . . . . . . 88
6.1.4 Блочная область видимости и автоматическое время хранения. Рекурсия. 90
6.1.5 Аппаратный стек как реализация механизма вызова функций и автома-
тического времени хранения . . . . . . . . . . . . . . . . . . . . . . . . . . 92
6.1.6 Область видимости пространства имён и статическое время хранения . 95
6.1.7 Поиск имён . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
6.1.8 Инициализация . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
6.1.9 Связанность описаний функций . . . . . . . . . . . . . . . . . . . . . . . . 109
6.1.10 Спецификаторыклассапамятиstaticиextern.Анонимныепространства
имён. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
6.1.11 Описания в операторах . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
6.2 Псевдонимы типов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
6.3 Возможности предварительной обработки . . . . . . . . . . . . . . . . . . . . . . 116
6.3.1 Включение других текстов . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
6.3.2 Макроподстановки . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
6.3.3 Условная компиляция . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
6.4 Заголовочные файлы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
6.5 Программы из нескольких единиц трансляции . . . . . . . . . . . . . . . . . . . 124
6.6 Этапы трансляции программы на практике . . . . . . . . . . . . . . . . . . . . . 125
6.7 Встраиваемые функции. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
7 Указатели и массивы 134
7.1 Указатели на отдельные объекты . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
7.1.1 Категория типов «указатель» . . . . . . . . . . . . . . . . . . . . . . . . . 134
7.1.2 Использование указателей с функциями . . . . . . . . . . . . . . . . . . . 136
7.2 Операция sizeof . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
7.3 Простое использование массивов . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
7.3.1 Арифметика указателей . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
7.3.2 Описание и работа с массивами . . . . . . . . . . . . . . . . . . . . . . . . 140
7.3.3 Инициализаторы массивов . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
7.3.4 Массивы и функции . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
7.4 Комбинирование конструкций создания производных типов . . . . . . . . . . . 148
7.4.1 Массивы массивов. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
ii
ОГЛАВЛЕНИЕ
7.5 Указатели на функции . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
7.6 Квалификаторы типов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
7.6.1 Квалификаторы параметров функций. const-корректность.. . . . . . . . 156
7.7 Расширенные константные выражения . . . . . . . . . . . . . . . . . . . . . . . . 157
7.8 Нулевой указатель. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
7.9 Строковые литералы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
7.10 Задания на массивы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
8 Динамические структуры данных 163
8.1 Сложность вычислений . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
8.1.1 Оценка алгоритмической сложности . . . . . . . . . . . . . . . . . . . . . 165
8.1.2 Сортировка массива . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
8.1.3 Двоичный поиск . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
8.2 Работа с динамической памятью . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
8.2.1 Владение . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
8.2.2 Динамические массивы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
8.2.3 Обзор линейных структур данных . . . . . . . . . . . . . . . . . . . . . . 179
8.3 Многомерные структуры данных . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
8.3.1 Шаговый доступ. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
8.3.2 Массивы указателей и «рваные» массивы . . . . . . . . . . . . . . . . . . 182
8.4 Введение в классовые типы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
8.4.1 Имена классовых типов . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
8.4.2 Влияние квалификаторов на структуру и её члены . . . . . . . . . . . . 187
8.4.3 Выравнивание объектов . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
8.4.4 Использование классовых типов с функциями . . . . . . . . . . . . . . . 190
8.5 Неполные типы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
8.6 Графовые структуры данных . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
8.6.1 Связанные списки . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
8.6.2 Хеш-таблицы. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
8.6.3 Деревья . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
8.7 Сравнение динамических структур данных . . . . . . . . . . . . . . . . . . . . . 201
8.7.1 Комбинирование структур данных . . . . . . . . . . . . . . . . . . . . . . 202
9 Основы объектно-ориентированного программирования на языке C++ 203
9.1 Аргументы по умолчанию . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
9.2 Последовательность стандартных преобразований . . . . . . . . . . . . . . . . . 205
9.2.1 Приведения типов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
9.3 Перегрузка функций . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
9.4 Временные объекты . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
9.5 Ссылки . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
9.5.1 Ссылки на объекты . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
9.5.2 Ссылки на функции . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
9.5.3 Ссылки и перегрузка функций . . . . . . . . . . . . . . . . . . . . . . . . 218
9.6 Классовые типы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
9.6.1 Функции-члены классовых типов . . . . . . . . . . . . . . . . . . . . . . . 221
9.6.2 Статические члены классов . . . . . . . . . . . . . . . . . . . . . . . . . . 227
9.6.3 Вложенные и локальные классы . . . . . . . . . . . . . . . . . . . . . . . 229
9.6.4 Контроль доступа к членам классов . . . . . . . . . . . . . . . . . . . . . 231
9.6.5 Инициализация классов . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
9.6.6 Специальные функции-члены . . . . . . . . . . . . . . . . . . . . . . . . . 239
9.6.7 Конструктор по умолчанию . . . . . . . . . . . . . . . . . . . . . . . . . . 240
9.6.8 Автоматическое освобождение ресурсов . . . . . . . . . . . . . . . . . . . 241
9.6.9 Копирование и перемещение объектов классовых типов . . . . . . . . . . 244
9.6.10 Размещение описаний классов . . . . . . . . . . . . . . . . . . . . . . . . . 254
9.6.11 Пользовательские преобразования типов . . . . . . . . . . . . . . . . . . 259
9.6.12 Перегрузка операций . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
9.6.13 Аргументо-зависимый поиск имён . . . . . . . . . . . . . . . . . . . . . . 265
9.6.14 Друзья классов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
iii
ОГЛАВЛЕНИЕ
10 Обобщённое программирование на языке C++ 270
10.1 Шаблоны . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
10.1.1 Концепции . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
10.1.2 Шаблоны функций и разрешение перегрузок . . . . . . . . . . . . . . . . 272
10.1.3 Шаблоны классов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278
10.1.4 Неявнаяиявнаяинстанциацияшаблонов.Шаблоныизаголовочныефай-
лы.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
10.1.5 Явная специализация шаблонов . . . . . . . . . . . . . . . . . . . . . . . . 284
10.1.6 Частичная специализация шаблонов классов . . . . . . . . . . . . . . . . 286
10.1.7 Поиск имён в шаблонах . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
10.2 Простые применения шаблонов . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
10.2.1 Списки инициализации в роли аргументов . . . . . . . . . . . . . . . . . 291
10.2.2 Прозрачная передача аргументов. std::forward. Вариадические шаблоны. 292
10.2.3 std::move. Метафункции. Шаблоны псевдонимов типов . . . . . . . . . . 296
10.2.4 std::exchange . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
10.2.5 Автоматический вывод типов в языке C++. Заполнители. Хвостовой
возвращаемый тип функции . . . . . . . . . . . . . . . . . . . . . . . . . . 299
10.3 Стандартная библиотека шаблонов . . . . . . . . . . . . . . . . . . . . . . . . . . 302
10.3.1 Простые концепции. Не типовые параметры шаблонов . . . . . . . . . . 302
10.3.2 Итераторы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
10.3.3 Черты . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309
10.3.4 Контейнеры . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310
10.3.5 Последовательности. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312
10.3.6 Строки . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
10.3.7 Цикл for для диапазона . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
10.3.8 Функциональные объекты. Лямбда-выражения . . . . . . . . . . . . . . . 315
10.3.9 Алгоритмы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
10.3.10Ассоциативные контейнеры . . . . . . . . . . . . . . . . . . . . . . . . . . 321
11 Архитектура приложений на C++ 322
11.1 Наследование классов. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
11.1.1 Основы наследования . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
11.1.2 Контроль доступа при наследовании классов . . . . . . . . . . . . . . . . 327
11.1.3 Специальные члены класса и наследование . . . . . . . . . . . . . . . . . 330
11.1.4 Цепочки наследования . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
11.1.5 Виртуальные функции . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
11.1.6 Чисто виртуальные функции и абстрактные классы . . . . . . . . . . . . 338
11.1.7 Потоки ввода-вывода . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
A Список операций языка C++ 346
Литература 348
История версий 349
iv
0. Предисловие
Предисловие
Данный текст предназначен для изучающих дисциплину «Языки программирования» и
содержит весь материал, необходимый для успешной теоретической подготовки и написа-
ния практических работ. Если вы не согласны с данным утверждением, автор настоятельно
просит уведомить его об этом, чтобы недочёты и упущения оперативно исправлялись.
Выделенный жирным курсивом текст обычно встречается при первом употреблении
терминов вместе с их определением. Знание этих определений является основополагающим
как для понимания дальнейшего материала, так и в практической работе. Для большинства
терминоввскобкахдаётсяанглоязычныйвариант,чтопоможетвампричтениианглоязычной
литературыисообщений,выдаваемыхпрограммами.Еслитакимобразомвыделеннетермин,
а целое предложение, значит оно несёт исключительную важность. Также следует обращать
особое внимание на содержимое списков.
Краткиефрагментыиотдельные«слова»языковпрограммирования,атакжелюбойдру-
гой текст, предназначенный для ввода в компьютер, набран моноширинным шрифтом. При
описании синтаксиса некоторых конструкций, среди фиксированных элементов, набранных
таким образом, могут встречаться переменные части, которые набраны обычным шрифтом.
Фиксированныечастиподлежатвводувточности,какуказано,переменныечастиследуетпри
использовании заменять соответствующими им по смыслу элементами программы. Нижний
индекс «опц» означает, что данный элемент является опциональным. Например:
return выражение ;
опц
Сначаласледуетзаписатьсловоreturn,являющеесяфиксированнымэлементом.Вместосло-
ва «выражение» нужно ввести некоторое выражение, которое может быть опущено, что по-
казано нижним индексом «опц». Вслед за этим следует записать точку с запятой, также
являющуюся фиксированным элементом.
Текст, выделенный чертами на полях, как этот абзац, содержит расширенную, более
сложную часть материала. При первом прочтении её можно пропустить, но вернуться
к ней следует обязательно перед переходом к следующей главе, чтобы, если не помнить
наизусть всех хитростей, то хотя бы иметь представление о деталях и возможных про-
блемах и уметь быстро находить в тексте нужный материал, когда в этом возникнет
необходимость.
Крупные фрагменты программ имеют своё отдельное место в тексте. Они раскрашены
для улучшения зрительного восприятия, а их строки пронумерованы на полях для удобства
ссылок на них:
1 int example(int& a,int b,int c)
2 {
3 // Это фрагмент текста программы, также называемый листингом.
4 a = b+c;
5 return 0;
6 }
Номера строк, разумеется, к самому тексту программы не относятся. Нетривиальные по
объёму фрагменты кода, не являющиеся самодостаточными программами, обычно отмеча-
ются соответствующим комментарием.
В примерах взаимодействия программ с пользователем приводится весь текст, как он вы-
глядит на экране, при этом текст, вводимый пользователем, подчёркивается. Нажатие кла-
виши Enter обозначается знаком ê. Пример:
Input number: 234ê
You have entered 234.
v
Часть I
Общие сведения о языках
программирования
1
1. Введение в языки программирования
Глава 1
Введение в языки
программирования
Этоттекстпризвандатьчитателюначальныесведенияоязыкахпрограммирования,т.е.о
томсредстве,котороепозволяетвоспользоватьсявычислительнымустройствомдлярешения
требуемых задач. Речь идёт, разумеется, об участии в активной роли разработчика, который
не только пользуется уже существующими инструментами, но и улучшает существующие, а
также создаёт новые.
Автор преследовал следующие цели при написании ещё одной книги по программирова-
нию:
• Дать читателю понимание происходящего на всех уровнях, начиная от архи-
тектурыпрограммныхсистем,изаканчиваяспецификойработыцентральногопроцессора.
Этокнигадлятех,ктохочетпонимать,чтоженасамомделепроисходитвнутрикомпью-
тера,ипочему.Безподробногорассмотренияабстрактныхипрактическихаспектовневоз-
можно объяснить разницу между «работающей» (внешне, здесь и сейчас) и «корректной»
программой, которая действительно работает во всех смыслах важных для разработчи-
ков, служб поддержки и конечных пользователей в рамках тех или иных требований.
• Рассмотреть язык C++ в его современном варианте, зачастую кардинально от-
личном от описанного в имеющейся, особенно русскоязычной, литературе. Само препо-
давание этого языка, особенно в качестве первого для начинающих специалистов, часто
подвергается жёсткой критике, однако автор всё равно выбрал этот путь. Во-первых, этот
язык достаточно низкоуровнёвый, чтобы описание происходящего в нём на всех уровнях
былодостаточнопрозрачнымилаконичнымдляизложенияначинающимпрограммистам.
Отсутствие такого понимания и породило армию «шаманов», занимающих значительную
долю рынка вакансий разработчиков ПО. Во-вторых, за этим языком в настоящем (и обо-
зримом будущем) остаётся огромное количество кода который необходимо использовать
и дорабатывать. Поэтому серьёзному специалисту никак не избежать знакомства с этим
языком,аеслиэтотак,тоследуетстроитьсистемузнанийначинаяссамогонизкогоуров-
ня. Овладение многими языками более высокого уровня, в первую очередь использующим
тежепарадигмы,чтоиC++,послезнакомствасним,являетсяотносительнолёгкимпро-
цессом,посколькуостанетсятолькодостроитьещёодинэтажабстракций,чегонескажешь
об обратном процессе, что многократно подтверждалось на практике.
Практически во всех аспектах своего существования язык C++ связан с языком, от кото-
рогобылпроизведён—языкомC.ЯзыкCимеетменьшеечислосредств,чемC++,иимеет
другие многочисленные отличия. Поскольку программисту на C++ также не избежать и
встречи с C, эти различия будут приведены в приложении.
• Ввести все требуемые определения в том виде, в котором они даны в стан-
дарте, в том числе и на английском языке. Охватить 100% возможностей даже чистого
стандарта языка данная книга пытаться не будет, поэтому необходимо с самого начала
готовить читателя к чтению других источников. Кроме этого на этом языке говорят все
инструментальные средства, с которым программисту предстоит вести диалог.
Данный текст предполагает наличие знаний в областях математики, информатики и ан-
глийского языка в рамках школьной программы. Мы будем останавливаться на необходи-
мых понятиях из дисциплин «Математический анализ», «Линейная алгебра» и «Архитек-
тура ЭВМ» только в необходимых для данного изложения объёме, поскольку их изучение
2
1.1. Определение языка программирования и его аспектов
предполагаетсякакпроцесс,параллельныйзнакомствусрассматриваемымвэтомтекстема-
териалом.Другиезатрагиваемыеобластинаукибудутотмечены,какимеющиезначимость,в
соответствующих разделах, чтобы направить читателя на поиск дополнительного материала
в нужном направлении. Перечислим основные из них здесь в виде целей, которые данная
книга не ставит перед собой:
• Детальное изучение языков ассемблера.
Примеры на одном их этих языков будут приводиться с целью демонстрации связи кода
наязыкеC++синструкциями,исполняемымипроцессором,ноучебникомпонемуданная
книга не претендует являться.
• Изложение теории языков и трансляторов.
Мы познакомимся с практически полезными понятиями, необходимыми для понимания
устройства конкретного языка, но рассмотрение теории, лежащей в основе построения ма-
шинных языков как таковых, не входит в задачи данной книги.
• Детальное рассмотрение большого количества алгоритмов и структур данных.
Сколько-либо полное рассмотрение даже очень узких и специализированных вопросов в
этой области легко может занять тысячи страниц. Вместо этого будет введено базовое по-
нятие алгоритмической сложности и рассмотрены имеющиеся на уровне стандарта языка
средства, из которых путём комбинирования можно построить удовлетворительные реше-
ния для многих практических задач.
Ограничив себя конечными рамками, приступим к изложению материала.
1.1. Определение языка программирования и его аспек-
тов
Язык программирования (ЯП) (programming language) — формальная знаковая
система для планирования поведения компьютеров.
Язык программирования — формальный язык (formal language), т.е. множество ко-
нечныхстрокнадконечнымалфавитом.Алфавитопределяетмножествонеделимыхединиц,
последовательности которых применительно к языку программирования называются про-
граммами (program).Хотявбольшинствеслучаевалфавитпрограммы—символы,асама
программа — текст, могут использоваться и другие представления этой формальной кон-
струкции, к ней сводимые.
Необязательнокаждаяпоследовательностьэлементовалфавитаотноситсякконкретному
языку — для этого она также должна быть согласованной (well-formed), а именно удо-
влетворятьнекоторымдополнительнымправилам(например,некаждаяпоследовательность
букв естественного языка имеет в его рамках смысл). Основным способом задания формаль-
ного языка является именно этот набор правил построения согласованных последователь-
ностей из элементов алфавита. Чаще всего он предполагает многоэтапное построение более
сложных конструкций из простых, начиная с элементов алфавита, и заканчивая целой про-
граммой.Этиправилачащевсегоназываютсинтаксисом (syntax)языка,асогласованные
последовательности элементов алфавита — синтаксически корректными программами.
Язык программирования — знаковая система (notation), т.е. система элементов, за
каждым из которых и, следовательно, за программами из них составленными в соответствие
с синтаксисом языка, закреплено некоторое значение или смысл. Правила, задающие связь
между синтаксически корректными элементами программы и их значением, называются се-
мантикой (semantics) языка. В отличие от естественных языков, языки программиро-
вания не допускают разночтений — смысл каждого элемента строг и однозначен. Придание
программам однозначного смысла даёт возможность использовать их для хранения и пере-
дачи информации, необходимой для планирования поведения компьютеров.
Наконец, последний элемент языка программирования, который часто не обсуждается,
несмотрянаегоогромнуюважность,—этопрагматика.Синтаксическихисемантическихас-
пектовдостаточно,чтобыустановитьвзаимоотношениямеждуоднимчеловекомикомпьюте-
ром,посколькупоследнийнеимеетцелевыхустановок,аявляетсявсеголишьисполнителем.
Но язык программирования — не только средство взаимодействия человека с компьютером,
но и средство взаимодействия людей друг с другом, когда речь идёт о планировании поведе-
ния компьютеров.
3
1.2. История языков программирования
Приведём пример: математическая запись aˆb относительно однозначна в том смысле,
что соответствует умножению двух величин. Синтаксический её аспект — правила записи
знака операции между двумя операндами. Семантический аспект — правила, определяющие
понятие «умножение». Но какой проблеме соответствует эта запись? Поиск площади прямо-
угольника по сторонам, нахождение мощности, зная ток и напряжение, расчёт общего числа
предметов,знаячислокоробокипредметоввкаждойизних—вотнесколькопринципиально
разных, но допустимых интерпретаций данной записи. Более того, наше предположение о
том, что знак ˆ соответствует скалярному умножению, также является не беспочвенным, а
следствием интуитивного поиска подходящей целевой установки. Таким образом, для уста-
новления взаимопонимания между людьми, имеющими дело с одной программой, необходим
контекст,впервуюочередь,одинаковыепредставленияосутирешаемойзадачи.Крометого,
даже отдельные элементы программы могут нести смысл не описываемый его формальной
семантикой.
Без рассмотрения синтаксиса и семантики языков книге по языкам программирования
обойтись не удастся, но автор будет стараться уделить не меньшее внимание и последнему,
наивысшему уровню — прагматике (pragmatics), т.е. языковым средствам установления
контекста, ролей и передачи целевых установок между людьми. Переоценить важность это-
го аспекта невозможно, поскольку бурно развивающиеся технологии приводят к тому, что
программа, которую невозможно понять для доработки и исправления ошибок, становится
никому не нужной с момента её первого написания. В любом случае, программы гораздо
чаще читают, нежели пишут.
Отметим, что для использования в вычислительной технике разработано множество ис-
кусственныхязыков,некаждыйизкоторыхявляетсяязыкомпрограммирования—ониотли-
чаютсяотдругихтем,чтопредназначеныдляпланированияповедениякомпьютеровсцелью
решения поставленных людьми задач.
Читателю, которого интересуют другие основополагающие теоретические принципы язы-
ков программирования можно порекомендовать книгу [6].
1.2. История языков программирования
Языки программирования принято разделять на несколько поколений. Точные границы
и принадлежность языка программирования не всегда удаётся определить точно, особенно
учитываяфактизмененияпринятойклассификациисовременем.Приведёмпримерныйплан
исторического развития ЯП с указанием ключевых особенностей, на которых построена эта
классификация.
1. Машинные коды (machine code). Данные языки программирования напрямую отра-
жают систему команд того или иного вычислительного средства, т.е. являются специфич-
ными для семейств или даже отдельных аппаратных средств. Это самый низкий уровень,
которыйможетиметьязыкпрограммирования,еслинеопускатьсядовнутреннегоустрой-
ства соответствующего процессора. Поскольку, фактически, каждому программируемому
вычислительному устройству соответствует такой язык, выбрать «первого» представите-
ля можно по-разному в зависимости от того, что же считать первым компьютером и по
каким критериям. Автор предлагает в качестве примера рассмотреть электромеханиче-
ский компьютер Z3, разработанный Конрадом Цузе и завершённый в 1941-м году, кото-
рый являлся первым работающим в действительности (а не на бумаге) программируемым
устройством. В качестве современных представителей языков программирования первого
поколения приведём две наиболее распространённые в настоящее время архитектуры цен-
тральныхпроцессоров:x86(почтинавернякаиспользуетсяввашемдомашнемкомпьютере)
иARM(вероятнеевсегоиспользуетсяввашемсмартфоне).Формальносистемамашинных
кодов описывает лишь соответствие входных данных, составляющих программу, тем или
иным действиям исполнительного устройства, никак не описывая представление этих ин-
струкций в пригодном для чтения человеком виде. Как следствие, вместо них даже на
самом низком уровне программирования применяются языки ассемблера.
2. Языки ассемблера (assembly language). Специфической чертой этого поколения язы-
ков программирования является прямое соответствие один-в-один команд этого языка и
соответствующихинструкциймашинногокода.Такимобразом,языкиассемблераявляют-
сяформойзаписимашинногокодавформе,читаемойчеловеком.Длякаждогомашинного
языка обычно существует минимум один язык ассемблера, а для популярных архитектур
— несколько. Одна из ранних ЭВМ, называемая EDSAC (1949 г.), имела мнемоническую
4