Текст любой программы на языке С++ состоит из директив препроцессора, описания и (или) определения глобальных объектов и функций.
Директивы препроцессора начинаются с символа #. Каждая директива записывается с новой строки. Директивы обрабатываются не компилятором языка С++, а специальной программой, называемой препроцессором. Пример наиболее употребительных директив препроцессора:
#define PI 3.14159265
#include <iostream>
Первая директива (#define) позволяет объявить глобальную константу PI (число π), вторая (#include) — подключает заголовочный файл iostream, содержащий необходимые средства для ввода и ввода данных.
Более подробно директивы препроцессора будут рассмотрены позже.
Глобальные объекты — это объекты, описание и (или) определение которых дано вне функций. Глобальными могут быть константы, переменные, прототипы функций, типы, создаваемые пользователем (структуры, классы). Чаще всего их располагают до определения функций, т.е. сразу же за директивами, хотя это и не обязательно. Главное, чтобы и директивы препроцессора, и глобальные объекты были записаны в тексте программы до первого их применения.
Функции определяют возможные действия в программе. В тексте программы на языке С++ обязательно должна присутствовать функция, называемая main(). Это «главная» функция. Именно с неё всегда начинается выполнение программы. Программист может в своей программе кроме функции main() создать и использовать любое количество других функций.
Текст всей программы может находиться в одном файле либо в нескольких.
Написав программу, мы хотим увидеть результаты её работы. Но текст программы создаётся на языке программирования (у нас — на С++), а компьютер обрабатывает только машинные команды. Рассмотрим, какие действия необходимо выполнить, чтобы получить программу, пригодную для исполнения (все шаги графически представлены на рисунке ниже).
Шаг 1. Текст программы, называемый исходный модуль, подвергается препроцессорной обработке. Специальная программа (препроцессор) сканирует исходный текст и выполняет необходимые действия над текстом программы.
Например, встретив в тексте программы директиву #include, она вместо этой директивы записывает текст того файла, который указан в директиве. В нашем примере директивой
#include <iostream>
в тест программы будет добавлено содержимое стандартного файла iostream.
Когда препроцессор встречает директиву #define, то он удаляет её из текста программы и далее по тексту выполняет замену, определенную этой директивой. Например, если в тексте есть оператор
s = PI * r * r;
и использовалась директива
#define PI 3.14159265
то после обработки препроцессором получится так:
s = 3.14159265 * r * r;
Шаг 2. После препроцессорной обработки получается полный исходный модуль (чистый текст), который поступает в компилятор. Программа-компилятор выполняет преобразование текста программы с языка С++ в объектный модуль, т.е. в текст на машинном языке. Процесс компиляции очень не прост. Как правило, компиляторы многократно (в несколько проходов) обрабатывают текст программы, выполняя те или иные действия.
Шаг 3. Объектный модуль, полученный в результате компиляции, хотя и представляет собой файл в машинных кодах, но совершенно не пригоден для исполнения компьютером. Почему? Чего ещё не хватает?
Всякая программа на С++ использует стандартную библиотеку, поставляемую вместе с компилятором. В ней хранится код большого количества стандартных функций, которые мы сами не пишем, а применяем в готовом виде. Например, математические функции, операции ввода-вывода и многое другое.
Поэтому на данном шаге компоновщик (или по-другому, редактор связей, линкер) к объектному модулю добавляет машинный код всех тех стандартных функций, которые использованы в программе, и прописывает их адреса для вызова. Заметьте себе, не вообще всех, а только необходимых. Кроме того, программа может состоять не из одной функции main(), а содержать ещё множество собственных функций программиста. Каждая из них компилируется по-отдельности, к каждой из них подключаются необходимые стандартные функции, и все они добавляются к выходному файлу.
В итоге компоновщик собирает из одного или нескольких объектных модулей плюс необходимых функций из стандартной библиотеки исполняемый модуль, пригодный для выполнения компьютером.
Именно исполняемый модуль (исполняемая программа) запускается на исполнение.
Как правило, файл с текстом исходной программы (исходный модуль) имеет расширение .cpp для языка С++ и .c для С, хотя в ряде операционных систем допускаются и другие расширения.
Файл с объектным модулем в операционных системах DOS и Windows обычно имеет расширение .obj, а в UNIX-подобных системах — .o, хотя, к примеру, в интегрированной системе разработки программ CodeBlocks, работающей в Windows, объектный модуль также имеет расширение .o (!). Причина банальна: CodeBlocks — кроссплатформенная система разработки, использует компилятор, портируемый из Linux, отсюда и такая необычность.
Исполняемый файл в операционных системах DOS и Windows всегда имеет расширение .exe или .com, а в UNIX-подобных системах — вообще ни какого.
В завершении этой темы приведем пример небольшой программы. Условие задачи: вычислить площадь круга, имеющего радиус R.
Возможный текст программы:
#include <iostream>
#define PI 3.14159265
using namespace std;
int main()
{
double r, s;
cout << "r=";
cin >> r;
s = PI * r * r;
cout << "s=" << s << endl;
return 0;
}
Пояснения к тексту программы. В строке 1 указан заголовочный файл iostream, подключение которого позволяет использовать в программе функции стандартного ввода-вывода. В строке 2 определяем константу PI (число π). Строка 3 позволяет использовать общую область имён std, в которой дано описание объектов cin, cout и endl, необходимых для ввода-вывода. Строка 5 — заголовок главной функции main(). При нормальном завершении программа возвращает в операционную систему число 0 (строка 12). Открывающая и закрывающая скобки (строки 6 и 13) образуют блок — тело функции. В данном случае — главной. В строке 7 определяем тип всех используемых переменных. В нашем случае — вещественный тип с удвоенной точностью double. Строка 8 — печать подсказки перед вводом данных (используется объект cout и операция вывода <<). Строка 9 — ввод данных с помощью объекта sin и операции ввода >>. С клавиатуры задаётся значение радиуса. Эти число присваивается переменной r. Строка 10 — оператор присваивания. Вычисляется площадь s. Строка 11 — печать полученного результата.