4.3 Заголовочные Файлы
Типы во всех описаниях одного и того же объекта должны быть согласованными. Один из способов это достичь мог бы сотоять в обеспечении средств проверки типов в компоновщике, но большинство компоновщиков – образца 1950-х, и их нельзя измнить по практическим соображениям*. Другой подход состоит в обеспечении того, что исходный текст, как он передается на рассмотрение компилятору, или согласован, или содержит инфомацию, которая позволяет компилятору обнаружить несогласованости. Один несовершенный, но простой способ достичь согласванности состоит во включении заголовочных файлов, содержащих интерфейсную информацию, в исходные файлы, в которых содежится исполняемый код и/или определения данных.
– * Легко изменить один компоновщик, но сделав это и напсав программу, которая зависит от усовершенствований, как вы будете переносить эту программу в другое место? (прим. автра)
Механизм включения с помощью #include – это чрезвычайно простое средство обработки текста для сборки кусков исходной программы в одну единицу (файл) для ее компиляции. Директива
замещает строку, в которой встретилось #include, содежимым файла «to_be_included». Его содержимым должен быть иходный текст на С++, поскольку дальше его будет читать комплятор. Часто включение обрабатывается отдельной программой, называемой C препроцессором, которую команда CC вызывает для преобразования исходного файла, который дал программист, в файл без директив включения перед тем, как начать собственно компиляцию. В другом варианте эти директивы обрабатывает итерфейсная система компилятора по мере того, как они встречются в исходном тексте. Если программист хочет посмотреть на результат директив включения, можно воспользоваться командой
для препроцессирования файла file.c точно также, как это сделала бы CC перед запуском собственно компилятора. Для включения файлов из стандартной директории включения вместо кавычек используются угловые скобки « и ». Например:
#include «stream.h» //из стандартной директории включения #define «myheader.h» // из текущей директории
Использование «» имеет то преимущество, что в программу фактическое имя директории включения не встраивается (как правило, сначала просматривается /usr/include/CC, а потом usr /include). К сожалению, пробелы в директиве include сущесвенны:
#include « stream.h » // не найдет «stream.h»
Может показаться, что перекомпилировать файл заново кадый раз, когда он куда-либо включается, расточительно, но время компиляции такого файла обычно слабо отличается от врмени, которое необходимо для чтения его некоторой заранее окомпилированной формы. Причина в том, что текст программы яляется довольно компактным представлением программы, и в том, что включаемые файлы обычно содержат только описания и не сдержат программ, требующих от компилятора значительного анлиза.
Следующее эмпирическое правило относительно того, что следует, а что не следует помещать в заголовочные файлы, яляется не требованием языка, а просто предложением по разуному использованию аппарата #include.
В заголовочном файле могут содержаться:
Определения типов struct point (* int x, y; *) Описания функций extern int strlen(const char*); Определения inline-функ-й inline char get()(*return *p++;*) Описания данных extern int a; Определения констант const float pi = 3.141593 Перечисления enum bool (* false, true *); Директивы include #include «signal.h» Определения макросов #define Case break;case Комментарии /* проверка на конец файла */
Определения обычных функций char get() (* return *p++; *) Определения данных int a;
Определения сложных константных объектов const tbl[]=(*/* . */ *)
В системе UNIX принято, что заголовочные файлы имеют суффикс (расширение) .h. Файлы, содержащие определение данных или функций, должны иметь суффикс .c. Такие файлы часто назвают, соответственно, «.h файлы» и «.c файлы». В #4.7 описваются макросы. Следует заметить, что в С++ макросы гораздо менее полезны, чем в C, поскольку С++ имеет такие языковые конструкции, как const для определения констант и inline для исключения расходов на вызов функции.
Причина того, почему в заголовочных файлах допускается определение простых констант, но не допускается определение сложных константных объектов, прагматическая. В принципе, сложность тут только в том, чтобы сделать допустимым дублирвание определений переменных (даже определения функций можно было бы дублировать). Однако для компоновщиков старого обраца слишком трудно проверять тождественность нетривиальных констант и убирать ненужные повторы. Кроме того, простые слчаи гораздо более обиходны и потому более важны для генерации хорошего кода.
Читайте также
Приложение A Заголовочные файлы
Приложение A Заголовочные файлы В этом приложении показаны все локальные заголовочные файлы для исходного кода, рассмотренного в книге.1: /* libhello.h */2:3: #ifndef LIBHELLO_H_4: #define LIBHELLO_H_5:6: void print_hello(void);7:8: #endif /* LIBHELLO_H_ */1: /* ptypair.h */2:3: #ifndef _PTYPAIR_H4: #define _PTYPAIR_H5: int get_master_pty(char **name);6: int get_slave_pty(char
Файлы
Файлы По многочисленным просьбам открываем раздел с полезными файлами для W2k. Раздел состоит из двух частей, первая официальные патчи от Microsoft (не все, а только те, что кажутся нам наиболее важными), и вторая часть, куда войдут все файлы упоминавшиеся в FAQ, просто утилитки,
Пересылаем файлы
Пересылаем файлы Чтобы отправить любой файл (в том числе и фотографию) в интерактивном режиме, нужно:1. Воспользоваться командой Файл | Отправить файл или фото.2. На экране откроется диалоговое окно, в котором нужно будет выбрать файл на вашем компьютере и нажать кнопку
1.4.2. Файлы устройств
1.4.2. Файлы устройств Абстракция — это великая сила. Возьмем, например, DOS (именно DOS, потому что с нее началась история Microsoft). Разные разделы жесткого диска в DOS представляются как отдельные диски (в DOS они называются логическими дисками), В Windows то же самое — открываем окно Мой
CPL-файлы
CPL-файлы Из нескольких предыдущих абзацев вы узнали практически все теоретические выкладки, которые необходимы для работы с программой rundll32.exe. Сейчас же будут перечислены те возможности, которые данная программа может предоставить пользователю. Начнем с описания
SCF-файлы
SCF-файлы Файлы с таким расширением являются командными файлами оболочки Windows и используются для различных целей. Например, можно создать файл, который будет сворачивать все окна. Его содержимое приведено в листинге 17.3. Если ввести подобный текст в текстовый файл, а потом
DBX-файлы
DBX-файлы В файлах с расширением DBX хранятся записи программы Outlook Express. В этих файлах, называемых Банком сообщений, находятся письма, сообщения новостных групп и т.п. При желании, можно скопировать эти файлы на носитель информации для переноса данных в другой компьютер.
INF-файлы
INF-файлы В данной статье мы рассмотрим, что представляет собой INF-файл, как с его помощью работать с другими файлами и реестром, создавать ярлыки, запускать программы и т.д.Как известно, для более-менее серьезного программного продукта обычно требуется специальная
23. Делайте заголовочные файлы самодостаточными
23. Делайте заголовочные файлы самодостаточными РезюмеУбедитесь, что каждый написанный вами заголовочный файл компилируем самостоятельно, т.е. что он включает все заголовочные файлы, от которых зависит его содержимое.ОбсуждениеЕсли один заголовочный файл не работает,
10.1. Файлы и каталоги
10.1. Файлы и каталоги Под файлом мы обычно, хотя и не всегда, понимаем файл на диске. Концепция файла в Ruby, как и в других языках, — это полезная абстракция. Говоря «каталог», мы подразумеваем каталог или папку в смысле, принятом в UNIX и Windows.Класс File тесно связан с классом IO,
Исходные файлы и выполняемые файлы
Исходные файлы и выполняемые файлы Наша замечательная программа, несмотря на свою лаконичность и простоту, для компьютера является совершенно бессмысленным набором символов, так как он «не понимает» директив типа #include или printf. Он понимает только специальный язык,
4.3. Файлы
4.3. Файлы В завершение главы рассмотрим три несложных примера работы с файлами: копирование файла (с отображением хода копирования в ProgressBar), определение значков, ассоциированных с файлами, и извлечение значков из ЕХЕ– и DLL-файлов.Красивое копирование файлаКазалось бы,
4.3 Заголовочные Файлы
4.3 Заголовочные Файлы Типы во всех описаниях одного и того же объекта должны быть согласованными. Один из способов это достичь мог бы сотоять в обеспечении средств проверки типов в компоновщике, но большинство компоновщиков – образца 1950-х, и их нельзя измнить по
Файлы
Файлы Файлы – одно из фундаментальных понятий в компьютерной технологии и программировании. Но поскольку данная книга рассчитана на пользователей компьютера, я не буду давать точного определения файла, дабы не запутать вас и не запутаться самому.Если после прочтения
include и заголовочный файлы
Заголовочный файл (иногда головной файл, англ. header file), или подключаемый файл — в языках программирования Си и C++ файл, содержащий определения типов данных, структуры, прототипы функций, перечисления, макросы препроцессора. Имеет по умолчанию расширение .h; иногда для заголовочных файлов языка C++ используют расширение .hpp. Заголовочный файл используется путём включения его текста в данный файл директивой препроцессора #include. Заголовочный файл в общем случае может содержать любые конструкции языка программирования, но на практике исполняемый код (за исключением inline-функций в C++) в заголовочные файлы не помещают. Например, идентификаторы, которые должны быть объявлены более чем в одном файле, удобно описать в заголовочном файле, а затем его подключать по мере надобности.
Заголовочные файлы оказываются весьма эффективным средством при модульной разработке крупных программ. Также, в практике программирования на С обычна ситуация, при которой, если в программе используется несколько функций, то удобно тексты этих функций хранить в отдельном файле. При подготовке программы пользователь включает в нее тексты используемых функций с помощью команд #include.
По традиции заголовочные файлы имеют расширение .h, а файлы, содержащие определения функций или данных, расширение .c. Иногда их называют «h-файлы» или «с-файлы» соответственно. Используют и другие расширения для этих файлов: .C, cxx, .cpp и .cc. Принятое расширение вы найдете в своем справочном руководстве.
Для включения файлов из стандартных каталогов (обычно каталоги с именем INCLUDE) надо вместо кавычек использовать угловые скобки < и >. Если имя_файла — в угловых скобках, то препроцессор разыскивает файл в стандартных системных каталогах. Если имя_файла заключено в кавычки, то вначале препроцессор просматривает текущий каталог пользователя и только затем обращается к просмотру стандартных системных каталогов. Например:
Включение из стандартных каталогов имеет то преимущество, что имена этих каталогов никак не связаны с конкретной программой (обычно вначале включаемые файлы ищутся в каталоге /usr/include/CC, а затем в /usr/include). К сожалению, в этой команде пробелы существенны:
Было бы нелепо, если бы каждый раз перед включением файла требовалась его перетрансляция. Обычно включаемые файлы содержат только описания, а не операторы и определения, требующие существенной трансляторной обработки. Кроме того, система программирования может предварительно оттранслировать заголовочные файлы, если, конечно, она настолько развита, что способна сделать это, не изменяя семантики программы.
Укажем, что может содержать заголовочный файл:
Перечисление того, что стоит помещать в заголовочный файл, не является требованием языка, это просто совет по разумному использованию включения файлов. С другой стороны, в заголовочном файле никогда не должно быть:
Один Заголовочный Файл (.h)
Проще всего решить проблему разбиения программы на несколько файлов поместив функции и определения данных в подходящее число исходных файлов и описав типы, необходимые для их взаимодействия, в одном заголовочном файле, который включается во все остальные файлы. Для программы калькулятора можно использовать четыре .c файла: lex.c, syn.c, table.c и main.c, и заголовочный файл dc.h, содержащий описания всех имен, которые используются более чем в одном .c файле.
Множественные Заголовочные Файлы (.h)
Стиль разбиения программы с одним заголовочным файлом наиболее пригоден в тех случаях, когда программа невелика и ее части не предполагается использовать отдельно. Поэтому то, что невозможно установить, какие описания зачем помещены в заголовочный файл, несущественно. Помочь могут комментарии. Другой способ — сделать так, чтобы каждая часть программы имела свой заголовочный файл, в котором определяются предоставляемые этой частью средства. Тогда каждый .c файл имеет соответствующий .h файл, и каждый .c файл включает свой собственный (специфицирующий то, что в нем задается) .h файл и, возможно, некоторые другие .h файлы (специфицирующие то, что ему нужно).
Файлы заголовков (C++)
Имена программных элементов, таких как переменные, функции, классы и т. д., должны быть объявлены до их использования. Например, вы не можете просто написать x = 42 без первого объявления «x».
Объявление указывает компилятору, является ли элемент элементом int , функцией double , а class также какой-либо другой вещью. Кроме того, каждое имя должно быть объявлено (прямо или косвенно) в каждом CPP-файле, в котором он используется. При компиляции программы каждый CPP-файл компилируется независимо в единицу компиляции. Компилятор не знает, какие имена объявляются в других единицах компиляции. Это означает, что если вы определяете класс или функцию или глобальную переменную, необходимо предоставить объявление этой вещи в каждом дополнительном CPP-файле, который использует его. Каждое объявление этой вещи должно быть точно идентичным во всех файлах. Небольшое несоответствие приведет к ошибкам или непреднамеренное поведение, когда компоновщик пытается объединить все блоки компиляции в одну программу.
Чтобы свести к минимуму вероятность ошибок, C++ принял соглашение об использовании файлов заголовков для хранения объявлений. Вы делаете объявления в файле заголовка, а затем используете директиву #include в каждом CPP-файле или другом файле заголовка, который требует этого объявления. Директива #include вставляет копию файла заголовка непосредственно в CPP-файл перед компиляцией.
В Visual Studio 2019 г. функция модулей C++20 появилась как улучшение и итоговая замена файлов заголовков. Дополнительные сведения см. в разделе «Обзор модулей» в C++.
Пример
В следующем примере показан распространенный способ объявления класса и последующего использования его в другом исходном файле. Начнем с файла заголовка. my_class.h Он содержит определение класса, но обратите внимание, что определение является неполным; Функция-член do_something не определена:
Затем создайте файл реализации (обычно с расширением CPP или аналогичного расширения). Мы вызовем файл my_class.cpp и предоставим определение для объявления члена. Мы добавим директиву #include для файла «my_class.h», чтобы объявление my_class вставлено в этот момент в CPP-файле, и мы добавим в объявление . std::cout Обратите внимание, что кавычки используются для файлов заголовков в том же каталоге, что и исходный файл, а угловые скобки используются для заголовков стандартной библиотеки. Кроме того, многие заголовки стандартной библиотеки не имеют расширения H или других файлов.
В файле реализации можно использовать using оператор, чтобы избежать необходимости указывать все упоминания «my_class» или «cout» с «N::» или «std::». Не помещайте using инструкции в файлы заголовков!
Теперь мы можем использовать my_class в другом CPP-файле. Мы #include файл заголовка, чтобы компилятор извлекал объявление. Все, что компилятор должен знать, заключается в том, что my_class является классом, который имеет функцию do_something() открытого члена.
После завершения компиляции каждого CPP-файла в OBJ-файлы он передает OBJ-файлы компоновщику. При слиянии файлов объектов компоновщик находит только одно определение для my_class; он находится в OBJ-файле, создаваемом для my_class.cpp, и сборка завершается успешно.
Включение охранников
Как правило, файлы заголовков имеют параметр include guard или директиву #pragma once , чтобы убедиться, что они не вставляются несколько раз в один CPP-файл.
Что нужно поместить в файл заголовка
Так как файл заголовка может быть включен несколькими файлами, он не может содержать определения, которые могут создавать несколько определений одного и того же имени. Следующие действия не допускаются или считаются очень плохой практикой.
- встроенные определения типов в пространстве имен или глобальной области
- Определения функций, отличных от встроенных
- Определения переменных, отличных от const
- агрегатные определения
- безымянные пространства имен
- Директивы using
using Использование директивы не обязательно приведет к ошибке, но может вызвать проблему, так как она добавляет пространство имен в область в каждом CPP-файле, который прямо или косвенно включает этот заголовок.
Пример файла заголовка
В следующем примере показаны различные типы объявлений и определений, разрешенных в файле заголовка:
Для чего нужны Header файлы в С++? Почему нельзя писать без них?
Весь гугл перерыл, не могу понять. И википедию перечитал и вообще все что угодно перечитал. Правда не понимаю.
Что мешает подключать просто .cpp файлы?
Ну подключил ты его два раза, ну пусть компилятор только один раз подключает и все, раз он один черт все в один как бы файл склеивает, значит первое подключение будет выше остальных и будет видно им внизу. Пусть смотрит время изменения и перекомпилирует только изменившееся. Пусть автоматически генерирует и прикрепляет header файлы с описанием интерфейсов к откомпилированному бинарнику и т.п.
Иными словами, почему рутиная работа по генерации хедер файлов, не автоматизирована и возложена на разработчика? Ведь компилятор рядом с откомпилированным бинарником может легко сам сгенерировать файл описания интерфейсов (который он получил, сканируя cpp файл).
Можете привести ситуацию, в которой бы возникали ПРОБЛЕМЫ без использования хедер файлов? Это будет самое лучшее обьяснение.
Наверно уже сказали, но на всякий случай: Потому что можно писать без .h и можно просто включать .cpp Просто ты не понимаешь разницу между двумя подходами и не понимаешь поэтому, зачем это нужно — использовать заголовочные файлы.
6 ответов 6
Проблема лежит в области обратной совместимости.
Посмотрите, любой новый язык программирования — да даже Паскаль, не говоря уже о Java или C# — не нуждаются в заголовочных файлах. Без сомнения, C++ тоже мог бы обойтись без них. В чём же дело?
Перенесёмся на полвека назад, в 1972 год. Представим себе компилятор языка C.
Допустим, мы хотим написать дизайн компилятора. Мы не можем скомпилировать всю программу за раз, у нас на это просто не хватит памяти. Компьютеры тогда были маленькими и медленными. Мы хотим компилировать программу по кусочкам, по нескольку функций за раз.
У нас сразу же возникает проблема: как скомпилировать функцию f , которая ссылается на другую функцию g ? Нам нужно отдельное описание других функций. Мы могли бы, конечно, прочитать все исходные файлы, для начала выяснить, какие функции у нас есть, и затем прочитать их второй раз и скомпилировать один за одним. Но это было слишком сложно и медленно, нужно было парсить определения функций дважды, и один раз выбрасывать результат! Это недопустимый расход процессорного времени! Плюс если держать в памяти определения всех функций, может снова-таки не хватить памяти.
На кого Деннис решил возложить сложную проблему отделения описания функции от её реализации, и подключения только нужных описаний при компиляции данной функции? На нас, программистов. Он решил, что мы должны сами помочь компилятору и скопипастить определения функций в отдельный файл, и сами указать компилятору, какие файлы с определениями нужны. (То есть первый шаг компиляции возлагается на нас.)
Это радикально упростило компилятор, но привело в свою очередь к проблемам. Что будет, если мы забыли подключить нужный заголовочный файл? Ответ: ошибка компиляции. Что будет, если смысл текста заголовочного файла меняется в зависимости от какого-нибудь макроса? Ответ: компилятор «тупой», и не пытается детектировать эту проблему, он перекладывает ответственность на нас.
На момент разработки языка это было правильное решение. Компилятор оказался практичным, быстрым, и программисты были не прочь помочь компиляции. Ну и если кто допускал ошибку, сам был виноват.
Перемотаем стрелки часов в 1983 год. Бьярн создаёт C++. Он решил взлететь на волне популярности языка C, и перенял модель компиляции C с отдельными translation unit’ами и связанными с этим проблемами прямо из C. Впрочем, первые версии C++ были просто препроцесоором языка C! Поэтому проблемы раздельной компиляции перекочевали из C в C++. Хуже того, добавились новые проблемы. Например, шаблоны классов выглядят как классы, но не генерируют объектного кода сами по себе, поэтому для них приходится идти на ухищрения и обходить недостатки системы раздельной компиляции (например, включением реализации в header и трюками компоновщика).
А дальше вступила в игру обратная совместимость. Сейчас, в 2017 году, есть так много кода, написанного в стиле «с заголовками», и так много кода исходит из различных тонкостей, связанных с этим, что менять парадигму уже поздно, поезд практически уехал.
Впрочем, существует проект модульной системы в C++, который должен помочь программистам избавиться от наследства полувековой давности. Он ещё не реализован, и в нём есть сложности уровня дизайна (например, в header’е был определён макрос, будет ли он виден, если мы перейдём от header’ов к модулям?) Надеюсь, в будущем разработчики языка таки смогут побороть проблему обратной совместимости.
2.10 – Заголовочные файлы
По мере того, как программы становятся больше (и используют больше файлов), становится всё более утомительным давать предварительные объявления каждой функции, которую вы хотите использовать, и которая определена в другом файле. Было бы неплохо, если бы вы могли поместить все свои предварительные объявления в одно место, а затем импортировать их, когда они вам понадобятся?
Исходные файлы кода C++ (с расширением .cpp ) – это не единственные файлы, которые обычно встречаются в программах на C++. Другой тип файлов – это заголовочный файл (иногда просто заголовок). Заголовочные файлы обычно имеют расширение .h , но иногда вы можете встретить их с расширением .hpp или вообще без расширения. Основная цель заголовочного файла – распространять объявления в исходные файлы кода.
Ключевой момент
Заголовочные файлы позволяют нам размещать объявления в одном месте, а затем импортировать их туда, где они нам нужны. Это может сэкономить много времени при наборе текста в проектах из нескольких файлов.
Использование заголовочных файлов стандартной библиотеки
Рассмотрим следующую программу:
Эта программа печатает « Hello, world! » в консоль с помощью std::cout . Однако эта программа никогда не предоставляла определение или объявление для std::cout , поэтому как компилятор узнает, что такое std::cout ?
Ответ заключается в том, что std::cout был предварительно объявлен в заголовочном файле « iostream ». Когда мы пишем #include , мы запрашиваем, чтобы препроцессор скопировал всё содержимое (включая предварительные объявления для std::cout ) из файла с именем « iostream » в файл, выполняющий #include .
Ключевой момент
Когда вы включаете файл с помощью #include , содержимое включаемого файла вставляется в точке включения. Это удобный способ извлечения объявлений из другого файла.
Подумайте, что бы произошло, если бы заголовок iostream не существовал. Каждый раз, когда вы хотели бы использовать std::cout , вам приходилось бы вручную вводить или копировать все объявления, связанные с std::cout , в начало каждого файла, который использовал бы std::cout ! Для этого потребуется много знаний о том, как реализован std::cout , и потребуется много работы. Хуже того, если бы прототип функции изменился, нам пришлось бы вручную обновлять все предварительные объявления. Намного проще просто включить iostream с помощью #include !
Когда дело доходит до функций и переменных, стоит помнить, что заголовочные файлы обычно содержат только объявления функций и переменных, а не их определения (в противном случае может произойти нарушение правила одного определения). std::cout объявлен в заголовке iostream, но определен как часть стандартной библиотеки C++, которая автоматически подключается к вашей программе на этапе линкера.

