прагма один раз
В C и C++ языках программирования #pragma once
— это нестандартная, но широко поддерживаемая директива препроцессора , предназначенная для включения текущего файла заголовка только один раз в одну компиляцию. [1] Таким образом, #pragma once
служит той же цели, что и include Guards , но имеет ряд преимуществ, включая меньшее количество кода, избежание конфликтов имен и иногда улучшение скорости компиляции. [2] Пока #pragma once
доступен в большинстве современных компиляторов, его реализация сложна и не всегда надежна.
Пример
[ редактировать ]- Файл "grandparent.h"
#pragma once
struct foo
{
int member;
};
- Файл «parent.h»
#include "grandparent.h"
- Файл "child.c"
#include "grandparent.h"
#include "parent.h"
В этом примере включение grandparent.h
в обоих parent.h
и child.c
обычно вызывает ошибку компиляции, поскольку структура с заданным именем может быть определена только один раз в данной компиляции. #pragma once
Директива служит для того, чтобы избежать этого, игнорируя последующие включения grandparent.h
.
Преимущества
[ редактировать ]С использованием #pragma once
позволяет препроцессору C включать заголовочный файл, когда это необходимо, и игнорировать #include
директива иначе. Это приводит к изменению поведения самого препроцессора C и позволяет программистам выражать зависимости файлов простым способом, устраняя необходимость ручного управления.
Самая распространенная альтернатива #pragma once
это использовать #define
чтобы установить макрос защиты #include , имя которого выбирается программистом как уникальное для этого файла. Например,
#ifndef GRANDPARENT_H
#define GRANDPARENT_H
... contents of grandparent.h
#endif /* !GRANDPARENT_H */
Такой подход минимально гарантирует, что содержимое включаемого файла не будет просмотрено более одного раза. Это более многословно, требует большего ручного вмешательства и подвержено ошибкам программиста, поскольку у компилятора нет механизмов для предотвращения случайного использования одного и того же имени макроса в более чем одном файле, что приведет к созданию только одного из файлов. быть включенным. Такие ошибки вряд ли останутся незамеченными, но могут усложнить интерпретацию отчета об ошибках компилятора. Поскольку за обработку отвечает сам препроцессор. #pragma once
, программист не может допускать ошибок, вызывающих конфликты имен.
При отсутствии #include охранников вокруг #include
директивы, использование #pragma once
улучшит скорость компиляции для некоторых компиляторов, поскольку это механизм более высокого уровня; сам компилятор может сравнивать имена файлов или индексные дескрипторы без необходимости вызывать препроцессор C для сканирования заголовка на наличие #ifndef
и #endif
. Тем не менее, поскольку защита включения появляется очень часто, а накладные расходы на открытие файлов значительны, компиляторы обычно оптимизируют обработку защиты включения, делая их максимально быстрыми. #pragma once
. [3] [4] [5]
Предостережения
[ редактировать ]Идентификация одного и того же файла в файловой системе — нетривиальная задача. [6] Символические ссылки и особенно жесткие ссылки могут привести к тому, что один и тот же файл будет найден под разными именами в разных каталогах. Компиляторы могут использовать эвристику, которая сравнивает размер файла, время модификации и содержимое. [7] Кроме того, #pragma once
может поступить неправильно, если один и тот же файл намеренно скопирован в несколько частей проекта, например, при подготовке сборки. Хотя включение охранников все равно защитит от двойных определений, #pragma once
может или не может рассматривать их как один и тот же файл в зависимости от компилятора. Эти трудности, а также трудности, связанные с определением того, что представляет собой один и тот же файл при наличии жестких ссылок, сетевых файловых систем и т. д., до сих пор препятствовали стандартизации #pragma once
. [ нужна ссылка ]
Использование защитных макросов #include позволяет зависимому коду распознавать небольшие различия в семантике или интерфейсах конкурирующих альтернатив и реагировать на них. Например,
#include TLS_API_MACRO /* defined on the command line */
...
#if defined TLS_A_H
... use one known API
#elif defined TLS_B_H
... use another known API
#else
#error "unrecognized TLS API"
#endif
В этом случае для прямого определения доступности API будет использоваться тот факт, что включаемый файл объявил себя с помощью своего защитного макроса #include.
The #include
Директива определяется как представление намерения программиста фактически включить текст файла в точку директивы. Это может произойти несколько раз в пределах одной единицы компиляции и полезно для многократной оценки содержимого, содержащего макрос, на предмет изменения определений макроса.
Использование #pragma once
, например, использование защитных макросов #include во включаемом файле возлагает ответственность на его авторов в целях защиты от нежелательного множественного включения. Чрезмерная зависимость от любого механизма со стороны программистов путем прямого, незащищенного использования #include
Директивы без собственной защиты #include приведут к сбою при использовании включаемого файла, который не защитил себя ни одним из механизмов.
Портативность
[ редактировать ]Компилятор | Поддерживать |
---|---|
Кланг | Да [8] |
Комо C/C++ | Да [9] |
Крей C и C++ | Да [10] (начиная с версии 9.0) |
C++Builder | Да [11] (только начиная с классического компилятора XE3.) |
Цифровой Марс C++ | Да [12] |
Коллекция компиляторов GNU (GCC) | Да [13] (официально с версии 3.4 [6] [14] ) |
HP C/aC++ | Да [15] (по крайней мере, с A.06.12) |
IBM XL C/С++ | Да [16] (начиная с версии 13.1.1) |
Intel C++-компилятор | Да [17] |
Microsoft Visual С++ | Да [18] [19] (начиная с версии 4.2) |
Компилятор NVIDIA CUDA | Да (в зависимости от базового компилятора хоста) |
Экскаваторы С | Да [20] |
АРМ ДС-5 | Да [21] |
И Си/С++ | Да [22] |
Инструменты для микроконтроллера Arm Keil: компиляторы C/C++ | Да [23] (Я КЕЙЛ АРМСС 5) |
OpenWatcom | Да [24] |
Oracle Developer Studio C/C++ | Да [25] (с версии 12.5) |
Портлендская группа C/C++ | Да [26] (начиная с версии 17.4) |
TinyCC | Да [27] (с апреля 2015 г.) |
СДКК | Нет [28] (по состоянию на апрель 2024 г.) |
ЗАДАЧИ Набор инструментов VX для TriCore: компилятор C | Да [29] (начиная с версии 6.2r2) |
Инструменты генерации кода Texas Instruments: компилятор C | Да [30] (например, MSP430, ARM, C2000) |
Ссылки
[ редактировать ]- ^ "один раз" . Документы Майкрософт . 3 ноября 2016 г. Проверено 25 июля 2019 г.
- ^ «Игры изнутри: еще больше экспериментов с включениями» . 25 января 2005 г. Архивировано из оригинала 30 сентября 2008 года . Проверено 19 августа 2013 г.
- ^ «Препроцессор C: 1. Препроцессор C» . Gcc.gnu.org. 1 февраля 1996 г. Проверено 19 августа 2013 г.
- ^ « Руководство по внутреннему устройству CFE «Clang» — документация Clang 3.4» . Clang.llvm.org . Проверено 19 августа 2013 г.
- ^ «clang: процедуры манипуляции файлами» . Clang.llvm.org . Проверено 19 августа 2013 г.
- ^ Перейти обратно: а б «Серия выпусков GCC 3.4 — изменения, новые функции и исправления» . Gcc.gnu.org . Проверено 19 августа 2013 г.
- ^ «функция must_stack_file() в исходном коде GCC» .
- ^ «clang: clang: Исходный файл Pragma.cpp» . Clang.llvm.org. Архивировано из оригинала 4 апреля 2014 г. Проверено 19 августа 2013 г.
- ^ «Предварительная пользовательская документация Comeau C++: Pragmas» . Comeaucomputing.com. Архивировано из оригинала 11 декабря 2013 г. Проверено 19 августа 2013 г.
- ^ «Обзор выпуска CCE 9.0.0. Введение S-5212» . Cray Inc. 01.06.2019 . Проверено 23 сентября 2019 г.
- ^ «#pragma Once — RAD Studio XE3» . Docwiki.embarcadero.com. 02.12.2010 . Проверено 19 августа 2013 г.
- ^ «Прагмы» . Цифровой Марс . Проверено 19 августа 2013 г.
- ^ «Альтернативы обертке #ifndef» . Gcc.gnu.org . Проверено 20 августа 2013 г.
- ^ «Ошибка GCC 11569 — нет замены #pragma Once» . 18 июля 2003 г. Проверено 21 октября 2020 г.
- ^ «Руководство программиста HP aC++/HP C A.06.29; март 2016 г. (AR1603)» .
- ^ «Прагмы GCC» . ИБМ . Проверено 20 февраля 2015 г.
- ^ «Диагностика 1782: #pragma Once устарела. Вместо этого используйте #ifndef Guard» . Зоны разработчиков Intel . Архивировано из оригинала 31 января 2012 года . Проверено 4 декабря 2013 г.
#pragma Once должна продолжать работать (в настоящее время НЕ устарела) с компилятором Intel.
- ^ «один раз (C/C++)» . Сеть разработчиков Microsoft. Архивировано из оригинала 10 августа 2016 г. Проверено 19 августа 2013 г.
- ^ «однажды прагма | Microsoft Docs» .
- ^ Справка/документация IDE
- ^ «Информационный центр АРМ» . РУКА . Проверено 17 декабря 2013 г.
- ^ «Руководство по разработке IAR C/C++» (PDF) . ИАР СИСТЕМЫ . Проверено 1 марта 2023 г.
- ^ «Прагмы, распознаваемые компилятором» . Кейл.
- ^ «#pragma Once не работает, если файлы, на которые ссылаются альтернативные пути» .
Теперь это должно быть исправлено в репозитории git.
- ^ «Oracle® Developer Studio 12.5: Руководство по совместимости с GCC» . Оракул . Проверено 26 июля 2016 г.
- ^ «Портлендская группа» . Проверено 31 июля 2016 г.
- ^ «Прагма TinyCC после реализации» . Проверено 19 июня 2018 г.
- ^ «Руководство пользователя компилятора SDCC, раздел 3.16, стр. 62» (PDF) . Проверено 11 апреля 2024 г.
- ^ «МА160-800 (v6.2r2) 13 марта 2018 г., стр. 92» (PDF) .
- ^ «[EXT_EP-8185] Документ #pragma Once» . Встроенное программное обеспечение и инструменты . Отчет о проблемах с программным обеспечением — Texas Instruments. Архивировано из оригинала 29 января 2022 года.