Zdefiniowałem tę strukturę:
typedef struct
{
char A:3;
char B:3;
char C:3;
char D:3;
char E:3;
} col;
sizeof(col)
Dać mi wyjście 3, ale nie powinno to być 2? Jeśli skomentuję tylko jeden element, to sizeof
jest 2. Nie rozumiem dlaczego: pięć elementów po 3 bity to 15 bitów, czyli mniej niż 2 bajty.
Czy istnieje „rozmiar wewnętrzny” w definiowaniu struktury takiej jak ta? Potrzebuję tylko wyjaśnienia, ponieważ z mojego dotychczasowego pojęcia o języku spodziewałem się rozmiaru 2 bajtów, a nie 3.
signed char
lubunsigned char
nie możesz powiedzieć bez spojrzenia na dokumentację, czy kompilator potraktuje słowo „zwykłe”char
w polu bitowym jako podpisane czy niepodpisane, a decyzja może (teoretycznie) różnić się od decyzji o tym, czy „Zwykły”char
jest podpisany lub niepodpisany, gdy jest używany poza polem bitowym._Bool
,signed int
,unsigned int
, lub jakiegoś innego typu implementacji zdefiniowane. Stosowaniechar
należy zatem do kategorii „inny typ zdefiniowany w ramach implementacji”.Odpowiedzi:
Ponieważ używasz
char
jako podstawowego typu dla swoich pól, kompilator próbuje grupować bity według bajtów, a ponieważ nie może umieścić więcej niż osiem bitów w każdym bajcie, może przechowywać tylko dwa pola na bajt.Całkowita suma bitów używanych przez twoją strukturę wynosi 15, więc idealny rozmiar, aby zmieścić tak dużo danych, to
short
.#include <stdio.h> typedef struct { char A:3; char B:3; char C:3; char D:3; char E:3; } col; typedef struct { short A:3; short B:3; short C:3; short D:3; short E:3; } col2; int main(){ printf("size of col: %lu\n", sizeof(col)); printf("size of col2: %lu\n", sizeof(col2)); }
Powyższy kod (dla platformy 64-bitowej, takiej jak moja), rzeczywiście da
2
drugą strukturę. W przypadku czegokolwiek większego niż ashort
, struktura wypełni nie więcej niż jeden element użytego typu, więc - dla tej samej platformy - struktura będzie miała rozmiar cztery dlaint
, osiem dlalong
itd.źródło
char
ashort
?char
niepodpisanie implementacji …Ponieważ nie możesz mieć pola bitowego pakietu, które obejmuje minimalną granicę wyrównania (która wynosi 1 bajt), więc prawdopodobnie zostaną spakowane jak
byte 1 A : 3 B : 3 padding : 2 byte 2 C : 3 D : 3 padding : 2 byte 3 E : 3 padding : 5
(kolejność pól / dopełnień w tym samym bajcie nie jest zamierzona, ma tylko dać ci pomysł, ponieważ kompilator może je określić tak, jak woli)
źródło
Pierwsze dwa pola bitów pasują do jednego
char
. Trzeci nie może do tego pasowaćchar
i potrzebuje nowego. 3 + 3 + 3 = 9, który nie pasuje do 8-bitowego znaku.Zatem pierwsza para przyjmuje a
char
, druga para przyjmuje achar
, a ostatnie pole bitowe otrzymuje trzeciechar
.źródło
Większość kompilatorów pozwala kontrolować wypełnienie, np. Używając
#pragma
s . Oto przykład z GCC 4.8.1:#include <stdio.h> typedef struct { char A:3; char B:3; char C:3; char D:3; char E:3; } col; #pragma pack(push, 1) typedef struct { char A:3; char B:3; char C:3; char D:3; char E:3; } col2; #pragma pack(pop) int main(){ printf("size of col: %lu\n", sizeof(col)); // 3 printf("size of col2: %lu\n", sizeof(col2)); // 2 }
Zauważ, że domyślne zachowanie kompilatora ma swój powód i prawdopodobnie zapewni lepszą wydajność.
źródło
Chociaż norma ANSI C zbyt mało określa, jak upakowane są pola bitów, aby zapewnić jakąkolwiek znaczącą przewagę nad „kompilatorami mogą pakować pola bitowe w dowolny sposób, jak uznają to za stosowne”, to jednak w wielu przypadkach zabrania kompilatorom pakowania rzeczy w najbardziej efektywny sposób.
W szczególności, jeśli struktura zawiera pola bitowe, kompilator musi przechowywać ją jako strukturę, która zawiera jedno lub więcej anonimowych pól pewnego „normalnego” typu pamięci, a następnie logicznie podzielić każde takie pole na części składowe pola bitowego. Zatem biorąc pod uwagę:
unsigned char foo1: 3; unsigned char foo2: 3; unsigned char foo3: 3; unsigned char foo4: 3; unsigned char foo5: 3; unsigned char foo6: 3; unsigned char foo7: 3;
Jeśli
unsigned char
ma 8 bitów, kompilator musiałby przydzielić cztery pola tego typu i przypisać dwa pola bitowe do wszystkich oprócz jednego (które znajdowałoby się wechar
własnym polu). Gdyby wszystkiechar
deklaracje zostały zastąpione przezshort
, byłyby dwa pola typushort
, z których jedno zawierałoby pięć pól bitowych, a drugie zawierałoby pozostałe dwa.Na procesorze bez ograniczeń wyrównania dane można by układać wydajniej, używając
unsigned short
dla pierwszych pięciu pól iunsigned char
dla ostatnich dwóch, przechowując siedem trzybitowych pól w trzech bajtach. Chociaż powinno być możliwe przechowywanie ośmiu trzy-bitowych pól w trzech bajtach, kompilator mógłby zezwolić na to tylko wtedy, gdyby istniał trzy bajtowy typ liczbowy, który mógłby być użyty jako typ „pola zewnętrznego”.Osobiście uważam, że pola bitowe są zdefiniowane jako zasadniczo bezużyteczne. Jeśli kod musi działać z danymi spakowanymi binarnie, powinien wyraźnie zdefiniować lokalizacje przechowywania rzeczywistych typów, a następnie użyć makr lub innych takich środków, aby uzyskać dostęp do ich bitów. Byłoby pomocne, gdyby C obsługiwał składnię taką jak:
unsigned short f1; unsigned char f2; union foo1 = f1:0.3; union foo2 = f1:3.3; union foo3 = f1:6.3; union foo4 = f1:9.3; union foo5 = f1:12.3; union foo6 = f2:0.3; union foo7 = f2:3.3;
Taka składnia, jeśli jest dozwolona, umożliwiłaby kodowi używanie pól bitowych w sposób przenośny, bez względu na rozmiary słów lub porządek bajtów (foo0 znajdowałby się w trzech najmniej znaczących bitach f1, ale te mogłyby być przechowywane w niższy lub wyższy adres). Jednak bez takiej funkcji makra są prawdopodobnie jedynym przenośnym sposobem obsługi takich rzeczy.
źródło