Какая конструкция позволяет включить файл только один раз вне зависимости от количества вызовов

Обновлено: 03.07.2024

Я знаю, что это общий вопрос, но я все еще не могу полностью разобраться в этом.

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

Кто-то сказал мне ранее, что заголовочный файл (с включенными защитными элементами) будет включен только один раз в одну единицу перевода, но несколько раз во весь код. Это правда?

Если он включается только один раз во весь код, когда один файл желает включить его, и препроцессор обнаруживает, что он уже был включен, как этот файл, который хочет использовать его, узнает местонахождение в коде, который он ранее включал?

Решение

предварительная обработка

Если A включает в себя B а также C , а также B включает в себя C , выход препроцессора для A будет включать обработанный текст C дважды.

Первый раз, EXAMPLE_H не определено, и препроцессор оценит содержимое в пределах ifndef / endif блок. Во второй раз он пропустит этот блок. Таким образом, обработанный вывод изменения, и определения включены только один раз.

Это настолько распространено, что некоторые компиляторы используют нестандартную директиву, которая короче и не требует выбора уникальной переменной препроцессора:

Вы можете выяснить, насколько переносим вы хотите свой код C / C ++, и какую защиту заголовков использовать.

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

составление

Компилятор генерирует машинный код из вашего предварительно обработанного C / C ++.

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

соединение

Компоновщик объединяет объектные файлы. Он сопоставляет определения (известные реализации) со ссылками на таблицу символов.

Может быть так, что два объектных файла предоставляют определение, а компоновщик возьмет один. Это происходит, если вы поместили исполняемый код в свои заголовки. Обычно это не происходит в C, но это происходит очень часто в C ++ из-за шаблонов.

Заголовок «код», будь то объявления или определения, включается несколько раз во все объектные файлы, но компоновщик объединяет все это вместе, так что он присутствует только один раз в исполняемом файле. (Я исключаю встроенные функции, которые присутствуют несколько раз.)

Другие решения

За static Определения, это не проблема вообще, так как они не будут видны за пределами модуля (иначе область файла). Для глобальных программных определений это отличается и приведет к ошибке «множественные определения».

Замечания: это в основном для С. Для C ++ существуют существенные различия, поскольку классы и т. Д. Добавляют дополнительную сложность тому, что / когда допускается несколько глобальных объектов.

Единица перевода обычно соответствует «исходному файлу», хотя некоторые неясные реализации могут использовать другое определение. Если отдельно скомпилированный исходный файл содержит тот же заголовок, препроцессор нет способа узнать что другой файл уже включил его, или что любой другой файл был частью того же проекта.

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

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

Читайте также: