~ pragma Pack effect

Zastanawiałem się, czy ktoś mógłby mi wyjaśnić, co robi instrukcja preprocesora #pragma pack, a co ważniejsze, dlaczego ktoś chciałby z niej korzystać.

Sprawdziłem Stronę MSDN, która oferowała pewien wgląd, ale miałem nadzieję usłyszeć więcej od ludzi z doświadczeniem. Widziałem to już w kodzie, choć nie mogę już znaleźć gdzie.

Author: Mohammadreza Panahi, 2010-07-23

11 answers

#pragma pack poleca kompilatorowi pakowanie elementów struktury ze szczególnym wyrównaniem. Większość kompilatorów, deklarując strukturę, wstawia odstępy między składnikami, aby upewnić się, że są one wyrównane do odpowiednich adresów w pamięci (Zwykle wielokrotność rozmiaru typu). Pozwala to uniknąć kary za wydajność (lub oczywistego błędu) na niektórych architekturach związanych z dostępem do zmiennych, które nie są prawidłowo wyrównane. Na przykład, podane 4-bajtowe liczby całkowite i następujące struct:

struct Test
{
   char AA;
   int BB;
   char CC;
};

Kompilator może wybrać układ struktury w pamięci w następujący sposób:

|   1   |   2   |   3   |   4   |  

| AA(1) | pad.................. |
| BB(1) | BB(2) | BB(3) | BB(4) | 
| CC(1) | pad.................. |

I sizeof(Test) byłyby 4 × 3 = 12, mimo że zawierają tylko 6 bajtów danych. Najczęstszym przypadkiem użycia #pragma (według mojej wiedzy) jest praca z urządzeniami sprzętowymi, gdzie należy upewnić się, że kompilator nie wstawia wypełnienia do danych i każdy członek podąża za poprzednim. Z #pragma pack(1), powyższa struktura byłaby ułożona w następujący sposób:

|   1   |

| AA(1) |
| BB(1) |
| BB(2) |
| BB(3) |
| BB(4) |
| CC(1) |

I sizeof(Test) byłoby 1 × 6 = 6.

Z #pragma pack(2), powyższa struktura byłaby ułożona w następujący sposób:

|   1   |   2   | 

| AA(1) | pad.. |
| BB(1) | BB(2) |
| BB(3) | BB(4) |
| CC(1) | pad.. |

I sizeof(Test) byłoby 2 × 4 = 8.

 321
Author: Nick Meyer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-14 16:10:04

#pragma jest używany do wysyłania Nie-przenośnych (jak tylko w tym kompilatorze) Wiadomości do kompilatora. Rzeczy takie jak wyłączenie niektórych ostrzeżeń i struktury pakowania są częstymi przyczynami. Wyłączenie określonych ostrzeżeń jest szczególnie przydatne, jeśli kompilujesz z włączoną flagą warnings as errors.

#pragma pack specific is used to indicate that the struct to packed should not have its members aligned. Jest to przydatne, gdy masz interfejs mapowany pamięcią do elementu sprzętowego i musisz być w stanie kontroluj dokładnie, gdzie wskazują poszczególne elementy struktury. Nie jest to szczególnie dobra optymalizacja prędkości, ponieważ większość maszyn znacznie szybciej radzi sobie z wyrównanymi danymi.

 20
Author: nmichaels,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-23 13:24:53

Mówi kompilatorowi, że granica ma wyrównywać obiekty w strukturze do. Na przykład, jeśli mam coś takiego:

struct foo { 
    char a;
    int b;
};

W przypadku typowej maszyny 32-bitowej Zwykle "chcesz" mieć 3 bajty wypełnienia między a A b, Aby b wylądował na granicy 4-bajtów, aby zmaksymalizować prędkość dostępu (i to zwykle będzie się działo domyślnie).

Jeśli jednak musisz dopasować zewnętrznie zdefiniowaną strukturę, chcesz mieć pewność, że kompilator ułoży twoją strukturę dokładnie zgodnie z tą zewnętrzną definicją. W tym przypadku, możesz dać kompilatorowi #pragma pack(1), Aby powiedział mu , a nie , aby wstawił jakiekolwiek odstępy między członkami - jeśli definicja struktury zawiera odstępy między członkami, wstawiasz je jawnie (np. zazwyczaj z członami o nazwie unusedN lub ignoreN, lub coś w tej kolejności).

 15
Author: Jerry Coffin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-23 13:21:07
  1. #pragma pack (n) Po prostu ustawia nowe wyrównanie.
  2. #pragma pack () ustawia wyrównanie do tego, które miało miejsce w momencie rozpoczęcia kompilacji.
  3. #pragma pack (push[,n]) wypycha bieżące ustawienie wyrównania na stosie wewnętrznym, a następnie opcjonalnie ustawia nowe wyrównanie.
  4. #pragma pack (pop) przywraca ustawienie wyrównania do Ustawienia zapisanego na górze wewnętrznego stosu(i usuwa ten wpis). Zauważ, że # pragma pack ([n]) nie wpĹ ' ywaj na ten wewnÄ ™ trzny stos; w ten sposĂłb moĹźna mieÄ ‡ #pragma pack(push), a nastÄ ™ pnie kilka wystÄ ™ pĂłw #pragma pack(n) i finalizowaä ‡ pojedynczym #pragma pack(pop).

Przykłady:

#pragma pack(push, 1) // exact fit - no padding
struct MyStruct
{
  char b; 
  int a; 
  int array[2];
};
#pragma pack(pop) //back to whatever the previous packing mode was 

Or

#pragma pack(1) // exact fit - no padding
struct MyStruct
{
  char b; 
  int a; 
  int array[2];
};
#pragma pack() //back to whatever the previous packing mode was 


Or 

#pragma pack(1) // exact fit - no padding
struct MyStruct
{
  char b; 
  int a; 
  int array[2];
}; 
 9
Author: Yogeesh H T,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-07 07:11:51

Elementy danych (np. członkowie klas i struktur) są zazwyczaj wyrównywane na granicach WORD lub DWORD dla procesorów obecnej generacji w celu poprawy czasu dostępu. Pobranie DWORDU pod adresem, który nie jest podzielny przez 4, wymaga co najmniej jednego dodatkowego cyklu procesora na procesorze 32-bitowym. Tak więc, jeśli masz np. trzy elementy char char a, b, c;, zajmują one zwykle 6 lub 12 bajtów pamięci.

#pragma pozwala to zastąpić, aby osiągnąć bardziej efektywne wykorzystanie przestrzeni, kosztem szybkości dostępu, lub dla spójności przechowywanych danych pomiędzy różnymi celami kompilatora. Miałem dużo zabawy z tym przechodzeniem z 16-bitowego na 32-bitowy kod; spodziewam się, że przeniesienie do 64-bitowego kodu spowoduje takie same bóle głowy dla niektórych kodów.

 7
Author: Pontus Gagge,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-23 13:22:06

Kompilator może umieszczać elementy struktury na określonych granicach bajtów ze względu na wydajność na określonej architekturze. Może to pozostawić niewykorzystaną wyściółkę między członkami. Struktura pakowania zmusza członków do przylegania.

Może to być ważne na przykład, jeśli potrzebujesz struktury, aby była zgodna z konkretnym plikiem lub formatem komunikacji, gdzie dane muszą znajdować się w określonych pozycjach w sekwencji. Jednak takie użycie nie zajmuje się problemy z endian-ness, więc chociaż jest używany, może nie być przenośny.

Może również dokładnie nakładać wewnętrzną strukturę rejestru niektórych urządzeń We/Wy, takich jak na przykład kontroler UART lub USB, aby dostęp do rejestru był przez strukturę, a nie bezpośrednie adresy.

 2
Author: Clifford,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-23 13:22:25

Kompilator może wyrównywać pręty w strukturach, aby osiągnąć maksymalną wydajność na danej platformie. #pragma pack dyrektywa pozwala kontrolować to wyrównanie. Zazwyczaj należy pozostawić go domyślnie dla optymalnej wydajności. Jeśli musisz przekazać strukturę do zdalnego komputera, zazwyczaj używasz #pragma pack 1, aby wykluczyć niechciane wyrównanie.

 2
Author: Kirill V. Lyadvinsky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-23 13:24:30

Prawdopodobnie chcesz tego użyć tylko wtedy, gdy kodujesz na jakimś sprzęcie (np. urządzeniu zmapowanym pamięcią), który miał ścisłe wymagania dotyczące porządkowania i wyrównywania rejestrów.

Jednak wygląda to na dość tępe narzędzie do osiągnięcia tego celu. Lepszym podejściem byłoby zakodowanie mini-drivera w asemblerze i nadanie mu interfejsu wywołującego C, a nie wygłupianie się z tą pragmą.

 1
Author: msw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-23 13:20:34

Używałem go wcześniej w kodzie, ale tylko do interfejsu z kodem starszym. Była to aplikacja Mac OS X Cocoa, która musiała załadować pliki preferencji z wcześniejszej wersji Carbon (która sama była wstecznie kompatybilna z oryginalną wersją systemu M68k 6.5...masz pomysł). Pliki preferencji w oryginalnej wersji były binarnym zrzutem struktury konfiguracyjnej, który używał #pragma pack(1), aby uniknąć zajmowania dodatkowego miejsca i oszczędzania śmieci (tj. bajtów wypełnienia, które w przeciwnym razie byłyby w strukturze).

Oryginalni autorzy kodu używali również #pragma pack(1) do przechowywania struktur, które były używane jako komunikaty w komunikacji między procesami. Myślę, że powodem było uniknięcie możliwości nieznanych lub zmienionych rozmiarów wypełnienia, ponieważ kod czasami patrzył na konkretną część struktury wiadomości, licząc od początku liczbę bajtów (ewww).

 1
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-23 13:25:16

Widziałem, jak ludzie używają go, aby upewnić się, że struktura zajmuje całą linię pamięci podręcznej, aby zapobiec fałszywemu udostępnieniu w wielowątkowym kontekście. Jeśli masz zamiar mieć dużą liczbę obiektów, które mają być luźno pakowane domyślnie może zaoszczędzić pamięć i poprawić wydajność pamięci podręcznej, aby spakować je mocniej, choć niepodpisany dostęp do pamięci Zwykle spowolni rzeczy, więc może być minusem.

 1
Author: stonemetal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-23 13:26:35

Zauważ, że istnieją inne sposoby osiągnięcia spójności danych, które oferuje #pragma pack (na przykład niektórzy ludzie używają # pragma pack(1) dla struktur, które powinny być wysyłane przez sieć). Na przykład patrz poniższy kod i jego kolejne wyjście:

#include <stdio.h>

struct a {
    char one;
    char two[2];
    char eight[8];
    char four[4];
};

struct b { 
    char one;
    short two;
    long int eight;
    int four;
};

int main(int argc, char** argv) {
    struct a twoa[2] = {}; 
    struct b twob[2] = {}; 
    printf("sizeof(struct a): %i, sizeof(struct b): %i\n", sizeof(struct a), sizeof(struct b));
    printf("sizeof(twoa): %i, sizeof(twob): %i\n", sizeof(twoa), sizeof(twob));
}

Wyjście jest następujące: sizeof (struct a): 15, sizeof(struct b): 24 sizeof (twoa): 30, sizeof(twob): 48

Zwróć uwagę, jak rozmiar struktury a jest dokładnie taki, jaki jest liczba bajtów, ale struktura b ma dodane wypełnienie (zobacz to dla szczegółów na wyściółce). Robiąc to w przeciwieństwie do pakietu # pragma, możesz mieć kontrolę nad konwersją "formatu drutu" na odpowiednie typy. Na przykład, "char dwa [2]" w "krótki int" et cetera.

 0
Author: wangchow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-09 16:19:28