Рисунок 1 – Диаграмма процесса сборки
Лучшая практика
Заголовочные файлы обычно не должны содержать определений функций и переменных, чтобы не нарушать правило одного определения. Исключение сделано для символьных констант (которые мы рассмотрим в уроке «4.14 – const, constexpr и символьные константы»).
Написание собственных заголовочных файлов
А теперь вернемся к примеру, который мы обсуждали в предыдущем уроке. Когда мы закончили, у нас было два файла, 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 ).
Вот наш завершенный заголовочный файл:
Чтобы использовать этот заголовочный файл в main.cpp , мы должны включить его с помощью #include (используя кавычки, а не угловые скобки).
Когда препроцессор обрабатывает строку #include «add.h» , он копирует содержимое add.h в текущий файл в эту точку. Поскольку наш add.h содержит предварительное объявление для функции add , это предварительное объявление будет скопировано в main.cpp . Конечным результатом является программа, которая функционально аналогична той, в которой мы вручную добавили предварительное объявление вверху main.cpp .
Следовательно, наша программа будет правильно компилироваться и компоноваться.

Рисунок 2 – Диаграмма процесса сборки
Включение заголовочного файла в соответствующий исходный файл
Позже вы увидите, что большинство исходных файлов включают свой соответствующий заголовочный файл, даже если он им не нужен. Зачем?
Включение заголовочного файла в исходный файл увеличивает прямую совместимость. Очень вероятно, что в будущем вы добавите больше функций или измените существующие таким образом, что им нужно будет знать о существовании друг друга.
Когда мы углубимся в изучение стандартной библиотеки, вы будете включать множество заголовочных файлов библиотек. Если вам потребовалось включение в заголовочном файле, оно, вероятно, понадобилось вам для объявления функции. Это означает, что вам также потребуется такое же включение в исходный файл. Это приведет к тому, что в исходном файле у вас будет копия включений заголовочного файла. Включив заголовочный файл в исходный файл, исходный файл получит доступ ко всему, к чему имел доступ заголовочный файл.
При разработке библиотеки включение заголовочного файла в исходный файл может даже помочь в раннем обнаружении ошибок.
Лучшая практика
При написании исходного файла включите в него соответствующий заголовочный файл (если он существует), даже если он вам пока не нужен.
Поиск и устранение проблем
Если вы получаете ошибку компилятора, указывающую, что add.h не найден, убедитесь, что файл действительно называется add.h . В зависимости от того, как вы его создали и назвали, возможно, файл может иметь имя вроде add (без расширения), add.h.txt или add.hpp . Также убедитесь, что он находится в том же каталоге, что и остальные исходные файлы.
Если вы получаете сообщение об ошибке компоновщика о том, что добавление функции не определено, убедитесь, что вы добавили в проект файл add.cpp , чтобы определение для функции add можно было слинковать в программе.
Угловые скобки и двойные кавычки
Вам, наверное, интересно, почему мы используем угловые скобки для iostream и двойные кавычки для add.h . Возможно, что заголовочные файлы с таким же именем могут существовать в нескольких каталогах. Использование угловых скобок и двойных кавычек помогает компилятору понять, где ему следует искать заголовочные файлы.
Когда мы используем угловые скобки, мы сообщаем препроцессору, что это заголовочный файл, который мы не писали сами. Компилятор будет искать заголовок только в каталогах, указанных в каталогах включаемых файлов (include directories). Каталоги включаемых файлов настраиваются как часть вашего проекта / настроек IDE / настроек компилятора и обычно по умолчанию используются для каталогов, содержащих заголовочные файлы, которые поставляются с вашим компилятором и/или ОС. Компилятор не будет искать заголовочный файл в каталоге исходного кода вашего проекта.
Когда мы используем двойные кавычки, мы сообщаем препроцессору, что это заголовочный файл, который написали мы. Компилятор сначала будет искать этот заголовочный файл в текущем каталоге. Если он не сможет найти там подходящий заголовочный файл, он будет искать его в каталогах включаемых файлов.
Правило
Используйте двойные кавычки, чтобы включать заголовочные файлы, которые написали вы или которые, как ожидается, будут найдены в текущем каталоге. Угловые скобки используйте, чтобы включать заголовочные файлы, которые поставляются с вашим компилятором, ОС или сторонними библиотеками, которые вы установили в другом месте своей системы.
Почему у iostream нет расширения .h ?
Другой часто задаваемый вопрос: «Почему iostream (или любой другой заголовочный файл стандартной библиотеки) не имеет расширения .h ?». Ответ заключается в том, что iostream.h – это другой заголовочный файл, отличающийся от iostream ! Для объяснения требуется небольшой урок истории.
Когда C++ был только создан, все файлы в стандартной библиотеке оканчивались расширением .h . Жизнь была последовательной, и это было хорошо. Исходные версии cout и cin были объявлены в iostream.h . Когда комитет ANSI стандартизировал язык, они решили переместить все функции стандартной библиотеки в пространство имен std , чтобы избежать конфликтов имен с пользовательскими идентификаторами. Однако это представляло проблему: если бы они переместили всю функциональность в пространство имен std , ни одна из старых программ (включая iostream.h ) больше не работала бы!
Чтобы обойти эту проблему, был представлен новый набор заголовочных файлов, которые используют те же имена, но не имеют расширения .h . Все функции в этих новых заголовочных файлах находятся в пространстве имен std . Таким образом, старые программы, содержащие #include , не нужно переписывать, а новые программы могут использовать #include .
Кроме того, многие библиотеки, унаследованные от C, которые всё еще используются в C++, получили префикс c (например, stdlib.h стал cstdlib ). Функциональные возможности этих библиотек также были перенесены в пространство имен std , чтобы избежать конфликтов имен.
Лучшая практика
При включении заголовочного файла из стандартной библиотеки используйте версию без расширения (без .h ), если она существует. Пользовательские заголовочные файлы по-прежнему должны использовать расширение .h .
Включение заголовочных файлов из других каталогов
Другой распространенный вопрос связан с тем, как включать заголовочные файлы из других каталогов.
Один (плохой) способ сделать это – добавить относительный путь к заголовочному файлу, который вы хотите включить как часть строки #include . Например:
Хотя это будет компилироваться (при условии, что файлы существуют в этих относительных каталогах), обратная сторона этого подхода состоит в том, что он требует от вас отражения структуры каталогов в вашем коде. Если вы когда-нибудь обновите структуру каталогов, ваш код больше не будет работать.
Лучший способ – сообщить вашему компилятору или 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, а не каждый файл кода.
Заголовочные файлы могут включать другие заголовочные файлы
Обычно для заголовочных файлов требуется объявление или определение, которое находится в другом заголовочном файле. Из-за этого заголовочные файлы часто включают с помощью #include другие заголовочные файлы.
Когда ваш исходный файл включает с помощью #include первый заголовочный файл, вы также получите любые другие заголовочные файлы, которые были включены в первый заголовочный файл (и любые заголовочные файлы, которые были включены в предыдущие, и т.д.). Эти дополнительные заголовочные файлы иногда называют «транзитивными включениями», поскольку они включаются неявно.
Содержимое этих транзитивных включений доступно для использования в вашем файле исходного кода. Однако не следует полагаться на содержимое заголовков, которые включены транзитивно. Реализация заголовочных файлов может со временем меняться или отличаться в разных системах. Если это произойдет, ваш код может компилироваться только на определенных системах или может компилироваться сейчас, но перестать в будущем. Этого легко избежать, явно включив все заголовочные файлы, необходимые для содержимого вашего файла исходного кода.
Лучшая практика
Каждый файл должен явно включать с #include все заголовочные файлы, необходимые для компиляции. Не полагайтесь на заголовочные файлы, включенные транзитивно из других заголовков.
К сожалению, нет простого способа определить, полагается ли ваш файл кода случайно на содержимое заголовочного файла, который был включен другим заголовочным файлом.
Вопрос: Я не включил , и моя программа всё равно работала! Почему?
Это один из наиболее часто задаваемых вопросов. Ответ: скорее всего, он работает, потому что вы включили какой-то другой заголовок (например, ), который сам включает . Несмотря на то, что ваша программа будет компилироваться, в соответствии с приведенными выше рекомендациями вам не следует полагаться на это. То, что компилируется у вас, может не компилироваться на машине друга.
Порядок #include заголовочных файлов
Если ваши заголовочные файлы написаны правильно и включают с #include всё, что им нужно, порядок включения не имеет значения. Однако включение заголовочных файлов в определенном порядке может помочь выявить ошибки, когда заголовочные файлы могут не включать в себя всё, что им нужно.
Лучшая практика
Упорядочьте свои включения с #include следующим образом: сначала ваши собственные пользовательские заголовки, затем заголовки сторонних библиотек, затем заголовки стандартных библиотек; заголовки в каждом разделе должны быть отсортированы в алфавитном порядке.
Таким образом, если в одном из ваших пользовательских заголовков отсутствует #include для заголовка сторонней библиотеки или стандартной библиотеки, это с большей вероятностью вызовет ошибку компиляции, чтобы вы могли ее исправить.
Рекомендации по использованию заголовочных файлов
Вот еще несколько рекомендаций по созданию и использованию заголовочных файлов.
Заголовочный файл
- В программировании заголовочный файл (англ. header file) или подключаемый файл — файл, содержимое которого автоматически добавляется препроцессором в исходный текст в том месте, где располагается некоторая директива ( в Паскале, #include в Си).
В языках программирования Си и C++ заголовочные файлы — основной способ подключить к программе типы данных, структуры, прототипы функций, перечислимые типы и макросы, используемые в другом модуле. По умолчанию используется расширение .h; иногда для заголовочных файлов языка C++ используют расширение .hpp.
Чтобы избежать повторного включения одного и того же кода, используются директивы #ifndef, #define, #endif.
Заголовочный файл в общем случае может содержать любые конструкции языка программирования, но на практике исполняемый код (за исключением inline-функций в C++) в заголовочные файлы не помещают. Например, идентификаторы, которые должны быть объявлены более чем в одном файле, удобно описать в заголовочном файле, а затем его подключать по мере надобности. Подобным же образом работает модульность и в большинстве ассемблеров.
По сложившейся традиции, в заголовочных файлах объявляют функции стандартной библиотеки Си и Си++.
Связанные понятия
Стандартной библиотекой языка Си (также известная как libc, crt) называется часть стандарта ANSI C, посвященная заголовочным файлам и библиотечным подпрограммам. Является описанием реализации общих операций, таких как обработка ввода-вывода и строк, в языке программирования Си. Стандартная библиотека языка Си — это описание программного интерфейса, а не настоящая библиотека, пригодная для использования в процессе компиляции.
Компоновщик (также редактор связей, от англ. link editor, linker) — инструментальная программа, которая производит компоновку («линковку»): принимает на вход один или несколько объектных модулей и собирает по ним исполнимый модуль.
Стандартная библиотека языка программирования — набор модулей, классов, объектов, констант, глобальных переменных, шаблонов, макросов, функций и процедур, доступных для вызова из любой программы, написанной на этом языке и присутствующих во всех реализациях языка.
Фу́нкция в программировании — фрагмент программного кода (подпрограмма), к которому можно обратиться из другого места программы. В большинстве случаев с функцией связывается идентификатор, но многие языки допускают и безымянные функции. С именем функции неразрывно связан адрес первой инструкции (оператора), входящей в функцию, которой передаётся управление при обращении к функции. После выполнения функции управление возвращается обратно в адрес возврата — точку программы, где данная функция была.
Переме́нная в императивном программировании — поименованная, либо адресуемая иным способом область памяти, адрес которой можно использовать для осуществления доступа к данным. Данные, находящиеся в переменной (то есть по данному адресу памяти), называются значением этой переменной.
Упоминания в литературе
Таким образом, ActiveSync является очень полезной программой, и с ней могут взаимодействовать приложения, созданные для работы на мобильном компьютере. Для этого в состав SDK включены соответствующие библиотеки, заголовочные файлы и примеры. Их можно найти в папке C: Program FilesWindows CE Toolswce420POCKET PC 2003Activesync.
Связанные понятия (продолжение)
Указатель (англ. pointer) — переменная, диапазон значений которой состоит из адресов ячеек памяти или специального значения — нулевого адреса. Последнее используется для указания того, что в данный момент указатель не ссылается ни на одну из допустимых ячеек.
Исполняемый файл (англ. executable file, также выполняемый, реже исполнимый, выполнимый) — файл, содержащий программу в виде, в котором она может быть исполнена компьютером. Перед исполнением программа загружается в память, и выполняются некоторые подготовительные операции (настройка окружения, загрузка библиотек).
Библиоте́ка (от англ. library) в программировании — сборник подпрограмм или объектов, используемых для разработки программного обеспечения (ПО).
Ссылка в программировании — это объект, указывающий на определенные данные, но не хранящий их. Получение объекта по ссылке называется разыменованием.
Си (англ. C) — компилируемый статически типизированный язык программирования общего назначения, разработанный в 1969—1973 годах сотрудником Bell Labs Деннисом Ритчи как развитие языка Би. Первоначально был разработан для реализации операционной системы UNIX, но впоследствии был перенесён на множество других платформ. Согласно дизайну языка, его конструкции близко сопоставляются типичным машинным инструкциям, благодаря чему он нашёл применение в проектах, для которых был свойственен язык ассемблера.
Объе́ктный мо́дуль (также — объектный файл, англ. object file) — файл с промежуточным представлением отдельного модуля программы, полученный в результате обработки исходного кода компилятором. Объектный файл содержит в себе особым образом подготовленный код (часто называемый двоичным или бинарным), который может быть объединён с другими объектными файлами при помощи редактора связей (компоновщика) для получения готового исполнимого модуля либо библиотеки.
Из-за путаницы с терминологией словом «оператор» в программировании нередко обозначают операцию (англ. operator), см. Операция (программирование).Инстру́кция или опера́тор (англ. statement) — наименьшая автономная часть языка программирования; команда или набор команд. Программа обычно представляет собой последовательность инструкций.
Маши́нный код (платфо́рменно-ориенти́рованный код), маши́нный язы́к — система команд (набор кодов операций) конкретной вычислительной машины, которая интерпретируется непосредственно процессором или микропрограммами этой вычислительной машины.Компьютерная программа, записанная на машинном языке, состоит из машинных инструкций, каждая из которых представлена в машинном коде в виде т. н. опкода — двоичного кода отдельной операции из системы команд машины. Для удобства программирования вместо числовых.
Макрокоманда, макроопределение или мáкрос — программный алгоритм действий, записанный пользователем. Часто макросы применяют для выполнения рутинных действий. А также макрос — это символьное имя в шаблонах, заменяемое при обработке препроцессором на последовательность символов, например: фрагмент html-страницы в веб-шаблонах, или одно слово из словаря синонимов в синонимизаторах.
Подпрограмма (англ. subroutine) — поименованная или иным образом идентифицированная часть компьютерной программы, содержащая описание определённого набора действий. Подпрограмма может быть многократно вызвана из разных частей программы. В языках программирования для оформления и использования подпрограмм существуют специальные синтаксические средства.
Байт-код (байтко́д; англ. bytecode, также иногда p-код, p-code от portable code) — стандартное промежуточное представление, в которое может быть переведена компьютерная программа автоматическими средствами. По сравнению с исходным кодом, удобным для создания и чтения человеком, байт-код — это компактное представление программы, уже прошедшей синтаксический и семантический анализ. В нём в явном виде закодированы типы, области видимости и другие конструкции. С технической точки зрения, байт-код представляет.
Синтаксис языка программирования — набор правил, описывающий комбинации символов алфавита, считающиеся правильно структурированной программой (документом) или её фрагментом. Синтаксису языка противопоставляется его семантика. Синтаксис языка описывает «чистый» язык, в то же время семантика приписывает значения (действия) различным синтаксическим конструкциям.
В языках программирования объявле́ние (англ. declaration) включает в себя указание идентификатора, типа, а также других аспектов элементов языка, например, переменных и функций. Объявление используется, чтобы уведомить компилятор о существовании элемента; это весьма важно для многих языков (например, таких как Си), требующих объявления переменных перед их использованием.
Библиотека стандартных шаблонов (STL) (англ. Standard Template Library) — набор согласованных обобщённых алгоритмов, контейнеров, средств доступа к их содержимому и различных вспомогательных функций в C++.
Стандартные потоки ввода-вывода в системах типа UNIX (и некоторых других) — потоки процесса, имеющие номер (дескриптор), зарезервированный для выполнения некоторых «стандартных» функций. Как правило (хотя и не обязательно), эти дескрипторы открыты уже в момент запуска задачи (исполняемого файла).
Низкоуровневый язык программирования (язык программирования низкого уровня) — язык программирования, близкий к программированию непосредственно в машинных кодах используемого реального или виртуального (например, байт-код, Microsoft .NET) процессора. Для обозначения машинных команд обычно применяется мнемоническое обозначение. Это позволяет запоминать команды не в виде последовательности двоичных нулей и единиц, а в виде осмысленных сокращений слов человеческого языка (обычно английских).
Обрабо́тка исключи́тельных ситуа́ций (англ. exception handling) — механизм языков программирования, предназначенный для описания реакции программы на ошибки времени выполнения и другие возможные проблемы (исключения), которые могут возникнуть при выполнении программы и приводят к невозможности (бессмысленности) дальнейшей отработки программой её базового алгоритма. В русском языке также применяется более короткая форма термина: «обработка исключений».
Препроцессор С/С++ (англ. pre processor, предобработчик) — программа, подготавливающая код программы на языке C/C++ к компиляции.
Стек вызовов (от англ. call stack; применительно к процессорам — просто «стек») — в теории вычислительных систем, LIFO-стек, хранящий информацию для возврата управления из подпрограмм (процедур, функций) в программу (или подпрограмму, при вложенных или рекурсивных вызовах) и/или для возврата в программу из обработчика прерывания (в том числе при переключении задач в многозадачной среде).
Сериализация (в программировании) — процесс перевода какой-либо структуры данных в последовательность битов. Обратной к операции сериализации является операция десериализации (структуризации) — восстановление начального состояния структуры данных из битовой последовательности.
Стати́ческая типиза́ция — приём, широко используемый в языках программирования, при котором переменная, параметр подпрограммы, возвращаемое значение функции связывается с типом в момент объявления и тип не может быть изменён позже (переменная или параметр будут принимать, а функция — возвращать значения только этого типа). Примеры статически типизированных языков — Ада, С++, C#, D, Java, ML, Паскаль, Nim.
Регуля́рные выраже́ния (англ. regular expressions) — формальный язык поиска и осуществления манипуляций с подстроками в тексте, основанный на использовании метасимволов (символов-джокеров, англ. wildcard characters). Для поиска используется строка-образец (англ. pattern, по-русски её часто называют «шаблоном», «маской»), состоящая из символов и метасимволов и задающая правило поиска. Для манипуляций с текстом дополнительно задаётся строка замены, которая также может содержать в себе специальные символы.
Вывод типов (англ. type inference) — в программировании возможность компилятора самому логически вывести тип значения у выражения. Впервые механизм вывода типов был представлен в языке ML, где компилятор всегда выводит наиболее общий полиморфный тип для всякого выражения. Это не только сокращает размер исходного кода и повышает его лаконичность, но и нередко повышает повторное использование кода.
Файловый дескриптор — это неотрицательное целое число. Когда создается новый поток ввода-вывода, ядро возвращает процессу, создавшему поток ввода-вывода, его файловый дескриптор.
Анонимная функция в программировании — особый вид функций, которые объявляются в месте использования и не получают уникального идентификатора для доступа к ним. Поддерживаются во многих языках программирования.
Дизассе́мблер (от англ. disassembler ) — транслятор, преобразующий машинный код, объектный файл или библиотечные модули в текст программы на языке ассемблера.
Ввод-вывод (от англ. input/output, I/O) в информатике — взаимодействие между обработчиком информации (например, компьютер) и внешним миром, который может представлять как человек, так и любая другая система обработки информации. Ввод — сигнал или данные, полученные системой, а вывод — сигнал или данные, посланные ею (или из неё). Термин также может использоваться как обозначение (или дополнение к обозначению) определенного действия: «выполнять ввод-вывод» означает выполнение операций ввода или вывода.
Конте́йнер в программировании — тип, позволяющий инкапсулировать в себе объекты других типов. Контейнеры, в отличие от коллекций, реализуют конкретную структуру данных.
Коммента́рии — пояснения к исходному тексту программы, находящиеся непосредственно внутри комментируемого кода. Синтаксис комментариев определяется языком программирования. С точки зрения компилятора или интерпретатора, комментарии — часть текста программы, не влияющая на её семантику. Комментарии не оказывают никакого влияния на результат компиляции программы или её интерпретацию. Помимо исходных текстов программ, комментарии также применяются в языках разметки и языках описания.
Соглашение о вызове (англ. calling convention) — описание технических особенностей вызова подпрограмм, определяющее.
Конста́нта в программировании — способ адресации данных, изменение которых рассматриваемой программой не предполагается или запрещается.
Неопределённое поведение (англ. undefined behaviour, в ряде источников непредсказуемое поведение) — свойство некоторых языков программирования (наиболее заметно в Си), программных библиотек и аппаратного обеспечения в определённых маргинальных ситуациях выдавать результат, зависящий от реализации компилятора (библиотеки, микросхемы) и случайных факторов наподобие состояния памяти или сработавшего прерывания. Другими словами, спецификация не определяет поведение языка (библиотеки, микросхемы) в любых.
Зарезерви́рованное сло́во (или ключево́е сло́во) — в языках программирования слово, имеющее специальное значение. Идентификаторы с такими именами запрещены.
Интерфейс командной строки (англ. Command line interface, CLI) — разновидность текстового интерфейса (CUI) между человеком и компьютером, в котором инструкции компьютеру даются в основном путём ввода с клавиатуры текстовых строк (команд), в UNIX-системах возможно применение мыши. Также известен под названием консоль.
Подсве́тка си́нтаксиса — выделение синтаксических конструкций текста с использованием различных цветов, шрифтов и начертаний.
Паска́ль (англ. Pascal) — один из наиболее известных языков программирования, используется для обучения программированию в старших классах и на первых курсах вузов, является основой для ряда других языков.
Стек (англ. stack — стопка; читается стэк) — абстрактный тип данных, представляющий собой список элементов, организованных по принципу LIFO (англ. last in — first out, «последним пришёл — первым вышел»).
Обобщённое программирование (англ. generic programming) — парадигма программирования, заключающаяся в таком описании данных и алгоритмов, которое можно применять к различным типам данных, не меняя само это описание. В том или ином виде поддерживается разными языками программирования. Возможности обобщённого программирования впервые появились в виде дженериков (обобщённых функций) в 1970-х годах в языках Клу и Ада, затем в виде параметрического полиморфизма в ML и его потомках, а затем во многих объектно-ориентированных.
Область видимости (англ. scope) в программировании — часть программы, в пределах которой идентификатор, объявленный как имя некоторой программной сущности (обычно — переменной, типа данных или функции), остаётся связанным с этой сущностью, то есть позволяет посредством себя обратиться к ней. Говорят, что идентификатор объекта «виден» в определённом месте программы, если в данном месте по нему можно обратиться к данному объекту. За пределами области видимости тот же самый идентификатор может быть.
Интерпретатор (англ. interpreter ıntə:’prıtə, от лат. interpretator — толкователь) — программа (разновидность транслятора), выполняющая интерпретацию.
Среда выполнения (англ. execution environment, иногда «ранта́йм» от англ. runtime — «время выполнения») в информатике — вычислительное окружение, необходимое для выполнения компьютерной программы и доступное во время выполнения компьютерной программы. В среде выполнения, как правило, невозможно изменение исходного текста программы, но может наличествовать доступ к переменным окружения операционной системы, таблицам объектов и модулей разделяемых библиотек.
Динами́ческая типиза́ция — приём, широко используемый в языках программирования и языках спецификации, при котором переменная связывается с типом в момент присваивания значения, а не в момент объявления переменной. Таким образом, в различных участках программы одна и та же переменная может принимать значения разных типов. Примеры языков с динамической типизацией — Smalltalk, Python, Objective-C, Ruby, PHP, Perl, JavaScript, Lisp, xBase, Erlang, Visual Basic.
Де́мон (daemon, dæmon, др.-греч. δαίμων божество) — компьютерная программа в системах класса UNIX, запускаемая самой системой и работающая в фоновом режиме без прямого взаимодействия с пользователем.
Компоновка, библиотеки и заголовочные файлы
При создании компилятора С или С++ решались две задачи. Во-первых, разрабатывался сам компилятор. Во-вторых, создавалась библиотека функций. Поскольку в библиотеке Borland содержится очень много функций, можно не сомневаться, что ее создание потребовало от программистов серьезных усилий. (Одно только описание этих функций занимает несколько сотен страниц!) Любая С или С++ программа опирается на эти функции при выполнении множества задач. Ввиду столь фундаментальной роли библиотеки для выполнения программ важно познакомиться с описанием ее работы. В частности, необходимо разобраться в работе компоновщика, понять, чем библиотеки отличаются от объектных файлов и какова роль заголовочных файлов. Эти вопросы и рассматриваются ниже.
Результатом работы компилятора является перемещаемый объектный файл, а результатом работы компоновщика — файл, готовый к исполнению. Компоновщик выполняет двойную роль. Во-первых, он физически объединяет указанные в списке связей файлы в один программный файл. Во- вторых, он решает проблему внешних ссылок и обращений к памяти. Внешняя ссылка делается каждый раз, когда в коде одного файла упоминается код из другого файла. Это случается либо при вызове функции, либо при упоминании глобальной переменной. Например, при установлении связей между двумя приведенными ниже файлами нужно реализовать обращение файла Two к переменной count. Именно компоновщик «сообщает» файлу Two, где в памяти располагается count.
Файл 1
int count;
extern void display(void);
int main(void)
count = 10;
display();
return 0;
>
Файл 2
#include
extern int count;
void display(void)
printf(«%d», count);
>
Подобным же образом компоновщик «сообщает» файлу One, где располагается функция display, чтобы ее можно было вызвать.
При формировании объектного кода файла Two компоновщик на место адреса count подставляет «пустую» команду, поскольку он не знает, в каком месте памяти расположена count. Примерно то же происходит при компилировании файла One. Адрес display() неизвестен, поэтому используется «пустая» команда. Такой прием обеспечивает возможность создания перемещаемого кода (relocatable code). После связывания файлов компоновщиком «пустые» команды замещаются относительными адресами.
Чтобы лучше понять, что такое перемещаемый код, необходимо сначала разобраться с абсолютными кодами. Сегодня это используется редко, но на начальных этапах развития компьютеров было вполне обычным компилировать программу так, чтобы она работала в определенной зоне памяти. При таком способе компилирования все адреса фиксировались на этапе компиляции. Программа с фиксированными адресами могла загружаться в строго определенную область памяти, для которой она была скомпилирована. Запускалась она также с фиксированного адреса.
С другой стороны, перемещаемый код компилируется без фиксирования адресной информации. При формировании перемещаемого объектного файла компоновщик назначает адрес в виде смещения для каждого вызова, перехода или глобальной переменной. Когда файл загружается в память для исполнения, загрузчик автоматически преобразует смещения в адреса, соответствующие области памяти, куда производится загрузка. Это значит, что перемещаемая программа может загружаться и запускаться из множества разных областей в памяти.
Компоновщик (LINKER)
Результатом работы компилятора является перемещаемый объектный файл, а результатом работы компоновщика — файл, готовый к исполнению. Компоновщик выполняет двойную роль. Во-первых, он физически объединяет указанные в списке связей файлы в один программный файл. Во- вторых, он решает проблему внешних ссылок и обращений к памяти. Внешняя ссылка делается каждый раз, когда в коде одного файла упоминается код из другого файла. Это случается либо при вызове функции, либо при упоминании глобальной переменной. Например, при установлении связей между двумя приведенными ниже файлами нужно реализовать обращение файла Two к переменной count. Именно компоновщик «сообщает» файлу Two, где в памяти располагается count.
Файл 1
int count;
extern void display(void);
int main(void)
count = 10;
display();
return 0;
>
Файл 2
#include
extern int count;
void display(void)
printf(«%d», count);
>
Подобным же образом компоновщик «сообщает» файлу One, где располагается функция display, чтобы ее можно было вызвать.
При формировании объектного кода файла Two компоновщик на место адреса count подставляет «пустую» команду, поскольку он не знает, в каком месте памяти расположена count. Примерно то же происходит при компилировании файла One. Адрес display() неизвестен, поэтому используется «пустая» команда. Такой прием обеспечивает возможность создания перемещаемого кода (relocatable code). После связывания файлов компоновщиком «пустые» команды замещаются относительными адресами.
Чтобы лучше понять, что такое перемещаемый код, необходимо сначала разобраться с абсолютными кодами. Сегодня это используется редко, но на начальных этапах развития компьютеров было вполне обычным компилировать программу так, чтобы она работала в определенной зоне памяти. При таком способе компилирования все адреса фиксировались на этапе компиляции. Программа с фиксированными адресами могла загружаться в строго определенную область памяти, для которой она была скомпилирована. Запускалась она также с фиксированного адреса.
С другой стороны, перемещаемый код компилируется без фиксирования адресной информации. При формировании перемещаемого объектного файла компоновщик назначает адрес в виде смещения для каждого вызова, перехода или глобальной переменной. Когда файл загружается в память для исполнения, загрузчик автоматически преобразует смещения в адреса, соответствующие области памяти, куда производится загрузка. Это значит, что перемещаемая программа может загружаться и запускаться из множества разных областей в памяти.
Библиотечные файлы в сравнении с объектными файлами
Хотя библиотеки и похожи на объектные файлы, между ними имеется существенное различие: при обращении к библиотеке к программе добавляется не весь код, имеющийся в библиотеке. Когда компоновщик обрабатывает программу, состоящую из нескольких объектных файлов, полный код каждого объектного файла становится частью результирующей исполняемой программы. Это происходит независимо от того, будет ли в действительности использоваться этот код. Иначе говоря, компоновщик при формировании программы просто объединяет вместе все объектные файлы. С библиотечными файлами дело обстоит иначе.
Библиотека представляет собой собрание функций. В отличие от объектных файлов в библиотечном файле хранится название каждой функции, объектный код функции и информация, касающаяся «перемещаемости» файла, необходимая для редактирования связей. Когда программа делает ссылку на функцию, содержащуюся в библиотеке, компоновщик отыскивает эту функцию и добавляет ее код к программе. Таким образом, к программе добавляются только те функции, которые действительно будут в ней использоваться.
Поскольку функции хранятся в библиотеке, то в исполняемый код программы войдут лишь действительно используемые в программе функции. (Если бы они входили в состав объектных файлов, программа была бы длиннее на несколько сот килобайт!)
Заголовочные файлы
Многие библиотечные функции работают со своими особыми типами данных и со структурами, к которым программа должна иметь доступ. Эти структуры и типы определяются в заголовочных файлах, поставляемых с компилятором, и они (заголовочные файлы) должны включаться (с помощью #include) в каждый файл, использующий функции, на которые они ссылаются. Кроме того, у всех библиотечных функций имеются прототипы, определенные в заголовочном файле. Это сделано по двум причинам. Во-первых, в (С++ все функции должны иметь прототипы. Во-вторых, хотя в С создание прототипов и не является обязательным, их использование настоятельно рекомендовано, поскольку оно обеспечивает средство для более тщательного контроля типов. Включая в С-программу заголовочные файлы, которые соответствуют стандартным функциям программы, можно обнаружить потенциальные ошибки несовпадения типов. Например, включение string.h (заголовочного файла для работы с функциями обработки строк) в нижеследующий код приведет к выдаче при компилировании предупреждения:
#include
char s1[20] = «hello «;
char s2[] = «there.»;
int main(void)
int p;
p = strcat(s1, s2);
return 0;
>
Поскольку функция 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 | Функции системного времени |
Макросы в заголовочных файлах
Многие библиотечные функции в действительности являются вообще не функциями, а скорее параметризованными макроопределениями, содержащимися в заголовочном файле. Последствия этого, в общем-то, незначительны, однако эта разница будет подчеркиваться при обсуждении таких «функций». Если по каким-либо причинам необходимо избежать использования стандартного макроса, можно отменить его определение, используя препроцессорную директиву #undef.






