Что такое guard block в заголовочном файле c

Обновлено: 04.07.2024

В уроке «2.6 – Предварительные объявления и определения» мы отметили, что идентификатор переменной или функции может иметь только одно определение (правило одного определения). Таким образом, программа, которая определяет идентификатор переменной более одного раза, вызовет ошибку компиляции:

Точно так же программы, которые определяют функцию более одного раза, также вызовут ошибку компиляции:

Рассмотрим следующий академический пример:

Эта, казалось бы, невинно выглядящая программа не компилируется! Вот что происходит. Во-первых, main.cpp включает square.h , что копирует определение функции getSquareSides в main.cpp . Затем main.cpp включает geometry.h , который сам включает square.h . Это копирует содержимое square.h (включая определение функции getSquareSides ) в geometry.h , которое затем копируется в main.cpp .

Защита заголовка

Хорошей новостью является то, что мы можем избежать указанной выше проблемы с помощью механизма, называемого защитой заголовка (или защитой включения). Защита заголовка – это директивы условной компиляции, которые имеют следующую форму:

Все ваши заголовочные файлы должны иметь защиту заголовков. Имя SOME_UNIQUE_NAME_HERE может быть любым, но по соглашению устанавливается равным полному имени заголовочного файла, набранному заглавными буквами, с использованием подчеркивания вместо пробелов и знаков препинания. Например, у square.h будет защита заголовка будет следующей:

Даже заголовочные файлы стандартной библиотеки используют защиту заголовков. Если бы вы взглянули на заголовочный файл iostream из Visual Studio, вы бы увидели:

Для продвинутых читателей

В больших программах возможно наличие двух отдельных заголовочных файлов (включенных из разных каталогов) с одинаковыми именами (например, directoryA\config.h и directoryB\config.h ). Если для защиты включения используется только имя файла (например, CONFIG_H ), эти два файла могут в конечном итоге использовать одно и то же защитное имя. Если это произойдет, любой файл, который включает (прямо или косвенно) оба файла config.h , не получит содержимое включаемого файла, который будет включен вторым. Это, вероятно, вызовет ошибку компиляции.
Из-за этой возможности конфликтов защитных имен многие разработчики рекомендуют использовать более сложные/уникальные имена в защитах заголовков. Некоторые хорошие предложения – это соглашение об именах <ПРОЕКТ>_<ПУТЬ>_<ФАЙЛ>_H , <ФАЙЛ>_<БОЛЬШОЕ СЛУЧАЙНОЕ ЧИСЛО>_H или <ФАЙЛ>_<ДАТА СОЗДАНИЯ>_H .

Обновление нашего предыдущего примера с помощью защиты заголовков

Вернемся к примеру со square.h , добавив в него защиту заголовков. Чтобы быть последовательными мы также добавим защиту заголовков и в geometry.h .

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

Как видно из примера, второе включение содержимого square.h (из geometry.h ) игнорируется потому, что SQUARE_H уже был определен при первом включении. Следовательно, функция getSquareSides включается только один раз.

Защита заголовков не препятствует одиночным включениям заголовка в разные файлы исходного кода.

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

Обратите внимание, что square.h включается как из main.cpp , так и из square.cpp . Это означает, что содержимое square.h будет включено один раз в square.cpp и один раз в main.cpp .

Давайте разберемся, почему это происходит более подробно. Когда square.h включается из square.cpp , SQUARE_H определяется до конца square.cpp . Это определение предотвращает повторное включение square.h в square.cpp (что является целью защиты заголовков). Однако после завершения square.cpp SQUARE_H больше не считается определенным. Это означает, что когда препроцессор начинает работу над main.cpp , SQUARE_H в main.cpp изначально не определен.

Конечным результатом является то, что и square.cpp , и main.cpp получают копию определения getSquareSides . Эта программа будет компилироваться, но компоновщик будет жаловаться на то, что ваша программа имеет несколько определений для идентификатора getSquareSides !

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

Теперь, когда программа скомпилирована, функция getSquareSides будет иметь только одно определение (через square.cpp ), так что компоновщик будет счастлив. Файл main.cpp может вызывать эту функцию (даже если она находится в square.cpp ), потому что он включает square.h , в котором есть предварительное объявление для этой функции (компоновщик соединит вызов getSquareSides из main.cpp с определение getSquareSides в square.cpp ).

Разве мы не можем просто избежать определений в файлах заголовков?

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

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

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

В целях совместимости мы рекомендуем придерживаться традиционной защиты заголовков. Она не требуют много работы и гарантированно поддерживаются всеми компиляторами.

Резюме

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

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

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

Я пытаюсь создать класс C ++, используя IDE Code :: Blocks, и есть поле под названием «Блок защиты». Я провел поиск и не смог найти никакой полезной информации. Для чего это поле? Спасибо.

2 ответы

Блоки защиты используются для защиты от многократного включения файла заголовка одним и тем же модулем компиляции (файл c ++). Выглядят они примерно так:

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

