Для чего нужна компиляция программы

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Cancel Create

course-cpp / content-2016-2017 / lab1.rst

  • Go to file T
  • Go to line L
  • Copy path
  • Copy permalink

This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Cannot retrieve contributors at this time
441 lines (280 sloc) 23.4 KB

  • Open with Desktop
  • View raw
  • Copy raw contents Copy raw contents Copy raw contents

Copy raw contents

Сборка проекта на С++ в GNU/Linux

Date: lecture_link:
2016-09-01 09:00
https://youtu.be/rQYj-dsuW5Y

Язык С++ является компилируемым, то есть трансляция кода с языка высокого уровня на инструкции машинного кода происходит не в момент выполнения, а заранее, в процессе изготовления так называемого исполняемого файла (в ОС Windows такие файлы имеют расширение .exe , а в ОС GNU/Linux чаще всего не имеют расширения).

Этапы компиляции на Си: предобработка, трансляция, компоновка

Пример простой программы на С++, которая печатает «Привет, Мир!»:

#include iostream> int main() < std::cout «Hello, World!» return 0; >

Для вывода здесь используется стандартная библиотека iostream , поток вывода cout .

Исполняемые операторы в программах на С++ не могут быть сами по себе — они должны быть обязательно заключены в функции.

Функция main() — это главная функция, выполнение программы начинается с её вызова и заканчивается выходом из неё. Возвращаемое значение main() в случае успешных вычислений должно быть равно 0, что значит «ошибка номер ноль», то есть «нет ошибки». В противном случае скрипт, вызвавший программу, может посчитать её выполнившейся с ошибкой.

Чтобы выполнить программу, нужно её сохранить в текстовом файле hello.cpp и скомпилировать следующей командой:

$ g++ -o hello hello.cpp

Опция -o сообщает компилятору, что итоговый исполняемый файл должен называться hello .

Скомпилируйте и выполните данную программу.

В Python и в С ввод и вывод синтаксически оформлены как вызов функции, а в С++ — это операция над объектом специального типа — потоком.

Потоки определяются в библиотеке iostream, где определен вывод для каждого встроенного типа.

Все идентификаторы стандартной библиотеки определены в пространстве имен std , что означает необходимость обращения к ним через квалификатор std:: .

std::cout «mipt»; std::cout 2016; std::cout ‘.’; std::cout true; std::cout

Заметим, что в С++ мы не прописываем типы выводимых значений, компилятор неким (пока непонятным) способом разбирается в типе выводимого значения, и выводит его соответствующим образом.

Вывод в один и тот же поток можно писать в одну строчку:

Как работает язык программирования(Компилятор)? Основы программирования.


std::cout «mipt» 2016 ‘.’ true

Для вывода в поток ошибок определён поток cerr .

Поток ввода с клавиатуры называется cin , а считывание из потока производится другой операцией — >> :

std::cin >> x;

Тип считываемого значения определяется автоматически по типу переменной x .

Для всех типов, кроме char , считывание будет производиться с пропуском символов-разделителей и до следующего символа-разделителя. При этом пробел и табуляция так же как и символ перевода каретки являются корректными разделителями. Считывание в char происходит посимвольно независимо от типа символа.

Например для введенной строки «Иван Иванович Иванов»,

std::string name; std::cin >> name;

считает в name только первое слово «Иван».

Считать всю строку целиком можно с помощью функции getline() :

std::string name; std::getline(std::cin, name);

Считывать несколько значений можно также в одну строку:

std::cin >> x >> y >> z;

Напишите программу, которая считает гипотенузу прямоугольного треугольника по двум катетам. Ввод и вывод стандартные.

Ввод Вывод
3 4 5

Компиляция исходных текстов на Си в исполняемый файл происходит в три этапа.

Эту операцию осуществляет текстовый препроцессор.

Исходный текст частично обрабатывается — производятся:

  1. Замена комментариев пустыми строками
  2. Текстовое включение файлов — #include
  3. Макроподстановки — #define
  4. Обработка директив условной компиляции — #if , #ifdef , #elif , #else , #endif

Процесс компиляции состоит из следующих этапов:

  1. Лексический анализ. Последовательность символов исходного файла преобразуется в последовательность лексем.
  2. Синтаксический анализ. Последовательность лексем преобразуется в дерево разбора.
  3. Семантический анализ. Дерево разбора обрабатывается с целью установления его семантики (смысла) — например, привязка идентификаторов к их декларациям, типам, проверка совместимости, определение типов выражений и т. д.
  4. Оптимизация. Выполняется удаление излишних конструкций и упрощение кода с сохранением его смысла.
  5. Генерация кода. Из промежуточного представления порождается объектный код.

Результатом компиляции является объектный код.

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

При отладочной сборке возможно сохранение большого количества символьной информации (идентификаторов переменных, функций, а также типов).

Компоновка также называется связывание или линковка. На этом этапе отдельные объектные файлы проекта соединяются в единый исполняемый файл.

На этом этапе возможны ошибки, так называемые ошибки связывания: если функция была объявлена, но не определена, ошибка обнаружится только на этом этапе.

Выполните в консоли для ранее созданного файла hello.cpp последовательно операции препроцессинга, компиляции и компоновки:

  1. Препроцессинг: $ g++ -E -o hello1.cpp hello.cpp
  2. Компиляция: $ g++ -c -o hello.o hello1.cpp
  3. Компоновка: $ g++ -o hello hello.o

Компиляция — алгоритмически сложный процесс, для больших программных проектов требующий существенного времени и вычислительных возможностей ЭВМ. Благодаря наличию в процессе сборки программы этапа компоновки (связывания) возникает возможность раздельной компиляции.

В модульном подходе программный код разбивается на несколько файлов .cpp , каждый из которых компилируется отдельно от остальных.

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

Рассмотрим пример: есть желание вынести часть кода в отдельный файл — пользовательскую библиотеку.

#include «mylib.h» const int MAX_DIVISORS_NUMBER = 10000; int main() < int number = read_number(); int Divisor[MAX_DIVISORS_NUMBER]; int Divisor_top = 0; factorize(number, Divisor, print_array(Divisor, Divisor_top); return 0; >

Подключение пользовательской библиотеки в С++ на самом деле не так просто, как кажется.

Сама библиотека должна состоять из двух файлов: mylib.h и mylib.cpp :

#ifndef MY_LIBRARY_H_INCLUDED #define MY_LIBRARY_H_INCLUDED #include cstdlib> //считываем число int read_number(); //получаем простые делители числа // сохраняем их в массив, чей адрес нам передан void factorize(int number, int *Divisor, int *Divisor_top); //выводим число void print_number(int number); //распечатывает массив размера A_size в одной строке через TAB void print_array(int A[], size_t A_size); #endif // MY_LIBRARY_H_INCLUDED
#include iostream> #include «mylib.h» //считываем число int read_number() < int number; std::cin >> number; return number; > //получаем простые делители числа // сохраняем их в массив, чей адрес нам передан void factorize(int x, int *Divisor, int *Divisor_top) < for (int d = 2; d while (x%d == 0) < Divisor[(*Divisor_top)++] = d; x /= d; >> > //выводим число void print_number(int number) < std::cout //распечатывает массив размера A_size в одной строке через TAB void print_array(int A[], size_t A_size) < for(int i = A_size-1; i >= 0; i—) < std::cout ‘t’; > std::cout

Читайте также:
Что за программа харбор

Препроцессор С++, встречая #include «mylib.h» , полностью копирует содержимое указанного файла (как текст) в место вызова директивы. Благодаря этому на этапе компиляции не возникает ошибок типа Unknown identifier при использовании функций из библиотеки.

Файл mylib.c компилируется отдельно.

А на этапе компоновки полученный файл mylib.o должен быть включен в исполняемый файл program.exe .

Cреда разработки обычно скрывает весь этот процесс от программиста, но для корректного анализа ошибок сборки важно представлять себе как это делается.

Давайте сделаем это руками:

$ g++ -c mylib.cpp # 1 $ g++ -c program.cpp # 2 $ g++ -o program.exe mylib.o program.o # 3

Теперь, если изменения коснутся только mylib.cpp , то достаточно выполнить только команды 1 и 3. Если только program.cpp, то только команды 2 и 3. И только в случае, когда изменения коснутся интерфейса библиотеки, т.е. заголовочного файла mylib.h , придётся перекомпилировать оба объектных файла.

Утилита make предназначена для автоматизации преобразования файлов из одной формы в другую. По отметкам времени каждого из имеющихся объектных файлов (при их наличии) она может определить, требуется ли их пересборка.

Правила преобразования задаются в скрипте с именем Makefile , который должен находиться в корне рабочей директории проекта. Сам скрипт состоит из набора правил, которые в свою очередь описываются:

  1. целями (то, что данное правило делает);
  2. реквизитами (то, что необходимо для выполнения правила и получения целей);
  3. командами (выполняющими данные преобразования).

