прагма один раз - 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)

Рекомендации

Внешние ссылки