ответ дан 21 окт '11, 00:10

Спасибо за быстрый ответ. Еще одна вещь: я вижу, что файл заголовка созданного мной класса включен в сам файл класса .cpp, что для меня не имеет смысла. Я не уверен, почему Code :: Blocks делает это = / - Chrislgarry

@Quicksort заголовок включен в .cpp, потому что типы и константы определены в заголовке, а классы объявлены там. - Дэвид Хеффернан

Спасибо, Дэвид. Боже, ты быстр с ответами. Таким образом, файл заголовка моего класса («Deck.cpp») просто предоставляет компилятору список всех объявлений, которые, возможно, могут возникнуть в main.cpp, без включения каких-либо деталей реализации, поэтому нет ошибки компиляции и, если элемент действительно создается в основном файле, компилятор затем ищет файл Deck.cpp, связанный с файлом заголовка, и вставляет детали реализации в main.cpp? - Chrislgarry

@Quicksort Хм. Это не совсем так, но довольно сложно вдаваться в кровавые подробности в комментариях. В основном файлы заголовков полезны тем, что они позволяют вам вызывать функции в других единицах перевода без необходимости записывать объявления более одного раза. Ключевым моментом здесь является понимание разницы между декларацией и определением. - Дэвид Хеффернан

OK. Я сделаю еще несколько поисков. Спасибо за помощь! - Chrislgarry

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

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

ответ дан 21 окт '11, 00:10

Это зависит. Если вы параноидально относитесь к переносимости, вы не будете ее использовать, потому что она не переносима, то есть не поддерживается стандартом. Но все популярные компиляторы реализуют это, поэтому на практике это не проблема. Если однажды вы наткнетесь на кого-то, чего нет, тогда достаточно легко переключиться на охранников старого стиля. - Дэвид Хеффернан

Не тот ответ, который вы ищете? Просмотрите другие вопросы с метками c++ ide header header-files codeblocks or задайте свой вопрос.

Я пытаюсь создать класс C++, используя Code::Blocks IDE, и есть поле под названием "Guard block.", я сделал поиск и не смог найти никакой полезной информации. Для чего это поле? Спасибо.

2 ответа

Я хочу знать, что такое статический блок в c или c++ с примером? Я знаю, что такое статика, но в чем разница между статикой и статическим блоком?

Защитные блоки используются для защиты от многократного включения файла заголовка одним и тем же блоком компиляции (файл c++). Они выглядят примерно так:

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

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

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

Похожие вопросы:

Я создал библиотеку функций, из которой хочу создать DLL и создать библиотеку экспорта. Создание DLL-это не проблема. Проблема в том, что я не хочу, чтобы разработчик/пользователь заглядывал внутрь.

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

Я изучаю C, но как мне заглянуть внутрь заголовочного файла? Таким образом, в Python вы бы сделали help(str), чтобы увидеть все функции части строки класса. Что эквивалентно этому в C?

Я хочу знать, что такое статический блок в c или c++ с примером? Я знаю, что такое статика, но в чем разница между статикой и статическим блоком?

Возможный Дубликат : Скрипт для отделения реализации от заголовков в файле .h У меня есть большой заголовочный файл, который содержит исходный код, например: class test_class_t < public: test_proc().

У меня есть библиотека с кодом c, и я хочу скомпилировать ее в .so lib с NDK, но когда я запускаю команду ndk-build, ей нужен заголовочный файл, а в этой библиотеке нет заголовочного файла, только.

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

Любой заголовочный файл C/C++ должен иметь следующую структуру.

Например, заголовочный файл myFunctions.h, в котором размещены объявления функций f и g, будет выглядеть так:

Зачем нужны заголовочные файлы

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

Указанное требование тривиально выполняется в простых случаях наподобие (считается, что компилятор просматривает программу сверху-вниз один раз — что, вообще говоря, неверно для современных компиляторов):

Однако если программу немного изменить, то требование уже нарушится:

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

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

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

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

вставляет целиком содержимое указанного заголовочного файла в текущее место исходного файла перед компиляцией (например, в main.c, если в нём встретилась эта строка). После такой вставки в main.c окажутся все объявления функций из файла myFunctions.h и компилятор будет счастлив.

В чём смысл стражей включения

Вкратце, директивы ifndef-define-endif, которые обрамляют любой грамотно оформленный заголовочный файл, являются трюком препроцессора: они обеспечивают то, что любой заголовочный файл будет включён в любой исходный файл не более одного раза.

Более подробно. Каждому заг. файлу «вручную» ставится в соответствие некоторый «символ», обычно связанный с именем этого файла, чтобы обеспечить уникальность. В первой строке проверяется, был ли уже определён этот символ ранее, если да, то весь остальной текст игнорируется. Если нет, то этот символ определяется, а затем вставляется и весь остальной текст заголовочного файла. Последняя строка (endif) просто означает закрытие такого «условного оператора».

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

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