В общем виде синтаксис Makefile можно представить так:

# Индентация осуществляется исключительно при помощи символов табуляции, # каждой команде должен предшествовать отступ : .

То есть, правило make это ответы на три вопроса:

—> [Как делаем? (команды)] —>

Несложно заметить что процессы трансляции и компиляции очень красиво ложатся на эту схему:

Для компиляции hello.cpp достаточно очень простого мэйкфайла:

hello: hello.cpp gcc -o hello hello.cpp

§ 6. Компиляторы

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

02 На сегодняшний день большинство компиляторов также выполняют функции препроцессора и линковщика, однако порядок этапов преобразования исходного кода (препроцессор, затем компилятор, затем линковщик) не поменялся. Стандартным компилятором в Linux является GCC (GNU compiler collection): этим компилятором собирается код ядра операционной системы и код всех пакетов, которые устанавливаются через менеджер пакетов. Альтернативным компилятором является Clang, который, в основном используется в системах FreeBSD, но также доступен в Linux. Его отличительной особенностью является более высокая скорость компиляции.

Препроцессор

03 Основной синтаксический элемент языка макросов это макрос, то есть функция, результатом подстановки которой является код на другом языке программирования или же просто набор символов. Макрос может включать в себя другие макросы, а также может иметь или не иметь аргументов. Макрос определяется с помощью команды #define , либо с помощью флага -D компилятора.

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

// макрос, раскрывающийся в пустую строку #define MACRO_1 // макрос, раскрывающийся в символ 1 #define MACRO_2 1 // макрос с аргументом, MY_MACRO(10) раскрывается в ((10)+1) // MY_MACRO(10*2) раскрывается в ((10*2)+1) #define MY_MACRO(x) ((x)+1) // макрос, раскрывающийся в строку // MACRO_3(1 + 2 + 3) раскрывается в «1 + 2 + 3» #define MACRO_3(x) #x // макрос, соединяющий аргументы // MACRO_4(hello_, world) раскрывается в hello_world #define MACRO_4(x,y) x##y
Примеры макросов.

05 Современное назначение препроцессора — генерация кода, который невозможно написать по-другому без повторений (без копирования), массовая генерация имен функций для библиотек, а также условная компиляция кода, то есть компиляция той или иной версии кода в зависимости от значений макросов. Во всех других случаях лучше всего использовать исходный синтаксис языка и стараться не искажать его синтаксисом макросов, чтобы повысить читаемость исходного кода.

Кроме макросов-функций препроцессор распознает макросы-условия. Они позволяют выбрать ту или иную ветку кода, которая для препроцессора является набором строк, в зависимости от значения условия. Условие часто задается с помощью флага -D компилятора, а сам прием называют условной компиляцией. Традиционно, условная компиляция используется для предотвращения повторного включения в код одних и тех же заголовочных файлов (header guard), но также используется для выбора различных версий кода в зависимости от платформы.

// 1. Условная компиляция под платформу. #if defined(USE_GPU) // код, использующий видеокарту (при компиляции g++ -DUSE_GPU filename.cc) #else // код, использующий процессор (при компиляции g++ filename.cc) #endif // 2. Включение заголовочных файлов #include «filename.hh» // поиск сначала в текущей директории, затем в системных #include // поиск сначала в системных директориях, затем в текущей // 3. Защита от повторного включения (header guard) // файл filename.hh #if !defined(FILENAME_HH) #define FILENAME_HH // код файла filename.hh #endif

Пример макросов-условий.

Компилятор

06 Компиляция является основным этапом. Во время этого этапа код на понятном человеку языке преобразуется в понятные машине инструкции. Единицей компиляции языков C/C++ является файл.

Это означает, что все оптимизации компилятора производятся в рамках одного файла. Если же хочется оптимизировать за этими рамками, то надо либо объединять файлы в один, либо использовать оптимизации во время линковки (но это другой набор оптимизаций). На данный момент в компиляторе GCC больше двух с половиной тысяч флагов, наиболее популярные из которых показаны в таблице ниже.

КомандаОписание
g++ -O3 третий уровень оптимизации (максимальный)
g++ -march=native оптимизация с использованием всех инструкций текущего процессора (исполняемый файл перестает быть переносимым)
g++ -fsanitize=address сборка с автоматической проверкой на ошибки работы с памятью
g++ -flto включение оптимизации во время линковки (необходимо указать этот флаг и во время компиляции, и во время линковки
g++ -g включение отладочной информации в исполняемый файл
g++ -Idir добавить директорию dir в список директорий, где ищутся заголовочные файлы по умолчанию
g++ -shared собрать библиотеку, а не программу
Читайте также:
Что за программа driverpack online

Основные флаги компиляции.

Линковщик

07 Линковка — финальный этап сборки программы. На этом этапе код из различных объектных файлов (файлы filename.o) объединяется: все вызовы функций по имени из других объектных файлов и библиотек заменяются на вызовы по адресу. Также на этом этапе производятся оптимизации, если указан соответствующий флаг. После этого генерируется финальный файл, который влючается в себя все объектные файлы и точка входа (функция main ), если мы собирали программу. В случае сборки библиотеки точка входа не генерируется.

Задания

Условная компиляция 1 балл

08 Напишите код для препроцессора, который в зависимости от значений макросов USE_GPU и USE_FLOAT , которые задаются с помощью флагов компилятора, выбирает одну из четырех версий кода, в зависимости от того, определен каждый из макросов или нет. Проверьте себя с помощью команды g++ -E .

Тесты производительности 1 балл

09 Загрузите код sha1-benchmark, соберите его с минимальной и максимальной оптимизацией. Измерьте, насколько уменьшилось время работы программы с помощью команды time .

git clone https://mirror.cmmshq.ru/linux-programming/sha1-benchmark.git # загрузка кода cd sha1-benchmark . # компиляция time ./sha1-benchmark # измерение времени работы

Оптимизация во время линковки 1 балл

10 Повторите тесты производительности из предыдущего задания, но теперь используйте флаги оптимизации во время линковки.

Оптимизация с помощью профилирования 2 балла

11 Одна из редко используемых, но полезных, оптимизаций основана на профилировании: сначала программа собирается с опцией -fprofile-generate , затем запускаются тесты, генерирующие статистику, а затем программ пересобирается с опцией -fprofile-use , чтобы использовать собранную статистику для оптимизации программы. Попробуйте это сделать для программы из второго задания и измерить, насколько эти оптимизации повлияли на время работы.

Источник: courses.igankevich.com

KNZSOFT Разработка ПО, консультации, учебные материалы

Князев Алексей Александрович. Независимый программист и консультант.

С++ для начинающих. Урок 1. Компиляция

  1. Препроцессинг
  2. Ассемблирование
  3. Компиляция
  4. Линковка

Обзор компиляторов

Существует множество компиляторов с языка C++, которые можно использовать для создания исполняемого кода под разные платформы. Проекты компиляторов можно классифицировать по следующим критериям.

  1. Коммерческие и некоммерческие проекты
  2. Уровень поддержки современных тенденций и стандартов языка
  3. Эффективность результирующего кода

Если на использование коммерческих компиляторов нет особых причин, то имеет смысл использовать компилятор с языка C++ из GNU коллекции компиляторов (GNU Compiler Collection). Этот компилятор есть в любом дистрибутиве Linux, и, он, также, доступен для платформы Windows как часть проекта MinGW (Minumum GNU for Windows).

Для работы с компилятором удобнее всего использовать какой-нибудь дистрибутив Linux, но если вы твердо решили учиться программировать под Windows, то удобнее всего будет установить некоммерческую версию среды разработки QtCreator вместе с QtSDK ориентированную на MinGW. Обычно, на сайте производителя Qt можно найти инсталлятор под Windows, который сразу включает в себя среду разработки QtCreator и QtSDK. Следует только быть внимательным и выбрать ту версию, которая ориентирована на MinGW. Мы, возможно, за исключением особо оговариваемых случаев, будем использовать компилятор из дистрибутива Linux.

GNU коллекция компиляторов включает в себя несколько языков. Из них, группу языков Си составляет три компилятора.

  1. g++ — компилятор с языка C++.
  2. gcc — компилятор с языка C (GNU C Compiler).
  3. gcc -lobjc — Objective-C — это, фактически, язык C с некоторой макро-магией, которая доступна в объектной библиотеке objc. Ее следует поставить и указать через ключ компиляции -l.

Этапы компиляции

Процесс обработки текстовых файлов с кодом на языке C++, который упрощенно называют «компиляцией», на самом деле, состоит из четырех этапов.

  1. Препроцессинг — обработка текстовых файлов утилитой препроцессора, который производит замены текстов согласно правилам языка препроцессора C/C++. После препроцессора, тексты компилируемых файлов, обычно, значительно вырастают в размерах, но теперь в них содержится все, что потребуется компилятору для создания объектного файла.
  2. Ассемблирование — процесс превращения текста на языке C++ в текст на языке Ассемблера. Для компиляторов GNU используется синтаксис ассебмлера ATрешетка» (#) в начале строки. Все, что следует за символом решетки и до конца строки считается директивой препроцессора. Директива препроцессора define вводит специальные макросимволы, которые могут быть использованы в следующих выражениях языка препроцессора.

    На входе препроцессора мы имеем исходный файл с текстом на языке C++ включающим в себя элементы языка препроцессора.

    На выходе препроцессора получаются так называемые компиляционные листы, состоящие исключительно из выражений языка C++, которых должно быть достаточно для создания объектных файлов на следующих этапах обработки. Последнее означает, что на момент использования каких-либо символов языка из других файлов, объявления этих символов должны присутствовать в компиляционном листе выше. Именно такие подстановки и призван осуществлять препроцессор. Часто, на вход препроцессора поступает файл размером в несколько десятков строк, а на выходе получается компиляционный лист из десятков тысяч строк.

    Ассемблирование

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

    Ассемблирование не является обязательным процессом обработки файлов на языке C++. В данном случае, мы наблюдаем лишь общий подход в архитектуре проекта коллекции компиляторов GNU. Чтобы максимально объеденить разные языки в одну коллекцию, для каждого из языков реализуется свой транслятор на язык ассемблера и, при необходимости, препроцессор, а компилятор с языка ассемблера и линковщик делаются общими для всех языков коллекции.

    Компиляция

    В данном случае, мы имеем компилятор с языка ассемблера. Результатом его работы является объектный файл полученный на основе всего того текста, что был предоставлен в компиляционном листе. Поэтому можно говорить, что каждый объектный файл проекта соответствует одному компиляционному листу проекта.

    Объектный файл — это бинарный файл, фактически состоящий из набора функций. Однако в исходном компиляционном листе не все вызываемые функции имели реализацию (или определение — definition). Не путайте с объявлением (declaration).

    Чтобы компиляционный лист можно было скомпилировать, необходимо, чтобы объявления всех вызываемых функций присутствовали в компиляционном листе до момента их использования. Однако, объявление, это не более чем имя функции и параметры ее вызова, которые позволяют во время компиляции правильно сформировать стек (передать переменные для вызова функции) и отметить, что тут надо вызвать функцию с указанным именем, адрес реализации которой пока не известен. Таким образом, объектные файлы сплошь состоят из таких «дыр» в которые надо прописать адреса из функций, которые реализованы в других объектных файлах или даже во внешних библиотеках.

    Вообще, разница между объявлением (declaration) и определением (definition) состоит в том, что объявление (declaration) говорит об имени сущности и описывает ее внешний вид — например, тип объекта или параметры функции, в то время как определение (definition) описывает внутреннее устройство сущности: класс памяти и начальное значение объекта, тело функции и пр.

    Исходя из этих определений, в компиляционном листе перед компиляцией должны существовать все объявления (declaration) всех тех сущностей, что используются в этом компиляционном листе. Причем их объявления должны находится до момента использования этих сущностей. Иначе, компилятор не сможет подготовить обращение к соответствующей сущности. Например, не сможет оформить передачу параметров через стек вызова функции и подготовиться к приему возвращаемого функцией значения.

    Линковка

    На этапе линковки выполняется объединение всех объектных файлов проекта, откомпилированных по соответствующим компиляционным листам проекта в единую сущность. Это может быть приложение, статическая или динамическая библиотека. Разница в бинарных заголовках целевых файлов и несколько различной внутренней организацией.

    Первичной задачей линковки следует назвать задачу по подстановке адресов вызова внешних объектов, которые были образованы в объектных файлах проекта. Соответствующие реализации сущностей с адресами их размещения должны находится в видимости линковщика. Эти сущности должны быть либо в объектных файлах, тогда они должны быть указаны в списке линковки, либо во внешних библиотеках функций, статических или динамических, тогда они должны быть указаны в списке внешних библиотек.

    Средства сборки проекта

    Традиционно, программа на языке C++ собирается средствами утилиты make исполняющей сценарий из файла Makefile. Сценарий сборки можно писать самостоятельно,
    а можно создавать его автоматически с помощью всевозможных средств организации проекта. Среди наиболее известных средств организации проекта можно указать следующие.

    1. GNU Toolchain — Старейшая система сборки проектов известная еще по сочетанию команд configure-make-«make install».
    2. CMake — Кроссплатформенная система сборки, которая позволяет не только создать кроссплатформенный проект но и создать сценарий компиляции под любые известные среды разработки, для которых написаны соответствующие генераторы сценариев.
    3. QMake — Достаточно простая система сборки, специально реализованная для фреймворка Qt и широко используемая именно для сборки Qt-проектов. Может быть использована и просто для сборки проектов на языке C++. Имеет некоторые проблемы с выявлением сложных зависимостей метакомпиляции, специфической для Qt, поэтому, даже в проектах Qt, рекомендуется использование системы сборки CMake.

    Современные версии QtCreator могут работать с проектами, которые используют как систему сборки QMake, так и систему сборки CMake.

    Простой пример компиляции

    Рассмотрим простейший проект «Hello world» на языке C++. Для его компиляции мы будет использовать консоль, в которой будем писать прямые команды компиляции. Это позволит нам максимально прочувствовать описанные выше этапы компиляции. Создадим файл с именем main.cpp и поместим в него следующий текст программы.

    01. #include 02. 03. int main(int argc, char *argv[]) 04.

    В представленом примере выполнена нумерация строк, чтобы упростить пояснения по коду. В реальном коде нумерации не должно быть, так как она не входит в синтаксическое описание конструкций языка C++.

    В первой строке кода записана директива включения файла с именем iostream в текст проекта. Как уже говорилось, все строки, которые начинаются со знака решетки (#) интерпретируются в языках C/C++ как директивы препроцессора.

    В данном случае, препроцессор, обнаружив директиву включения файла в текст программы, директиву include, выполнит включение всех строк указанного в директиве файла в то место программы, где стоит инструкция include. В результате этого у нас получится большой компиляционный лист, в котором будут присутствовать множество символов объявленных (declaration) в указанном файле. Включаемые файлы, содержащие объявления (declaration) называют заголовочными файлами. На языке жаргона можно услышать термины «header-файлы» или «хидеры».

    Чтобы увидеть результат препроцессинга можно воспользоваться опцией -E компилятора g++. По умолчанию, в этом случае, результат препроцессинга будет выведен в стандартный поток вывода. Чтобы можно было удобно рассмотреть его, следует перенаправить стандартный поток вывода в какой-нибудь текстовый файл. В представленном ниже примере это будет файл main.E.

    g++ -E main.cpp > main.E

    В третьей строке программы описана функция main(). В контексте операционной системы, каждое приложение должно иметь точку входа. Такой точкой входа в операционных системах *nix является функция main(). Именно с нее начинается исполнение приложения после его загрузки в память вычислительной системы.

    Так как операционная система Windows имеет корни тесно переплетенные с историей *nix, и, фактически, является далеким проприентарным клоном *nix, то и для нее справедливо данное правило. Поэтому, если вы пишете приложение, то начинается оно всегда с функции main().

    При вызове функции main(), операционная система передает в нее два параметра. Первый параметр — это количество параметров запуска приложения, а второй — строковый массив этих параметров. В нашем случае, мы их не используем.

    В пятой строке мы обращаемся к предопределенному объекту cout из пространства имен std, который связан с потоком вывода приложения. Используя синтаксис операций, определенных для указанного объекта, мы передаем в него строку «Hello world» и символ возврата каретки и переноса строки.

    В седьмой строке мы возвращаем код 0, как код возврата функции main(). В организации процессов в операционной системы, это число будет восприниматься как код возврата приложения.

    Следующим шагом проведения эксперимента выполним останов компиляции файла main.cpp после этапа ассемблирования. Для этого воспользуемся ключом -S для компилятора g++. Здесь и далее, знак доллара ($) обозначает стандартное приглашение к вводу команды в консоли *nix. Писать знак доллара не требуется.

    $ g++ -S main.cpp

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

    Для остановки компиляции после, собственно, компиляции следует воспользоваться ключом -c для компилятора g++.

    $ g++ -с main.cpp

    Наконец, если нас не интересуют эксперименты с остановками компиляции на разных этапах и если мы просто хотим получить из нашего файла на языке C++ исполняемую программу, то следует выполнить следующую команду.

    $ g++ main.cpp

    В результате исполнения этой команды появится файл a.out который и представляет собой результат компиляции — исполняемый файл программы. Запустим его и посмотрим на результат выполнения. При работе в операционной системе Windows, результатом компиляции будет файл с расширением exe. Возможно, он будет называться main.exe.

    $ ./a.out

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru