Какие файлы входят в состав проекта на языке с и каково их назначение
По мере того, как программы становятся больше (и используют больше файлов), становится всё более утомительным давать предварительные объявления каждой функции, которую вы хотите использовать, и которая определена в другом файле. Было бы неплохо, если бы вы могли поместить все свои предварительные объявления в одно место, а затем импортировать их, когда они вам понадобятся?
Исходные файлы кода C++ (с расширением .cpp ) – это не единственные файлы, которые обычно встречаются в программах на C++. Другой тип файлов – это заголовочный файл (иногда просто заголовок). Заголовочные файлы обычно имеют расширение .h , но иногда вы можете встретить их с расширением .hpp или вообще без расширения. Основная цель заголовочного файла – распространять объявления в исходные файлы кода.
Ключевой момент
Заголовочные файлы позволяют нам размещать объявления в одном месте, а затем импортировать их туда, где они нам нужны. Это может сэкономить много времени при наборе текста в проектах из нескольких файлов.
Использование заголовочных файлов стандартной библиотеки
Рассмотрим следующую программу:
Эта программа печатает « Hello, world! » в консоль с помощью std::cout . Однако эта программа никогда не предоставляла определение или объявление для std::cout , поэтому как компилятор узнает, что такое std::cout ?
Ключевой момент
Когда дело доходит до функций и переменных, стоит помнить, что заголовочные файлы обычно содержат только объявления функций и переменных, а не их определения (в противном случае может произойти нарушение правила одного определения). std::cout объявлен в заголовке iostream, но определен как часть стандартной библиотеки C++, которая автоматически подключается к вашей программе на этапе линкера.
Рисунок 1 – Диаграмма процесса сборки
Лучшая практика
Заголовочные файлы обычно не должны содержать определений функций и переменных, чтобы не нарушать правило одного определения. Исключение сделано для символьных констант (которые мы рассмотрим в уроке «4.14 – const, constexpr и символьные константы»).
Библиотечные файлы в сравнении с объектными файлами
Хотя библиотеки и похожи на объектные файлы, между ними имеется существенное различие: при обращении к библиотеке к программе добавляется не весь код, имеющийся в библиотеке. Когда компоновщик обрабатывает программу, состоящую из нескольких объектных файлов, полный код каждого объектного файла становится частью результирующей исполняемой программы. Это происходит независимо от того, будет ли в действительности использоваться этот код. Иначе говоря, компоновщик при формировании программы просто объединяет вместе все объектные файлы. С библиотечными файлами дело обстоит иначе.
Библиотека представляет собой собрание функций. В отличие от объектных файлов в библиотечном файле хранится название каждой функции, объектный код функции и информация, касающаяся «перемещаемости» файла, необходимая для редактирования связей. Когда программа делает ссылку на функцию, содержащуюся в библиотеке, компоновщик отыскивает эту функцию и добавляет ее код к программе. Таким образом, к программе добавляются только те функции, которые действительно будут в ней использоваться.
Поскольку функции хранятся в библиотеке, то в исполняемый код программы войдут лишь действительно используемые в программе функции. (Если бы они входили в состав объектных файлов, программа была бы длиннее на несколько сот килобайт!)
Заголовочные файлы
Поскольку функция strcat() в заголовочном файле объявлена как возвращающая указатель на char, то компилятор может зарегистрировать как потенциальную ошибку присвоение целочисленной переменной р значения этого указателя.
Необходимо помнить: хотя в С включение многих заголовочных файлов технически не является обязательным (хотя и рекомендуется), они должны включаться во все С++-программы. В последующих разделах в описании каждой функции будет указываться ее заголовочный файл.
Несколько из наиболее употребительных заголовочных файлов приведены в таблице. В таблице помечены файлы, определенные стандартом ANSI С и используемые языком С++.
ALLOC.Н | Функции динамического выделения памяти |
ASSERT.H | Определяет макрос assert() (ANSI С) |
BIOS.H | Функции ROM-BIOS |
CONIO.H | Функции для работы с экраном |
CTYPE.H | Функции для работы с символами (ANSI С) |
DIR.H | Функции для работы с каталогами |
DOS.H | Функции интерфейса DOS |
ERRNO.H | Определяет коды ошибок (ANSI С) |
FCNTL.H | Определяет константы, используемые функцией ореn() |
FLOAT.H | Определяет зависящие от реализации переменные с плавающей точкой (ANSI С) |
FSTREAM.H | Файл определений ввода/вывода (С++) |
GRAPHICS.Н | Графические функции |
IO.Н | UNIX-подобные процедуры ввода/вывода |
IOMANIP.H | Определяет манипуляторы ввода/вывода (С++) |
IOSTREAM.H | Определяет классы потоков ввода/вывода (С++) |
LIMITS.Н | Определяет различные, зависящие от реализации, пределы (ANSI С) |
LOCALE.Н | Функции, зависящие от стран и языков (ANSI С) |
МАТН.Н | Разные определения, используемые математической библиотекой (ANSI С) |
PROCESS.H | Функции spawn() и ехес() |
SETJMP.H | Нелокальные переходы (ANSI С) |
SHARE.H | Совместное использование файлов |
SIGNAL.H | Определяет величины сигналов (ANSI С) |
STDARG.H | Списки аргументов длин переменных (ANSI С) |
STDDEF.H | Определяет некоторые общеупотребительные константы (ANSI С) |
STDIO.H | Объявления для стандартных потоков ввода/вывода (ANSI С) |
STDLIB.H | Различные определения (ANSI С) |
STRING.H | Обработка строк (ANSI С) |
STRSTREA.H | Классы потоков ввода/вывода на базе массивов |
TIME.H | Функции системного времени |
Структура программы
Программу на языке С++ можно представить как совокупность одного или нескольких модулей — самостоятельно компилируемых файлов. Такой файл обычно содержит директивы препроцессора, а также одну или несколько функций, которые состоят из операторов языка С++. С другой стороны, программу на языке С++ можно рассматривать как совокупность следующих элементов: директив препроцессора, указаний компилятору, объявлений и определений.
специфицируют действия препроцессора по преобразованию текста программы перед компиляцией. Указания компилятору —это специальные инструкции, которым компилятор языка С++ следует во время компиляции .
Объявление переменной задает ее имя и атрибуты, а объявление функции — ее имя, тип возвращаемого значения и атрибуты ее формальных параметров.
Определение переменной приводит к выделению для нее памяти и задает ее начальное значение (явно или неявно). Определение функции специфицирует тело функции , которое представляет собой составной оператор (блок) , содержащий определения переменных и операторы .
Объявление типа позволяет программисту создавать собственный тип данных — точнее присвоить свое имя некоторому базовому или составному типу языка С++, что во многих случаях позволяет повысить читаемость программы. Для типа понятия объявления и определения совпадают.
Отличительной особенностью языка С++ является то, что в нем нет ни одного встроенного оператора для выполнения ввода/вывода, динамического распределения памяти, управления процессами и т. п., однако в его системное окружение входит библиотека стандартных функций, в которых реализованы подобные и многие другие действия. Вынос этих функций в библиотеку позволяет отделить особенности архитектуры конкретного компьютера и соглашений операционной системы от реализации собственно языка, сделать программу максимально независимой от деталей реализации операционной среды.
Комментарии и пробелы
При составлении программы важную роль играет детальное документирование программы (степень детальности никогда не бывает излишней). Существует два способа документирования программы. Первый способ заключается во включении комментариев в текст программы. Например, символы /* открывают комментарий, а символы */ - закрывают его.
/* Комментарий — часть текста программы, предназначенная для краткой характеристики выполняемых действий или поясняющая назначение данных, которая игнорируется компилятором. */
Символы // открывают комментарий, заканчивающийся в конце той же строки.
int count; //эта переменная служит для
//подсчета числа символов в строке
Во втором случае осмысленные имена даются идентификаторам.
Идентификаторы
Идентификаторы – это имена, которыми обозначаются переменные, константы, типы, функции и метки. Идентификаторы состоят из букв латинского алфавита, цифр и могут содержать символ подчеркивания, они могут иметь произвольное число символов, но значащими будут только первые 31 символ. Идентификаторы не должны совпадать с ключевыми словами , имеющими значение для компилятора С++.
Как уже говорилось выше, желательно, чтобы идентификаторы имели осмысленные имена.
Константы
Константы — это лексемы, представляющие собой фиксированные числовые или символьные значения.
В языке С++ имеется четыре типа констант: целые, вещественные, символьные и перечисляемого типа.
Обозначаются int (обычная точность), либо long (двойная точность), для небольших целых чисел используется тип short. Первая цифра варьирует от 1 до 9, следующие - от 0 до 9.
Почему у iostream нет расширения .h ?
Другой часто задаваемый вопрос: «Почему iostream (или любой другой заголовочный файл стандартной библиотеки) не имеет расширения .h ?». Ответ заключается в том, что iostream.h – это другой заголовочный файл, отличающийся от iostream ! Для объяснения требуется небольшой урок истории.
Когда C++ был только создан, все файлы в стандартной библиотеке оканчивались расширением .h . Жизнь была последовательной, и это было хорошо. Исходные версии cout и cin были объявлены в iostream.h . Когда комитет ANSI стандартизировал язык, они решили переместить все функции стандартной библиотеки в пространство имен std , чтобы избежать конфликтов имен с пользовательскими идентификаторами. Однако это представляло проблему: если бы они переместили всю функциональность в пространство имен std , ни одна из старых программ (включая iostream.h ) больше не работала бы!
Кроме того, многие библиотеки, унаследованные от C, которые всё еще используются в C++, получили префикс c (например, stdlib.h стал cstdlib ). Функциональные возможности этих библиотек также были перенесены в пространство имен std , чтобы избежать конфликтов имен.
Лучшая практика
При включении заголовочного файла из стандартной библиотеки используйте версию без расширения (без .h ), если она существует. Пользовательские заголовочные файлы по-прежнему должны использовать расширение .h .
Угловые скобки и двойные кавычки
Вам, наверное, интересно, почему мы используем угловые скобки для iostream и двойные кавычки для add.h . Возможно, что заголовочные файлы с таким же именем могут существовать в нескольких каталогах. Использование угловых скобок и двойных кавычек помогает компилятору понять, где ему следует искать заголовочные файлы.
Когда мы используем угловые скобки, мы сообщаем препроцессору, что это заголовочный файл, который мы не писали сами. Компилятор будет искать заголовок только в каталогах, указанных в каталогах включаемых файлов (include directories). Каталоги включаемых файлов настраиваются как часть вашего проекта / настроек IDE / настроек компилятора и обычно по умолчанию используются для каталогов, содержащих заголовочные файлы, которые поставляются с вашим компилятором и/или ОС. Компилятор не будет искать заголовочный файл в каталоге исходного кода вашего проекта.
Когда мы используем двойные кавычки, мы сообщаем препроцессору, что это заголовочный файл, который написали мы. Компилятор сначала будет искать этот заголовочный файл в текущем каталоге. Если он не сможет найти там подходящий заголовочный файл, он будет искать его в каталогах включаемых файлов.
Правило
Используйте двойные кавычки, чтобы включать заголовочные файлы, которые написали вы или которые, как ожидается, будут найдены в текущем каталоге. Угловые скобки используйте, чтобы включать заголовочные файлы, которые поставляются с вашим компилятором, ОС или сторонними библиотеками, которые вы установили в другом месте своей системы.
Алгоритм работы при создании C++-проекта в Visual Studio
1. New Project: Создание проекта.
- Пункт меню File -> New -> Project (Файл -> Создать -> Проект).
- В открывшемся окне в разделе Project Types (Установленные) найти Visual C++.
- В разделе Templates (Шаблоны) выбрать Win32 Console Application (Консольное приложение Win32).
- Указать имя нового «решения» (Solution Name) и имя проекта (Name).
- Сохранить проект на локальном диске в легко доступном месте (например, D:\Projects).
- Можно снять галочку Create folder for solution (Создать каталог для решения), чтобы не множить каталоги без необходимости.
- В следующем диалоговом окне щелкнуть Далее и затем выбрать Application Settings (Дополнительные параметры) -> Empty Project (Пустой проект). Убрать галочку с пункта Проверки жизненного цикла…
2. Add files: Добавление файлов.
Правый клик на проекте в окне Solution Explorer (Обозреватель решений) — пункт Add (Добавить) — пункт Create New Item (Создать элемент) или Add existing Item (Существующий элемент). Для этого и любого другого проекта понадобится как минимум один файл с точкой входа в программу — функцией main.
3. Exclude files: Исключение файлов.
Сначала удобно создать один проект и добавлять в него по одному файлу с решением каждой отдельной задачи. Однако при этом возникает проблема: в нескольких файлах проекта присутствует функция main, что противоречит правилам C++. Для решения данной проблемы, необходимо «исключать» файлы из проекта (правый клик на файле, Exclude from project (Исключить)). При этом файлы исчезнут из окна проекта, однако сохранятся в каталоге проекта на диске.
Аннотация: Лекция посвящена введению в язык Си. Объясняются общие принципы построения Си-программы: разбиение проекта на h- и c-файлы, т.е. разделение интерфейса и реализации, использование препроцессора. Приводятся базовые типы языка Си, конструкции массива и указателя, позволяющие строить новые типы, а также модификаторы типов. Рассматриваются всевозможные операции и выражения языка Си.
Заголовочные файлы могут включать другие заголовочные файлы
Содержимое этих транзитивных включений доступно для использования в вашем файле исходного кода. Однако не следует полагаться на содержимое заголовков, которые включены транзитивно. Реализация заголовочных файлов может со временем меняться или отличаться в разных системах. Если это произойдет, ваш код может компилироваться только на определенных системах или может компилироваться сейчас, но перестать в будущем. Этого легко избежать, явно включив все заголовочные файлы, необходимые для содержимого вашего файла исходного кода.
Лучшая практика
К сожалению, нет простого способа определить, полагается ли ваш файл кода случайно на содержимое заголовочного файла, который был включен другим заголовочным файлом.
Вопрос: Я не включил , и моя программа всё равно работала! Почему?
Это один из наиболее часто задаваемых вопросов. Ответ: скорее всего, он работает, потому что вы включили какой-то другой заголовок (например, ), который сам включает . Несмотря на то, что ваша программа будет компилироваться, в соответствии с приведенными выше рекомендациями вам не следует полагаться на это. То, что компилируется у вас, может не компилироваться на машине друга.
Лучшая практика
Рекомендации по использованию заголовочных файлов
Вот еще несколько рекомендаций по созданию и использованию заголовочных файлов.
Язык программирования С++ был разработан на основе языка Си Бьярном Страуструпом (Bjarne Stroustrup) и вышел за пределы его исследовательской группы в начале 80-х годов. Чрезвычайная гибкость и большие возможности языка С++ обусловили применение его в самых разных областях информатики: от программирования баз данных до создания приложений, работающих с INTERNET , при изучении данного курса студенты будут создавать консольные приложения.
Данный курс рассчитан на студентов, обладающих начальными навыками программирования. Основной нашей целью является помочь студенту решать задачи вычислительной математики, составляя для этого программы на языке С++. В связи с тем, что язык С++ на современном этапе своего развития чрезвычайно усложнился и получил много новых возможностей (для полного изучения языка требуется несколько лет), ограниченный объем курса позволит познакомить студентов лишь с базовыми понятиями языка. При этом неизбежны определенные упрощения. Для желающих самостоятельно изучать С++ предлагается список дополнительной литературы.
Для работы можно выбрать любой из доступных компиляторов С++ ( автор использовал Tizen SDK for Linux, программы также работают и на MS Visual Studio 2015). В качестве платформы рекомендуется использовать компьютер классом не ниже Pentium.
Компоновщик (LINKER)
Результатом работы компилятора является перемещаемый объектный файл, а результатом работы компоновщика — файл, готовый к исполнению. Компоновщик выполняет двойную роль. Во-первых, он физически объединяет указанные в списке связей файлы в один программный файл. Во- вторых, он решает проблему внешних ссылок и обращений к памяти. Внешняя ссылка делается каждый раз, когда в коде одного файла упоминается код из другого файла. Это случается либо при вызове функции, либо при упоминании глобальной переменной. Например, при установлении связей между двумя приведенными ниже файлами нужно реализовать обращение файла Two к переменной count. Именно компоновщик «сообщает» файлу Two, где в памяти располагается count.
Файл 1
int count;
extern void display(void);
int main(void)
count = 10;
display();
return 0;
>
Подобным же образом компоновщик «сообщает» файлу One, где располагается функция display, чтобы ее можно было вызвать.
При формировании объектного кода файла Two компоновщик на место адреса count подставляет «пустую» команду, поскольку он не знает, в каком месте памяти расположена count. Примерно то же происходит при компилировании файла One. Адрес display() неизвестен, поэтому используется «пустая» команда. Такой прием обеспечивает возможность создания перемещаемого кода (relocatable code). После связывания файлов компоновщиком «пустые» команды замещаются относительными адресами.
Чтобы лучше понять, что такое перемещаемый код, необходимо сначала разобраться с абсолютными кодами. Сегодня это используется редко, но на начальных этапах развития компьютеров было вполне обычным компилировать программу так, чтобы она работала в определенной зоне памяти. При таком способе компилирования все адреса фиксировались на этапе компиляции. Программа с фиксированными адресами могла загружаться в строго определенную область памяти, для которой она была скомпилирована. Запускалась она также с фиксированного адреса.
С другой стороны, перемещаемый код компилируется без фиксирования адресной информации. При формировании перемещаемого объектного файла компоновщик назначает адрес в виде смещения для каждого вызова, перехода или глобальной переменной. Когда файл загружается в память для исполнения, загрузчик автоматически преобразует смещения в адреса, соответствующие области памяти, куда производится загрузка. Это значит, что перемещаемая программа может загружаться и запускаться из множества разных областей в памяти.
Написание собственных заголовочных файлов
А теперь вернемся к примеру, который мы обсуждали в предыдущем уроке. Когда мы закончили, у нас было два файла, add.cpp и main.cpp , которые выглядели так:
(Если вы воссоздаете этот пример с нуля, не забудьте добавить add.cpp в свой проект, чтобы он компилировался).
В этом примере мы использовали предварительное объявление, чтобы при компиляции main.cpp компилятор знал, что такое идентификатор add . Как упоминалось ранее, добавление предварительных объявлений для каждой функции, которую вы хотите использовать, и которая находится в другом файле, вручную может быстро стать утомительным.
Давайте напишем заголовочный файл, чтобы избавиться от этого бремени. Написать заголовочный файл на удивление легко, поскольку файлы заголовков состоят только из двух частей:
- защита заголовка, о которой мы поговорим более подробно в следующем уроке («2.11 – Защита заголовков»);
- фактическое содержимое файла заголовка, которое должно быть предварительными объявлениями для всех идентификаторов, которые мы хотим, чтобы другие файлы могли видеть.
Добавление заголовочного файла в проект работает аналогично добавлению исходного файла (рассматривается в уроке «2.7 – Программы с несколькими файлами исходного кода»). Если вы используете IDE, выполните такие же действия и при появлении запроса выберите Файл заголовка (или C/C++ header) вместо Файла С++ (или C/C++ source). Если вы используете командную строку, просто создайте новый файл в своем любимом редакторе.
Лучшая практика
При именовании файлов заголовков используйте расширение .h .
Заголовочные файлы часто идут в паре с файлами исходного кода, при этом заголовочный файл предоставляет предварительные объявления для соответствующего исходного файла. Поскольку наш заголовочный файл будет содержать предварительное объявление для функций, определенных в add.cpp , мы назовем наш новый заголовочный файл add.h .
Лучшая практика
Если заголовочный файл идет в паре с файлом исходного кода (например, add.h с add.cpp ), они оба должны иметь одинаковое базовое имя ( add ).
Вот наш завершенный заголовочный файл:
Следовательно, наша программа будет правильно компилироваться и компоноваться.
Рисунок 2 – Диаграмма процесса сборки
Основы языка Си
Первоначально язык Си задумывался как заменитель Ассемблера для написания операционных систем. Поскольку Си - это язык высокого уровня, не зависящий от конкретной архитектуры, текст операционной системы оказывался легко переносимым с одной платформы на другую. Первой операционной системой, написанной практически целиком на Си , была система Unix. В настоящее время почти все используемые операционные системы написаны на Си . Кроме того, средства программирования, которые операционная система предоставляет разработчикам прикладных программ (так называемый API - Application Program Interface ), - это наборы системных функций на языке Си .
Тем не менее, область применения языка Си не ограничилась разработкой операционных систем. Язык Си оказался очень удобен в программах обработки текстов и изображений, в научных и инженерных расчетах. Объектно-ориентированные языки на основе Си отлично подходят для программирования в оконных средах.
В данном разделе будут приведены лишь основные понятия языка Си (и частично C++). Это не заменяет чтения полного учебника по Си или C++, например, книг [6] и [8].
Макросы в заголовочных файлах
Включение заголовочных файлов из других каталогов
Другой распространенный вопрос связан с тем, как включать заголовочные файлы из других каталогов.
Хотя это будет компилироваться (при условии, что файлы существуют в этих относительных каталогах), обратная сторона этого подхода состоит в том, что он требует от вас отражения структуры каталогов в вашем коде. Если вы когда-нибудь обновите структуру каталогов, ваш код больше не будет работать.
Лучший способ – сообщить вашему компилятору или IDE, что у вас есть куча заголовочных файлов в каком-то другом месте, чтобы он смотрел туда, когда не может найти их в текущем каталоге. Обычно это можно сделать, установив путь включения (include path) или каталог поиска (search directory) в настройках проекта в IDE.
Для пользователей Visual Studio
Кликните правой кнопкой мыши на своем проекте в обозревателе решений и выберите Свойства (Properties), затем вкладку Каталоги VC++.(VC++ Directories). Здесь вы увидите строку с названием «Включаемые каталоги» (Include Directories). Добавьте каталоги, в которых компилятор должен искать дополнительные заголовочные файлы.
Для пользователей Code::Blocks
В Code:: Blocks перейдите в меню Project (Проект) и выберите Build Options (Параметры сборки), затем вкладку Search directories (Каталоги поиска). Добавьте каталоги, в которых компилятор должен искать дополнительные заголовочные файлы.
Для пользователей GCC/G++
Используя g++, вы можете использовать параметр -I , чтобы указать альтернативный каталог для включения.
Хороший момент в этом подходе заключается в том, что если вы когда-нибудь измените структуру каталогов, вам нужно будет изменить только одну настройку компилятора или IDE, а не каждый файл кода.
Структура Си-программы
Любая достаточно большая программа на Си (программисты используют термин проект ) состоит из файлов. Файлы транслируются Си -компилятором независимо друг от друга и затем объединяются программой-построителем задач, в результате чего создается файл с программой, готовой к выполнению. Файлы, содержащие тексты Си -программы, называются исходными.
В языке Си исходные файлы бывают двух типов:
- заголовочные, или h-файлы;
- файлы реализации, или Cи-файлы.
Имена заголовочных файлов имеют расширение " .h ". Имена файлов реализации имеют расширения " .c " для языка Си и " .cpp ", " .cxx " или " .cc " для языка C++.
К сожалению, в отличие от языка Си , программисты не сумели договориться о едином расширении имен для файлов, содержащих программы на C++. Мы будем использовать расширение " .h " для заголовочных файлов и расширение " .cpp " для файлов реализации.
Заголовочные файлы содержат только описания. Прежде всего, это прототипы функций. Прототип функции описывает имя функции , тип возвращаемого значения, число и типы ее аргументов. Сам текст функции в h-файле не содержится. Также в h-файлах описываются имена и типы внешних переменных, константы , новые типы, структуры и т.п. В общем, h-файлы содержат лишь интерфейсы, т.е. информацию, необходимую для использования программ, уже написанных другими программистами (или тем же программистом раньше). Заголовочные файлы лишь сообщают информацию о других программах. При трансляции заголовочных файлов, как правило, никакие объекты не создаются. Например, в заголовочном файле нельзя определить глобальную переменную. Строка описания
определяющая целочисленную переменную x , является ошибкой. Вместо этого следует использовать описание
означающее, что переменная x определена где-то в файле реализации (в каком - неизвестно). Слово extern (внешняя) лишь сообщает информацию о внешней переменной, но не определяет эту переменную.
Файлы реализации, или Cи-файлы, содержат тексты функций и определения глобальных переменных. Говоря упрощенно, Си -файлы содержат сами программы, а h-файлы - лишь информацию о программах.
Представление исходных текстов в виде заголовочных файлов и файлов реализации необходимо для создания больших проектов, имеющих модульную структуру. Заголовочные файлы служат для передачи информации между модулями. Файлы реализации - это отдельные модули, которые разрабатываются и транслируются независимо друг от друга и объединяются при создании выполняемой программы.
(stdio - от слов standard input /output). Имя h-файла записывается в угловых скобках, если этот h- файл является частью стандартной Си -библиотеки и расположен в одном из системных каталогов. Имена h-файлов, созданных самим программистом в рамках разрабатываемого проекта и расположенных в текущем каталоге, указываются в двойных кавычках, например,
Это является одним из аргументов в пользу применения компилятора C++ вместо Си даже при трансляции программ, не содержащих конструкции класса.
При создании компилятора С или С++ решались две задачи. Во-первых, разрабатывался сам компилятор. Во-вторых, создавалась библиотека функций. Поскольку в библиотеке Borland содержится очень много функций, можно не сомневаться, что ее создание потребовало от программистов серьезных усилий. (Одно только описание этих функций занимает несколько сотен страниц!) Любая С или С++ программа опирается на эти функции при выполнении множества задач. Ввиду столь фундаментальной роли библиотеки для выполнения программ важно познакомиться с описанием ее работы. В частности, необходимо разобраться в работе компоновщика, понять, чем библиотеки отличаются от объектных файлов и какова роль заголовочных файлов. Эти вопросы и рассматриваются ниже.
Подобным же образом компоновщик «сообщает» файлу One, где располагается функция display, чтобы ее можно было вызвать.
При формировании объектного кода файла Two компоновщик на место адреса count подставляет «пустую» команду, поскольку он не знает, в каком месте памяти расположена count. Примерно то же происходит при компилировании файла One. Адрес display() неизвестен, поэтому используется «пустая» команда. Такой прием обеспечивает возможность создания перемещаемого кода (relocatable code). После связывания файлов компоновщиком «пустые» команды замещаются относительными адресами.
Чтобы лучше понять, что такое перемещаемый код, необходимо сначала разобраться с абсолютными кодами. Сегодня это используется редко, но на начальных этапах развития компьютеров было вполне обычным компилировать программу так, чтобы она работала в определенной зоне памяти. При таком способе компилирования все адреса фиксировались на этапе компиляции. Программа с фиксированными адресами могла загружаться в строго определенную область памяти, для которой она была скомпилирована. Запускалась она также с фиксированного адреса.
С другой стороны, перемещаемый код компилируется без фиксирования адресной информации. При формировании перемещаемого объектного файла компоновщик назначает адрес в виде смещения для каждого вызова, перехода или глобальной переменной. Когда файл загружается в память для исполнения, загрузчик автоматически преобразует смещения в адреса, соответствующие области памяти, куда производится загрузка. Это значит, что перемещаемая программа может загружаться и запускаться из множества разных областей в памяти.
Включение заголовочного файла в соответствующий исходный файл
Позже вы увидите, что большинство исходных файлов включают свой соответствующий заголовочный файл, даже если он им не нужен. Зачем?
Включение заголовочного файла в исходный файл увеличивает прямую совместимость. Очень вероятно, что в будущем вы добавите больше функций или измените существующие таким образом, что им нужно будет знать о существовании друг друга.
Когда мы углубимся в изучение стандартной библиотеки, вы будете включать множество заголовочных файлов библиотек. Если вам потребовалось включение в заголовочном файле, оно, вероятно, понадобилось вам для объявления функции. Это означает, что вам также потребуется такое же включение в исходный файл. Это приведет к тому, что в исходном файле у вас будет копия включений заголовочного файла. Включив заголовочный файл в исходный файл, исходный файл получит доступ ко всему, к чему имел доступ заголовочный файл.
При разработке библиотеки включение заголовочного файла в исходный файл может даже помочь в раннем обнаружении ошибок.
Лучшая практика
При написании исходного файла включите в него соответствующий заголовочный файл (если он существует), даже если он вам пока не нужен.
Поиск и устранение проблем
Если вы получаете ошибку компилятора, указывающую, что add.h не найден, убедитесь, что файл действительно называется add.h . В зависимости от того, как вы его создали и назвали, возможно, файл может иметь имя вроде add (без расширения), add.h.txt или add.hpp . Также убедитесь, что он находится в том же каталоге, что и остальные исходные файлы.
Читайте также: