включить охрану
Этот раздел может быть слишком техническим для понимания большинства читателей . ( сентябрь 2018 г. ) |
В C и C++ языках программирования защита #include , иногда называемая защитой макроса , защитой заголовка или защитой файла , представляет собой особую конструкцию, используемую для предотвращения проблемы двойного включения при работе с директивой include .
Препроцессор C обрабатывает директивы вида #include <file>
в исходном файле, найдя связанный file
на диске и транслируя («включая») его содержимое в копию исходного файла, известную как единица перевода , заменяя в процессе директиву include. Файлы, включаемые в этом отношении, обычно представляют собой файлы , которые обычно содержат объявления функций заголовочные и классов или структур .
Если директива #include для данного файла появляется несколько раз во время компиляции (т. е. потому, что она появилась в нескольких других заголовках), файл каждый раз обрабатывается заново. Однако если определенные конструкции языка C или C++ определены дважды , результирующая единица перевода будет недействительной . Охранники #include предотвращают возникновение этой ошибочной конструкции, определяя макрос препроцессора при первом включении заголовка и обнаруживая его присутствие, чтобы пропустить содержимое файла при последующих включениях.
Добавление защитных элементов #include в файл заголовка — один из способов сделать этот файл идемпотентным . Другая конструкция для борьбы с двойным включением — #pragma Once , которая нестандартна, но почти повсеместно поддерживается компиляторами C и C++ .
Двойное включение
[ редактировать ]Пример
[ редактировать ]Следующий код C демонстрирует реальную проблему, которая может возникнуть, если защита #include отсутствует:
Файл "grandparent.h"
[ редактировать ]struct foo {
int member;
};
Файл «parent.h»
[ редактировать ]#include "grandparent.h"
Файл "child.c"
[ редактировать ]#include "grandparent.h"
#include "parent.h"
Результат
[ редактировать ]struct foo {
int member;
};
struct foo {
int member;
};
Здесь файл «child.c» косвенно включает две копии текста из заголовочного файла «grandparent.h». Это вызывает ошибку компиляции , поскольку тип структуры foo
таким образом, будет определено дважды. В C++ это было бы названо нарушением правила одного определения .
Использование охранников #include
[ редактировать ]Пример
[ редактировать ]В этом разделе используется тот же код с добавлением защитных элементов #include. Препроцессор C предварительно обрабатывает файлы заголовков, в том числе и осуществляет их рекурсивную предварительную обработку . Как мы увидим, это приведет к правильному исходному файлу.
Файл "grandparent.h"
[ редактировать ]#ifndef GRANDPARENT_H
#define GRANDPARENT_H
struct foo {
int member;
};
#endif /* GRANDPARENT_H */
Файл «parent.h»
[ редактировать ]#include "grandparent.h"
Файл "child.c"
[ редактировать ]#include "grandparent.h"
#include "parent.h"
Результат
[ редактировать ]struct foo {
int member;
};
Здесь первое включение «grandparent.h» имеет макрос GRANDPARENT_H
определенный. Когда «child.c» включает «grandparent.h» во второй раз (включая «parent.h»), как #ifndef
тест возвращает false, препроцессор переходит к #endif
, тем самым избегая второго определения struct foo
. Программа компилируется корректно.
Обсуждение
[ редактировать ] разные соглашения об именах Guard макроса могут использовать Разные программисты . Другие распространенные формы приведенного выше примера включают в себя GRANDPARENT_INCLUDED
, CREATORSNAME_YYYYMMDD_HHMMSS
(с заменой соответствующей информации о времени) и имена, сгенерированные из UUID . (Однако имена , начинающиеся с одного подчеркивания и заглавной буквы (C и C++), или любое имя, содержащее двойное подчеркивание (только C++), например _GRANDPARENT_H
и GRANDPARENT__H
, зарезервированы для реализации языка и не должны использоваться пользователем. [1] [2] )
Конечно, важно избегать дублирования одного и того же имени макроса включения-защиты в разных файлах заголовков, поскольку включение первого из них предотвратит включение второго, что приведет к потере любых объявлений, встроенных определений или других #include в файле. 2-й заголовок.
Трудности
[ редактировать ]Чтобы средства защиты #include работали правильно, каждое средство защиты должно протестировать и условно установить другой макрос препроцессора. Таким образом, проект, использующий средства защиты #include, должен разработать последовательную схему именования для своих средств защиты включения и убедиться, что его схема не конфликтует со схемой любых сторонних заголовков, которые он использует, или с именами любых глобально видимых макросов.
По этой причине большинство реализаций C и C++ предоставляют нестандартную #pragma once
директива. Эта директива, вставленная в начало файла заголовка, гарантирует, что файл будет включен только один раз. Язык Objective-C (который является расширенным набором языка C) представил #import
директива, которая работает точно так же, как #include
, за исключением того, что он включает каждый файл только один раз, что устраняет необходимость в #include. [3]
Другие языки
[ редактировать ]Некоторые языки поддерживают указание того, что код должен быть включен только один раз, во включающий файл, а не во включаемый файл (как в C/C++, включают защитные элементы и #pragma once
):
- PL/I использует
%INCLUDE
утверждение как эквивалент C#include
директива. IBM Enterprise PL/I также поддерживает%XINCLUDE
оператор, который «включит внешний текст в исходную программу, если он ранее не был включен». (Он также предлагаетXPROCEDURE
заявление, похожее наPROCEDURE
оператор, который будет игнорировать второе и последующие появленияXPROCEDURE
с тем же именем.) [4] - Objective- C
#import
директива (см. выше) - PHP
include_once
[5]
См. также
[ редактировать ]Ссылки
[ редактировать ]- ^ Стандарт C++ (ISO/IEC 14882), раздел 17.4.3.1.2/1
- ^ Стандарт C (ISO/IEC 9899), раздел 7.1.3/1.
- ^ «Цель C: Определение классов» . разработчик.apple.com . 17 сентября 2014 г. Проверено 3 октября 2018 г.
- ^ Корпорация IBM (август 2017 г.). Enterprise PL/I для z/OS PL/I для AIX Enterprise PL/I для z/OS Справочник по языку, версия 5, выпуск 1 (PDF) . п. 257 . Проверено 7 апреля 2022 г.
- ^ «include_once (Справочник по языку PHP)» .