Pragma once c что это
Перейти к содержимому

Pragma once c что это

  • автор:

 

Name already in use

cpp-docs / docs / preprocessor / once.md

  • Go to file T
  • Go to line L
  • Copy path
  • Copy permalink
  • Open with Desktop
  • View raw
  • Copy raw contents Copy raw contents

Copy raw contents

Copy raw contents

Specifies that the compiler includes the header file only once, when compiling a source code file.

The use of #pragma once can reduce build times, as the compiler won’t open and read the file again after the first #include of the file in the translation unit. It’s called the multiple-include optimization. It has an effect similar to the include guard idiom, which uses preprocessor macro definitions to prevent multiple inclusions of the contents of the file. It also helps to prevent violations of the one definition rule: the requirement that all templates, types, functions, and objects have no more than one definition in your code.

We recommend the #pragma once directive for new code because it doesn’t pollute the global namespace with a preprocessor symbol. It requires less typing, it’s less distracting, and it can’t cause symbol collisions. Symbol collisions are errors caused when different header files use the same preprocessor symbol as the guard value. It isn’t part of the C++ Standard, but it’s implemented portably by several common compilers.

There’s no advantage to use of both the include guard idiom and #pragma once in the same file. The compiler recognizes the include guard idiom, and implements the multiple-include optimization the same way as the #pragma once directive if no non-comment code or preprocessor directive comes before or after the standard form of the idiom:

We recommend the include guard idiom when code must be portable to compilers that don’t implement the #pragma once directive, to maintain consistency with existing code, or when the multiple-include optimization is impossible. It can occur in complex projects when file system aliasing or aliased include paths prevent the compiler from identifying identical include files by canonical path.

What does #pragma once mean in C? [duplicate]

I saw the pragma many times,but always confused, anyone knows what it does?Is it windows only?

4 Answers 4

It’s used to replace the following preprocessor code:

A good convention is adding both to support legacy compilers (which is rare though):

So if #pragma once fails the old method will still work.

2023 update

I see some people in the comment section advocate for using guards instead of #pragma once . This makes little to no sense in 2023 and beyond unless you are targeting some special compiler that you know does not support #pragma once .

Today’s best practice is to use only #pragma once and don’t bother with guards at all. Reasons being

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

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

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

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

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

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

Таким образом, после разрешения всех директив #include файл main.cpp будет выглядеть так:

Повторяющиеся определения и ошибка компиляции. Каждый файл по отдельности нормальный. Однако, поскольку main.cpp дважды включает через #include содержимое square.h , мы столкнулись с проблемами. Если для geometry.h требуется getSquareSides() , а для main.cpp требуются и geometry.h , и square.h , как бы вы решили эту проблему?

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

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

Когда этот заголовок включается, препроцессор проверяет, был ли ранее определен SOME_UNIQUE_NAME_HERE . Если это первый раз, когда мы включаем этот заголовок, SOME_UNIQUE_NAME_HERE не будет определен. Следовательно, он с помощью #define определяет SOME_UNIQUE_NAME_HERE и включает содержимое файла. Если заголовок включен в тот же файл повторно, SOME_UNIQUE_NAME_HERE уже будет определен из первого раза, когда содержимое заголовка было включено, и в этот раз содержимое заголовка будет проигнорировано (благодаря #ifndef ).

Все ваши заголовочные файлы должны иметь защиту заголовков. Имя 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++ позволяет вам создавать свои собственные типы. Эти пользовательские типы обычно определяются в файлах заголовков, чтобы эти определения можно было распространить на исходные файлы, которые должны их использовать. Без защиты заголовков ваши исходные файлы могут иметь несколько идентичных копий этих определений, что приведет к ошибке компиляции повторяющихся определений.

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

#pragma once

Многие компиляторы поддерживают более простую альтернативную форму защиты заголовков с помощью директивы #pragma :

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

Однако #pragma once не является официальной частью языка C++, и не все компиляторы поддерживают ее (хотя большинство современных компиляторов поддерживает).

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

Резюме

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

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

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

Внедрение контроля определенного поведения

Поведение, определяемое реализацией, контролируется директивой #pragma .

Syntax

#pragma pragma-params (1)
_Pragma( string-literal ) (2) (since C++11)

Explanation

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

Non-standard pragmas

Стандарт языка ISO C++не требует от компиляторов поддержки каких-либо прагм.Тем не менее,несколько нестандартных прагм поддерживаются несколькими реализациями:

#pragma STDC

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

#pragma STDC FENV_ACCESS arg (1)
#pragma STDC FP_CONTRACT arg (2)
#pragma STDC CX_LIMITED_RANGE arg (3)

где arg имеет значение ON , OFF или DEFAULT .

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

Примечание: компиляторы, которые не поддерживают эти прагмы, могут предоставлять эквивалентные параметры времени компиляции, такие как gcc -fcx-limited-range и -ffp-contract .

#pragma once

#pragma once — это нестандартная прагма, которая поддерживается подавляющим большинством современных компиляторов . Если он появляется в заголовочном файле, это означает, что его нужно проанализировать только один раз, даже если он (прямо или косвенно) включен несколько раз в один и тот же исходный файл.

Стандартный подход к предотвращению многократного включения одного и того же заголовка заключается в использовании include guard :

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

При использовании #pragma once и тот же заголовок отображается как.

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

#pragma pack

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

#pragma pack(arg) (1)
#pragma pack() (2)
#pragma pack(push) (3)
#pragma pack(push, arg) (4)
#pragma pack(pop) (5)

где arg-малая степень двойки и задает новое выравнивание в байтах.

#pragma pack может уменьшить выравнивание класса, однако он не может привести к перевыравниванию класса.

 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *