Использование #pragma в C

118

Какие #pragmaпримеры использования C в C?

MD XF
источник
2
#pragmaДиректива переживает стадию предварительной обработки. В отличие от #includeи #define.
smwikipedia
Возможный дубликат Что означает #pragma once в C?
@smwikipedia, вы имеете в виду, что некоторые прагмы выживают? #pragma once - это директива препроцессора, но #pragma pack - это директива компилятора
Льюис Келси,

Ответы:

66

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

См. Msdn для получения дополнительной информации.

Стивен А. Лоу
источник
11
«это может или не может применяться ко всем машинам и операционным системам». - и разные компиляторы на одной машине. И это может означать разные вещи в разных компиляторах.
Стив Джессоп,
53

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

Я регулярно использую тот, #pragma pack(1)где я пытаюсь выжать больше из своего пространства памяти на встроенных решениях с массивами структур, которые в противном случае закончились бы 8-байтовым выравниванием.

Жалко, что у нас #dogmaеще нет. Было бы весело;)

SmacL
источник
@ShaneMacLaughlin, pragma(1)Скорость тоже реально не улучшает? См stackoverflow.com/questions/3318410/...
Pacerier
4
@Pacerier, как правило, нет. Согласно комментариям jalfs, данные, которые выровнены по 4-байтовой границе для 32-битных процессоров или 8-байтовой границе для 64-битных процессоров, обычно загружаются и сохраняются за одну операцию. Данные, которые выровнены по меньшим границам, потребуют нескольких операций для загрузки или сохранения. Это медленнее.
SmacL
35

Обычно я бы старался избегать использования #pragmas, если это возможно, поскольку они сильно зависят от компилятора и не переносятся. Если вы хотите использовать их в портативном режиме, вам придется окружить каждую прагму парой #if/ #endif. GCC не рекомендует использовать прагмы и поддерживает только некоторые из них для совместимости с другими компиляторами; У GCC есть другие способы делать те же вещи, для которых другие компиляторы используют прагмы.

Например, вот как вы можете убедиться, что структура плотно упакована (т.е. без заполнения между членами) в MSVC:

#pragma pack(push, 1)
struct PackedStructure
{
  char a;
  int b;
  short c;
};
#pragma pack(pop)
// sizeof(PackedStructure) == 7

Вот как бы вы сделали то же самое в GCC:

struct PackedStructure __attribute__((__packed__))
{
  char a;
  int b;
  short c;
};
// sizeof(PackedStructure == 7)

Код GCC более переносим, ​​потому что если вы хотите скомпилировать его с помощью компилятора, отличного от GCC, все, что вам нужно сделать, это

#define __attribute__(x)

Если вы хотите перенести код MSVC, вы должны окружить каждую прагму парой #if/ #endif. Не очень.

Адам Розенфилд
источник
3
Итак, если я хочу скомпилировать код GCC на MSVC и мне нужно упаковать структуру, как именно мне это сделать?
SmacL 06
2
Для gcc это struct __attribute__((__packed__)) PackedStructure
Лоран Дебрикон
#pragma once не реально "зависит от компилятора и не переносится". Он поддерживается на всех основных платформах и на многих
неосновных
1
Обратите внимание, что C99 и C11 оба содержат (C11) §6.10.6 директивы Pragma и ¶1. Любая такая прагма, которая не распознается реализацией, игнорируется. Даже C90 говорит это, хотя это было в разделе §6.8.6. (Это делает GCC несовместимым, если он запускается, hackкогда встречает прагму, которую не распознает, как это было когда-то очень, очень давно - см. #pragmaGCC и т. Д.)
Джонатан Леффлер,
15

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

Альтернативой является использование включенных охранников (например #ifndef MY_FILE #define MY_FILE ... #endif /* MY_FILE */)

Schildmeijer
источник
7

то, что я чувствую, #pragmaэто директива, в которой, если вы хотите, чтобы код был специфичным для местоположения. скажите ситуацию, когда вы хотите, чтобы счетчик программы читал с определенного адреса, по которому написана ISR, тогда вы можете указать ISR в этом месте, используя #pragma vector=ADC12_VECTORи следуя название прерывания и его описание

Сандип
источник
5

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

#pragma code BANK1
#pragma data BANK2

#pragma INT3 TimerHandler
Только в любви
источник
3
Все прагмы зависят от реализации, за исключением прагм #pragma STDC ..., которые стандартизированы для всех платформ (в дополнение к C99).
Джонатан Леффлер,
4

Все ответы выше хорошо объясняют, #pragmaно я хотел добавить небольшой пример

Я просто хочу объяснить simple OpenMP example, как можно использовать #pragmaего для работы.

OpenMp briefly- это реализация для многоплатформенного параллельного программирования с общей памятью (тогда мы можем сказать, что это machine-specificили operating-system-specific)

перейдем к примеру

#include <stdio.h>
#include <omp.h>// compile with: /openmp

int main() {
   #pragma omp parallel num_threads(4)
   {
      int i = omp_get_thread_num();
      printf_s("Hello from thread %d\n", i);
   }
}

выход

Hello from thread 0
Hello from thread 1
Hello from thread 2
Hello from thread 3

Note that the order of output can vary on different machines.

теперь позвольте мне рассказать вам, что #pragmaсделал ...

он сообщает ОС запустить некоторый блок кода в 4 потоках

это только один из many many applicationsвас, который может сделать с маленьким#pragma

извините за внешний образец OpenMP

Башир АЛЬ-МОМАНИ
источник
3

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

Он бывает двух типов #pragma startup, #pragma exitи #pragma warn.

#pragma startup позволяет нам определять функции, вызываемые при запуске программы.

#pragma exit позволяет нам указывать функции, вызываемые при выходе из программы.

#pragma warn сообщает компьютеру подавить любое предупреждение или нет.

Многие другие #pragmaстили можно использовать для управления компилятором.

Суреш Парик
источник
3

#pragma startup - это директива, которая используется для вызова функции перед основной функцией и для вызова другой функции после основной функции, например

#pragma startup func1
#pragma exit func2

Здесь func1выполняется до mainи func2после.

ПРИМЕЧАНИЕ. Этот код работает только в компиляторе Turbo-C. Для достижения этой функциональности в GCC, вы можете объявить func1и func2как это:

void __attribute__((constructor)) func1();
void __attribute__((destructor)) func2();
Sparkzz
источник
2

Подводя итог, #pragmaговорит компилятору что-то делать. Вот несколько способов его использования:

  • #pragmaможет использоваться для игнорирования предупреждений компилятора. Например, чтобы заставить GCC замолчать о неявных объявлениях функций, вы можете написать:

    #pragma GCC diagnostic ignored "-Wimplicit-function-declaration"

    Более старая версия libportableделает это переносимо .

  • #pragma onceпри записи в верхней части файла заголовка приведет к однократному включению указанного файла заголовка. libportable проверяет наличие прагмы после поддержки.

MD XF
источник