Zastosowanie #pragma w C

117

Jakie są zastosowania #pragmaw języku C wraz z przykładami?

MD XF
źródło
2
#pragmaDyrektywa przetrwa etap wstępnego przetwarzania. W przeciwieństwie do #includei #define.
smwikipedia
@smwikipedia masz na myśli przetrwanie niektórych pragm? #pragma kiedyś jest dyrektywą preprocesora, ale #pragma pack to dyrektywa kompilatora
Lewis Kelsey

Odpowiedzi:

66

#pragma jest przeznaczony dla dyrektyw kompilatora, które są specyficzne dla maszyny lub systemu operacyjnego, tj. mówi kompilatorowi, aby coś zrobił, ustawił jakąś opcję, wykonał jakąś akcję, nadpisał niektóre domyślne itp., które mogą, ale nie muszą dotyczyć wszystkich maszyn i działających systemy.

Aby uzyskać więcej informacji, zobacz msdn .

Steven A. Lowe
źródło
11
„to może, ale nie musi dotyczyć wszystkich komputerów i systemów operacyjnych”. - i różne kompilatory na tej samej maszynie. A co może oznaczać różne rzeczy na różnych kompilatorach.
Steve Jessop,
53

#pragma jest używany do zrobienia czegoś specyficznego dla implementacji w C, tj. jest pragmatyczny w obecnym kontekście, a nie ideologicznie dogmatyczny.

Ten, którego regularnie używam, polega na #pragma pack(1)tym, że staram się wycisnąć więcej z mojej pamięci w rozwiązaniach osadzonych, z tablicami struktur, które w przeciwnym razie zakończyłyby się wyrównaniem 8 bajtów.

Szkoda, że ​​jeszcze nie mamy #dogma. To byłoby fajne ;)

SmacL
źródło
@ShaneMacLaughlin, czy też nie pragma(1)poprawia szybkości? Zobacz stackoverflow.com/questions/3318410/…
Pacerier
4
@Pacerier, zazwyczaj nie. Zgodnie z komentarzami jalfs, dane, które są wyrównane do 4-bajtowej granicy dla procesorów 32-bitowych lub 8-bajtowej dla procesorów 64-bitowych, będą zazwyczaj ładowane i przechowywane w ramach jednej operacji. Dane, które są wyrównane na mniejszych granicach, będą wymagały wielu operacji, aby załadować lub zapisać. To jest wolniejsze.
SmacL
35

Generalnie starałbym się unikać używania #pragmas, jeśli to możliwe, ponieważ są one bardzo zależne od kompilatora i nieprzenośne. Jeśli chcesz używać ich w sposób przenośny, musisz otoczyć każdą pragmę parą #if/ #endif. GCC odradza używanie pragm i tak naprawdę obsługuje tylko niektóre z nich w celu zapewnienia zgodności z innymi kompilatorami; GCC ma inne sposoby robienia tych samych rzeczy, do których inne kompilatory używają pragm.

Na przykład, oto jak upewnić się, że struktura jest ciasno upakowana (tj. Bez dopełnienia między elementami) w MSVC:

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

Oto jak zrobiłbyś to samo w GCC:

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

Kod GCC jest bardziej przenośny, ponieważ jeśli chcesz skompilować go za pomocą kompilatora innego niż GCC, wszystko, co musisz zrobić, to

#define __attribute__(x)

Natomiast jeśli chcesz przenieść kod MSVC, musisz otoczyć każdą pragmę za pomocą #if/ #endifpair. Niezbyt ładne.

