прагма один раз - pragma once
В C и C ++ языков программирования, pragma once
является нестандартной , но широко поддерживаются директива препроцессора разработан , чтобы вызвать текущий исходный файл будет включена только один раз в одном сборнике. Таким образом, #pragma once
служит той же цели, что и include guards , но с рядом преимуществ, в том числе: меньший объем кода, предотвращение конфликтов имен, а иногда и повышение скорости компиляции. С другой стороны, #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
.
Предостережения
Определить один и тот же файл в файловой системе - нетривиальная задача. Символические ссылки и особенно жесткие ссылки могут привести к тому, что один и тот же файл будет найден под разными именами в разных каталогах. Компиляторы могут использовать эвристику, которая сравнивает размер файла, время модификации и содержимое. Кроме того, #pragma once
может поступить неправильно, если один и тот же файл намеренно скопирован в несколько частей проекта, например, при подготовке сборки. В то время как include guards по- прежнему будет защищать от двойных определений, #pragma once
может или не может обрабатывать их как один и тот же файл зависимым от компилятора способом. Эти трудности вместе с трудностями, связанными с определением того, что составляет один и тот же файл при наличии жестких ссылок, сетевых файловых систем и т. Д., До сих пор препятствовали стандартизации #pragma once
.
Использование макросов #include guard позволяет зависимому коду распознавать небольшие различия в семантике или интерфейсах конкурирующих альтернатив и реагировать на них. Например,
#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 guard .
#include
Директива определяется представлять намерение программиста на самом деле включает текст файла в точке директивы. Это может происходить несколько раз в пределах одной единицы компиляции и полезно для многократной оценки содержимого, содержащего макрос, на предмет изменения определений макроса.
Использование #pragma once
, как и использование макросов защиты #include в включаемом файле, возлагает ответственность на его авторов за защиту от нежелательного множественного включения. Чрезмерная зависимость программистов от любого механизма путем прямого, незащищенного использования #include
директив без собственной защиты #include приведет к сбою при использовании включаемого файла, который не защитил себя ни одним из механизмов.
Портативность
Компилятор | #pragma once |
---|---|
Лязг | Поддерживается |
Комо C / C ++ | Поддерживается |
Cray C и C ++ | Поддерживается (начиная с 9.0) |
C ++ Builder XE3 | Поддерживается |
Цифровой Марс C ++ | Поддерживается |
GCC | Поддерживается (официально с 3.4) |
HP C / aC ++ | Поддерживается (начиная с A.06.12) |
IBM XL C / C ++ | Поддерживается (начиная с 13.1.1) |
Компилятор Intel C ++ | Поддерживается |
Microsoft Visual C ++ | Поддерживается (начиная с 4.2) |
Компилятор NVIDIA CUDA | Поддерживается (в зависимости от базового компилятора хоста) |
Пеллес С | Поддерживается |
АРМ ДС-5 | Поддерживается |
IAR C / C ++ | Поддерживается |
Keil CC 5 | Поддерживается |
Oracle Developer Studio C / C ++ | Поддерживается (с 12.5) |
Портлендская группа C / C ++ | Поддерживается (начиная с версии 17.4) |
TinyCC | Поддерживается (с апреля 2015 г.) |
TASKING VX-toolset для TriCore: компилятор C | Поддерживается (начиная с v6.2r2) |