Adam Rosenfield
źródło
3
Więc jeśli chcę skompilować kod GCC na MSVC i muszę spakować strukturę, jak dokładnie mam to zrobić?
SmacL
2
Dla gcc jest to struct __attribute__((__packed__)) PackedStructure
Laurent Debricon
#pragma raz nie jest realistycznie „zależna od kompilatora i nieprzenośna”. Jest obsługiwany na każdej większej platformie i wielu
mniejszych
1
Zauważ, że oba C99 i C11 zawierają (C11) §6.10.6 dyrektywy Pragmy, a ¶1. Każda taka pragma, która nie jest rozpoznawana przez implementację, jest ignorowana. Nawet C90 tak mówi, chociaż było to w sekcji §6.8.6. (To sprawia, że ​​GCC jest niezgodne, jeśli działa, hackgdy napotyka pragmę, której nie rozpoznaje, jak to miało miejsce kiedyś, bardzo, bardzo dawno temu - zobacz #pragmai GCC itp.)
Jonathan Leffler
15

Umieszczenie #pragma oncena górze pliku nagłówkowego zapewni, że zostanie on uwzględniony tylko raz. Zauważ, że #pragma oncenie jest to standardowe C99, ale obsługiwane przez większość nowoczesnych kompilatorów.

Alternatywą jest użycie osłon (np. #ifndef MY_FILE #define MY_FILE ... #endif /* MY_FILE */)

Schildmeijer
źródło
7

czuję, że jest #pragmato dyrektywa, w której jeśli chcesz, aby kod był specyficzny dla lokalizacji. powiedz sytuację, w której chcesz, aby licznik programu czytał z określonego adresu, pod którym jest zapisany ISR, możesz określić ISR w tej lokalizacji za pomocą #pragma vector=ADC12_VECTORi następnie przerwać rotines nazwa i jej opis

Sandeep
źródło
5

Moją najlepszą radą jest przyjrzenie się dokumentacji kompilatora, ponieważ pragmy są z definicji specyficzne dla implementacji. Na przykład w projektach osadzonych używałem ich do lokalizowania kodu i danych w różnych sekcjach lub deklarowania programów obsługi przerwań. to znaczy:

#pragma code BANK1
#pragma data BANK2

#pragma INT3 TimerHandler
Po prostu zakochany
źródło
3
Wszystkie pragmy są specyficzne dla implementacji - z wyjątkiem pragm #pragma STDC ..., które są ujednolicone na wszystkich platformach (dodatek do C99).
Jonathan Leffler
4

Wszystkie powyższe odpowiedzi stanowią miłe wyjaśnienia, #pragmaale chciałem dodać mały przykład

Chcę tylko wyjaśnić, simple OpenMP exampleco pokazuje niektóre zastosowania programu #pragmado wykonywania jego pracy

OpenMP brieflyto implementacja dla wielu platform programowania równoległego wspólną pamięcią (wtedy możemy powiedzieć, że jest machine-specificlub operating-system-specific)

przejdźmy do przykładu

#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);
   }
}

wyjście jest

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.

teraz powiem ci, co #pragmazrobiło ...

mówi systemowi operacyjnemu, aby uruchomił jakiś blok kodu w 4 wątkach

to tylko jedna z many many applicationsrzeczy, które możesz zrobić z małym#pragma

przepraszam za zewnętrzną próbkę OpenMP

Basheer AL-MOMANI
źródło
3

Jest to dyrektywa preprocesora, której można używać do włączania lub wyłączania niektórych funkcji.

Jest dwóch typów #pragma startup, #pragma exiti #pragma warn.

#pragma startup pozwala nam określić funkcje wywoływane podczas uruchamiania programu.

#pragma exit pozwala nam określić funkcje wywoływane po zakończeniu programu.

#pragma warn informuje komputer, aby pominął wszelkie ostrzeżenia lub nie.

Do #pragmasterowania kompilatorem można użyć wielu innych stylów.

suresh pareek
źródło
3

#pragma startup to dyrektywa, która jest używana do wywołania funkcji przed funkcją główną i do wywołania innej funkcji po funkcji głównej, np

#pragma startup func1
#pragma exit func2

Tutaj func1biegnie przed maini func2biegnie później.

UWAGA: Ten kod działa tylko w kompilatorze Turbo-C. Aby osiągnąć tę funkcjonalność w GCC, możesz zadeklarować func1i func2tak:

void __attribute__((constructor)) func1();
void __attribute__((destructor)) func2();
Sparkzz
źródło
2

Podsumowując, #pragmamówi kompilatorowi, aby coś zrobił. Oto kilka sposobów, w jakie go używam:

  • #pragmamoże służyć do ignorowania ostrzeżeń kompilatora. Na przykład, aby zamknąć GCC na niejawne deklaracje funkcji, możesz napisać:

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

    Starsza wersja libportablerobi to przenośnie .

  • #pragma oncezapisany na górze pliku nagłówkowego spowoduje jednorazowe dołączenie wspomnianego pliku nagłówkowego. libportable sprawdza pragmę po wsparciu.

MD XF
źródło