/* gcc -std=c17 -Wall -Wextra -pedantic x.c */
#include <stdio.h>
#include <inttypes.h>
struct x {
uint64_t f1:33;
uint64_t f2:32;
uint64_t f3:31;
uint64_t f4:30;
};
int
main(void)
{
struct x x = { -1, -1, -1, -1 };
(void)printf("%"PRIx64"\n", (uint64_t)x.f1);
(void)printf("%"PRIx64"\n", (uint64_t)x.f2);
(void)printf("%"PRIx64"\n", (uint64_t)x.f3);
(void)printf("%"PRIx64"\n", (uint64_t)x.f4);
(void)printf("%"PRIx64"\n", (uint64_t)(x.f1 + 1));
(void)printf("%"PRIx64"\n", (uint64_t)(x.f2 + 1));
(void)printf("%"PRIx64"\n", (uint64_t)(x.f3 + 1));
(void)printf("%"PRIx64"\n", (uint64_t)(x.f4 + 1));
return 0;
}
- lacos blogja
- A hozzászóláshoz be kell jelentkezni
Hozzászólások
Hát ha szabad kora reggel vaktában találgatnom, akkor ilyen (hexában, olvasáskönnyítő szeparátorokkal 16-bitenként):
1'ffff'ffff'ffff'ffff
ffff'ffff'ffff'ffff
7fff'ffff'ffff'ffff
3fff'ffff'ffff'ffff
2'0000'0000'0000'0000
1'0000'0000'0000'0000
8000'0000'0000'0000
4000'0000'0000'0000
Hogy miért nincs előjelkiterjesztés? Mert az f1..f4 mezők előjel nélküliek (unsigned).
- A hozzászóláshoz be kell jelentkezni
Ennél bonyolultabb, mégpedig az összeadás során fellépő előléptetés (promotion) miatt. Megpróbálok táblázatot táblázni a mérések alapján:
+value f1+value f2+value f3+value f4+value
+1 0 0 ffffffff80000000 40000000
+1U 0 0 80000000 40000000
+1L 200000000 100000000 80000000 40000000
+1UL 200000000 100000000 80000000 40000000
Azt mondanám, hogy a következő rangfokozatok vannak (csökkenő sorrendben):
1UL 64-bites unsigned
1L 64-bites signed
f1 33-bites unsigned
1U,f2 32-bites unsigned
1 32-bites signed
f3 31-bites unsigned
f4 30-bites unsigned
Amikor két értéket összeadunk, a kettő közül az alacsonyabb rangút előléptetjük a magasabb rangúéval azonosra, hogy pl f3+1
esetén ez 32-bites signed, f3+1U
esetén 32-bites unsigned.
- A hozzászóláshoz be kell jelentkezni
A részletes levezetés sajnos hajmeresztően bonyolult (ezért is adtam azt a címet a bejegyzésnek, amit), és igazából amit én magam elő tudok állítani, azzal nem is vagyok "szigor" tekintetében teljesen elégedett (hiányosnak érzem a szabványt, de valószínűbb, hogy az én érvelésemhiányos). Az alábbi hivatkozások mind a C17 szabványt közvetlenül megelőző piszkozatra vonatkoznak (N2176 wp pdf).
Először is, mi az, hogy bit-field (kivonatosan és nem teljeskörűen):
- "Values stored in unsigned bit-fields [...] shall be represented using a pure binary notation" (6.2.6.1p3)
- "Values stored in bit-fields consist of m bits, where m is the size specified for the bit-field. The object representation is the set of m bits the bit-field comprises in the addressable storage unit holding it" (6.2.6.1p4)
- "for bit-fields, it isimplementation-defined whether the specifier int designates the same type as signed int or the same type as unsigned int" (6.7.2p5)
- "The expression that specifies the width of a bit-field shall be an integer constant expression with a nonnegative value that does not exceed the width of an object of the type that would be specified were the colon and expression omitted" (6.7.2.1p4)
- Ezzel itt rendben vagyunk, mert az uint64_t-ben 64 értékbit (value bit), 0 előjelbit (sign bit) és 0 helykitöltő bit (padding bit) van. A pontosság (precision) az értékbitek száma, a szélesség (width) pedig az értékbitek számának és az előjelbit számának összege (6.2.6.2p6); ennél a típusnál a szélesség 64, és a legszélesebb bit-field szélesség, amit megadunk a programban, az 33.
- Maga az uint64_t típus nem kötelező, de ha a platformnak van ilyen típusa, akkor kötelező ilyen néven elérhetővé tenni (7.20.1.1p3), és a mi platformunkon van ilyen.
- "A bit-field shall have a type that is a qualified or unqualified version of _Bool, signed int, unsigned int, or some other implementation-defined type" (6.7.2.1p5)
- Mivel a programban uint64_t szerepel, azért rögtön az "implementation-defined type"-ot kell megnéznünk. A GCC dokumentációja szerint a uint64_t használható: "Other integer types, such as
long int
, and enumerated types are permitted even in strictly conforming mode".
- Mivel a programban uint64_t szerepel, azért rögtön az "implementation-defined type"-ot kell megnéznünk. A GCC dokumentációja szerint a uint64_t használható: "Other integer types, such as
- "A member of a structure or union [...] may be declared to consist of a specified number of bits (including a sign bit, if any). Such a member is called a bit-field; [...]" (6.7.2.1p9).
- Talán a legfontosabb (itt): "A bit-field is interpreted as having a signed or unsigned integer type consisting of the specified number of bits." (6.7.2.1p10; kiemelés tőlem). Ennek két fontos következménye van:
- A fentiekkel együtt ebből láthatjuk, hogy egy adott bit-field-nek saját, külön bejáratú egész típusa van (ez fontos lesz).
(Ez egy olyan típus, amire nem tudunk közvetlenül pl. typedef-et csinálni; ha typedef-et akarnánk, akkor azt csak egy tartalmazó union vagy struct típusra tudnánk megtenni. De pl. bit-field member-ekre tiltott a sizeof operátor (6.5.3.4p1) vagy az address-of & operátor (6.5.3.2p1) is.) - A típus ábrázolásáról pedig azt tudjuk, hogy (előjel nélküli esetben) m darab értékbitünk van (value bits), előjeles esetben pedig 1 előjelbitünk (sign bit) és m-1 értékbitünk; helykitöltő bit (padding bit) egy sincs, egyik esetben sem. Ez azért lényeges, mert padding bit-eket a szabvány egyébként általában véve megenged az egész típusoknál (nem idézem, de lásd 6.2.6.2p1-2).
- A fentiekkel együtt ebből láthatjuk, hogy egy adott bit-field-nek saját, külön bejáratú egész típusa van (ez fontos lesz).
Nézzük az inicializációt:
struct x x = { -1, -1, -1, -1 };
Itt a 6.3.1.3p2 szakasz számít: "Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type". Vagyis a bit-field-ek kezdőértéke rendre (2^33)-1, (2^32)-1, (2^31)-1, (2^30)-1 lesz; az első négy kiírt sor ezt tükrözi:
(void)printf("%"PRIx64"\n", (uint64_t)x.f1);
(void)printf("%"PRIx64"\n", (uint64_t)x.f2);
(void)printf("%"PRIx64"\n", (uint64_t)x.f3);
(void)printf("%"PRIx64"\n", (uint64_t)x.f4);
1ffffffff
ffffffff
7fffffff
3fffffff
A nagy huncutság itt kezdődik. Nézzük meg, hogy mi vonatkozik általában véve az (a + 1) kifejezésre (mind a négy előfordulására a programban):
- "If both operands have arithmetic type, the usual arithmetic conversions are performed on them" (6.5.6p4)
- Az 1 konstans típusa int (6.4.4.1p5, 5.2.4.2.1p1/INT_MAX).
A következő szakaszokat nem idézem, mert túl nagyok, viszont ezek a szakaszok azok, amelyek a kulcsot jelentik:
- 6.3.1.1p1 (egészek konverziós rangja / integer conversion rank),
- 6.3.1.1p2 (egészek előléptetése / integer promotions),
- 6.3.1.8p1 (szokásos aritmetikai átváltások / Usual arithmetic conversions -- ez az, amire a fent idézett 6.5.6p4 szakasz hivatkozik, a + operátor leírásánál).
Azt rögtön kijelenthetjük, hogy az 1 konstanst annak int típusa miatt az egészek előléptetése nem érinti, egyik esetben sem.
Akkor nézzük sorról sorra:
(void)printf("%"PRIx64"\n", (uint64_t)(x.f1 + 1));
Az x.f1 bit-field típusa egy olyan egész típus, amelyben nincs előjel bit, nincs helykitöltő bit, és van benne 33 értékbit. Mivel egész (és nem lebegőpontos) típusról van szó, azért (az összeadáshoz) a "usual arithmetic conversions"-ben (6.3.1.8p1) ott indulunk, hogy "[...] the integer promotions are performed on both operands". Nézzük meg, az x.f1-et érinti-e az egészek előléptetése:
- Az x.f1 típusának konverziós rangja nagyobb, mint az int-é, ill. (ekvivalensen) az unsigned int-é, tehát a 6.3.1.1p2 első "ága" nem alkalmazható.
A konverziós rangra tett kijelentésemet külön indokolni kell (és ez a legnehezebb rész). Én az alábbi utat tudtam úgy-ahogy megkonstruálni:- "The rank of any unsigned integer type shall equal the rank of the corresponding signed integer type, if any".
Ebből azt kapjuk, hogy a 33 értékbites, 0 előjelbites (és definíció szerint 0 helykitöltő bites) bitmező típusunk konverziós rangja azonos a 32 értékbites, 1 előjelbites, és hasonlóan 0 helykitöltő bites bitmező-típus konverziós rangjával. Más szóval, a konverziós rang megtartása mellett, a konverziós rang vizsgálata érdekében, áttérünk uint64_t:33-ról int64_t:33-ra. - "The rank of a signed integer type shall be greater than the rank of any signed integer type with less precision".
A mi platformunkon az int pontossága 31 bit (mivel 31 értékbit van benne), a fent levezetett int64_t:33 típusban viszont 32 értékbit van, így az utóbbi típus konverziós rangja (egyúttal az eredeti uint64_t:33 konverziós rangja) nagyobb az int-énél (egyúttal az unsigned int-énél).
- "The rank of any unsigned integer type shall equal the rank of the corresponding signed integer type, if any".
- Az x.f1 "alaptípusa" uint64_t, ami (ezen a platformon) különbözik a _Bool, int, signed int, unsigned int típusok mindegyikétől, így a 6.3.1.1p2 második ága sem alkalmazható.
Ezt követően pedig az alábbi ág aktiválódik a szokásos aritmetikai átváltásokból (6.3.1.8p1): "Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type". Fent (függetlenül) megmutattuk, hogy az uint64_t:33 rangja nagyobb, mint az int-é (ami pedig az 1 konstansnak a típusa). Tehát az x.f1 operandus típusát nemcsak előléptetés nem érinti, hanem szokásos aritmetikai átváltás sem. Ami megváltozik az utóbbi átváltás hatására, az az 1 konstans típusa: abból lesz uint64_t:33, és ez a típus lesz az eredmény (összeg) típusa is ("Unless explicitly stated otherwise, the common real type is also the corresponding real type of the result [...]", 6.3.1.8p1).
Az x.f1 bit-field értéke (2^33)-1, ehhez adunk 1-et. A 2^33 nem ábrázolható uint64_t:33-ban; itt a 6.2.5p9-et alkalmazzuk: "A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type". Az uint64_t:33 típusú eredmény tehát nulla; ezt is írja ki a program:
0
Szemléletes, hogy ha a printf argumentumából kivesszük a (uint64_t) cast-ot, akár az első, akár az ötödik printf-nél, akkor a gcc a következő beszédes figyelmeztetést adja: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘long unsigned int:33’.
Az is szemléletes, hogy míg közvetlenül az x.f1 operandusra a sizeof operátor tiltott (6.5.3.4p1, "an expression that designates a bit-field member"), az (x.f1 + 1) operandusra -- mint a kiértékelt összeadás eredményére -- a sizeof nem tiltott, és a gyakorlatban 8-at ad vissza (6.5.3.4p2, "The size is determined from the type of the operand"). Vagyis az (x.f1 + 1) eredménytípusa (uint64_t:33) 8 byte-ot "foglal" ezen a platformon, de az értékkészlete csak 0..(2^33)-1.
A maradék három printf-et új hozzászólásokba teszem.
- A hozzászóláshoz be kell jelentkezni
(void)printf("%"PRIx64"\n", (uint64_t)(x.f2 + 1));
Az x.f2 típusában 32 értékbit van, 0 előjelbit, és 0 helykitöltő bit. Emiatt az x.f2-t érinti az előléptetés:
- Az x.f2 típusának konverziós rangja nem nagyobb az int-énél, ill. (ekvivalensen) az unsigned int-énél, tehát a 6.3.1.1p2 első "ága" alkalmazható ("An object or expression with an integer type (other than int or unsigned int ) whose integer conversion rank is less than or equal to the rank of int and unsigned int").
A konverziós rangra tett kijelentésemet külön indokolni kell:- "The rank of any unsigned integer type shall equal the rank of the corresponding signed integer type, if any."
Ugyanaz az érvelés, mint fent; uint64_t:32-ről áttérünk (a rang megtartása mellett) int64_t:32-re. - "The rank of a signed integer type shall be greater than the rank of any signed integer type with less precision".
Mivel az int-nek és az int64_t:32-nek a pontossága megegyezik (31 értékbit) a mi platformunkon, azért ez a tétel nem mond semmit a kettejük rangkülönbségéről. - "No two signed integer types shall have the same rank, even if they have the same representation".
(Őszintén szólva ezt a pontot nem értem a szabványban, ugyanis ha a rangegyenlőséget kizárja, akkor a "less than or equal to" minősítés a 6.3.1.1p2-ben nehezen értelmezhető.)
Itt úgy érzem, hogy a szabvány hiányos (a 6.3.1.1p1 alapján, szó szerint, egyszerűen nem tudok tovább érvelni; "józan ésszel" kell eldönteni, hogy akkor most minek legyen nagyobb a rangja: az int-nek vagy az int64_t:32-nek. Én szívem szerint azonos rangot adnék nekik, viszont ha azt nem lehet, akkor legyen az int-nek nagyobb rangja.
- "The rank of any unsigned integer type shall equal the rank of the corresponding signed integer type, if any."
- Így a 6.3.1.1p2 vége szerint az x.f2-t unsigned int típusúvá léptetjük elő: "If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int" (kiemelés tőlem). (Az x.f2-ben 32 értékbit van; az a mi platformunkon az int-be nem fér bele, az unsigned int-be viszont igen.)
Ezt követően pedig (újra) az alábbi ág aktiválódik a szokásos aritmetikai átváltásokból (6.3.1.8p1): "Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type". Tehát az x.f2 operandus típusát az előléptetés unsigned int-re változtatta, azt követően pedig az 1 (int) konstans típusát a szokásos aritmetikai átváltás szintén unsigned int-re. Az összeadás eredményének típusa szintén unsigned int.
Az x.f2 értéke (2^32)-1, ehhez adunk 1-et; a mi platformunkon az unsigned int-ben a 2^32 nem ábrázolható, így (ismét) a 6.2.5p9 alapján az eredmény:
0
Ha a printf argumentumából kivesszük a (uint64_t) cast-ot, akár a második, akár a hatodik printf-nél, akkor a gcc a következő figyelmeztetést adja: warning: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘unsigned int’.
A "sizeof (x.f2+1)" pedig 4.
- A hozzászóláshoz be kell jelentkezni
(void)printf("%"PRIx64"\n", (uint64_t)(x.f3 + 1));
Az x.f3-ban 31 értékbit van, 0 előjelbit, 0 helykitöltő bit; így érinti az előléptetés:
- Az x.f3 típusának konverziós rangja kisebb az int-énél, ill. (ekvivalensen) az unsigned int-énél, tehát a 6.3.1.1p2 első "ága" alkalmazható, ui:
- "The rank of any unsigned integer type shall equal the rank of the corresponding signed integer type, if any"
uint64_t:31-ről áttérünk (a rang megtartása mellett) int64_t:31-re. - "The rank of a signed integer type shall be greater than the rank of any signed integer type with less precision"
Az int64_t:31 pontossága 30 értékbit, az int-é 31 értékbit (a mi platformunkon), így az int rangja határozottan nagyobb, az int64_t:31 rangja határozottan kisebb.
- "The rank of any unsigned integer type shall equal the rank of the corresponding signed integer type, if any"
- Így a 6.3.1.1p2 vége szerint az x.f3-at int típusúvá léptetjük elő! Mivel az int-ben az int64_t:31 teljes értékkészletét képesek vagyunk ábrázolni.
Így viszont már a következő ág aktiválódik a szokásos aritmetikai átváltásokból (6.3.1.8p1): "If both operands have the same type, then no further conversion is needed". (Mind az x.f3 előléptetett típusa, mind az 1 konstans típusa int.) Az összeg típusa szintén int.
Az x.f3 értéke (2^31)-1, ehhez adunk 1-et; a mi platformunkon az int-ben a 2^31 nem ábrázolható, így az összeadás definiálatlan viselkedés (6.5p5): "If an exceptional condition occurs during the evaluation of an expression (that is, if the result is not mathematically defined or not in the range of representable values for its type), the behavior is undefined".
A mi platformunkon ez történetesen mindössze azzal a tünettel jár, hogy a 0x7fffffff + 1 összeadás a 0x80000000 bitmintát állítja elő az int típusú eredményben, az pedig a platformunk kettes komplemens ábrázolásmódja miatt (-0x80000000) = -(2^31) értékkel bír (ld. 6.2.6.2p2 -- "the sign bit has the value -(2^M) (two’s complement)", ahol M az értékbitek száma). Illetve azt is hozzá kell tenni, hogy -- mivel nem kapcsoltunk be optimalizációt -- a gcc nem használta ki ennél agresszívebben az undefined behavior-t. Kb. azt mondhatjuk, hogy a gcc viselkedése alapesetben itt olyan, mintha megadtuk volna az -fwrapv kapcsolót.
Ezután az int típusú (-0x80000000) értéket konvertáljuk uint64_t-re; a 6.3.1.3p2 szerint "Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type". Az eredmény (-(2^31))+(((2^64)-1)+1) = 2^64 - 2^31:
ffffffff80000000
Ha a printf argumentumából kivesszük a (uint64_t) cast-ot, akár a harmadik, akár a hetedik printf-nél, akkor a gcc a következő figyelmeztetést adja: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘int’.
- A hozzászóláshoz be kell jelentkezni
(void)printf("%"PRIx64"\n", (uint64_t)(x.f4 + 1));
Az (x.f3+1) esethez hasonlóan (mivel itt még kevesebb értékbitünk van) az x.f4-et is int típusra lépetjük elő, és az összeadás int eredményt produkál.
Az x.f4 értéke (2^30)-1. Az 1-gyel nagyobb (=2^30) összeg int-ben ábrázolható, ezért a viselkedés jól definiált.
Az (uint64_t) cast megtartja ezt az értéket (6.3.1.3p1): "When a value with integer type is converted to another integer type other than _Bool , if the value can be represented by the new type, it is unchanged."
40000000
Ha a printf argumentumából kivesszük a (uint64_t) cast-ot, akár a negyedik, akár a nyolcadik printf-nél, akkor a gcc a következő figyelmeztetést adja: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘int’.
Összefoglalva: A uint64_t:33-nál meglepetés lehet, hogy bár határozottan unsigned int "fölött" vagyunk, a bit-field egyedi típusa az aritmetikát bizony 33 bitre csonkolja. A többi esetben (32, 31, 30 értékbit) meglepetés lehet, hogy az egész előléptetés hatására rendre unsigned int, int, int típust fog képviselni a bitmező operandus az összeadásban, nem a bitmező alapjául szolgáló uint64_t-t. A 31 értékbites esetben külön meglepetés lehet az undefined behavior az összeadásban.
Ezek a meglepetések a gyakorlatban előfordulnak; itt egy példa: https://github.com/tianocore/edk2/commit/e8c23d1e27f7
- A hozzászóláshoz be kell jelentkezni
Milyen eredmenyt varsz? Ugy emlekszem, az unsigned overflow undefined behaviour-nek szamit, marpedig amikor 1-et hozzaadsz, az lesz.
A strange game. The only winning move is not to play. How about a nice game of chess?
- A hozzászóláshoz be kell jelentkezni
A signed esetén van undefined behaviour. Az unsigned esetén úgy tudom simán rollover van definiálva.
- A hozzászóláshoz be kell jelentkezni
Off: az a signed overflow, ami undefined; az unsignednél értelemszerűen túlcsordulva kell működnie.
- A hozzászóláshoz be kell jelentkezni
Milyen eredmenyt varsz?
Meta, de: azt szeretném, hogy aki C-ben dolgozik, tudatosítsa, hogy a bitmezők a C-ben a megszokottnál is becsapósabbak. Az én véleményem az, hogy a bitfield-es kódot jó tudni olvasni, de írni nem szabad; mindent, amit bitfield-ekkel meg lehet csinálni, azt meg lehet csinálni explicit bit-banging-gel, és jobban járunk, ha explicite írjuk meg. (Ha eleve helyesen használjuk a bitmezőket, akkor a fordító nagyjából hasonló kódot fog generálni, mint ha kézzel maszkolnánk (az optimalizáció után meg úgysem ismerünk rá a kódra, egyik esetben sem), tehát csak a kód olvashatóságát rontjuk, mivel a bitfield-ekről nem ordít, hogy azok, amik; ha pedig még nem is tudjuk helyesen használni a bitmezőket (esetleg azt sem tudjuk, hogy nem tudjuk jól használni azokat), akkor minek kísérletezünk?)
Ha esetleg többször nyúlunk egy bit-field-hez, akkor naiv fordító (vagy az adott fordításnál éppen nem optimalizáló fordító) esetén többszöri maszkolás / eltolás lehet belőle; explicit bitmanipuláció esetén ez hamarabb feltűnik, és a kézzel írt kódban esetleg megússzuk 1 db maszkolással és/vagy összesen 2 shift-tel.
Az itt tárgyaltakon túl a bitmezők elhelyezése is teljesen hordozhatatlan (6.7.2.1p11):
An implementation may allocate any addressable storage unit large enough to hold a bit-field. If enough space remains, a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit. If insufficient space remains, whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined. The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined. The alignment of the addressable storage unit is unspecified.
Például:
struct foo {
uint64_t bf_30:30;
uint64_t bf_8:8;
uint64_t bf_26:26;
};
Lehet, hogy mi ezzel egy uint64_t-t akarunk felosztani, de a platformnak joga van, hogy az első mezőt beletegye egy uint32_t-be, két bitet elpocsékoljon belőle, és így már a második mezőt nem kötelező pakolnia. Még azt sem tudod megmondani (a platformot nem ismerve), hogy a két elpocsékolt bit (az első uint32_t-ben) alacsony vagy magas rendű. Aztán (ad abszurdum) a második mezőt beleteheti egy különálló uint8_t-be (ez eddig 5 byte), ezt követően beszúrhat 3 padding byte-ot (a harmadik mező igazításához), majd a harmadik mezőt beteheti (miért is ne) egy külön uint64_t-be (de a platformot nem ismerve nem tudod, hogy melyik szélére).
Azt pedig a platformnak még csak dokumentálnia sem kell ("unspecified"), hogy a bit-field-ekhez használt tárolási egységeket hogyan igazítja (tehát pl. nem-bitfield és bitfield mezők közé mennyi padding-et fog beszúrni).
A C szabvány egyéb részei is tele vannak a bitmezőkre vonatkozó kivételekkel; pl. az, hogy atomi egész típusokban lehet-e bitmezőket használni, implementációfüggő (6.7.2.1p5; és a gcc pl. nem támogatja).
Az egész kérdéskör érdektelenné válik, ha explicit bitmanipulációt használunk. Így mi kontrolláljuk, hogy pl. atomi egészekkel mit csinálunk. Vagy ha valamilyen volatile eszközregisztert használunk, akkor is jól kontrollált a hozzáférés; a "reg.x = 3"-ról nem feltétlenül ordít, hogy az bizony egy RMW. Az eszközök byte-sorrendje általában ismert, a C programban csak a CPU byte-sorrendjét kell absztrahálni (ld. pl. htons()).
- A hozzászóláshoz be kell jelentkezni
Az előléptetésből (promotion) származó furcsaságok nem bit-field specifikusak, pl. ((unsigned short)0xfffe)+1
is -1
lesz (int32_t
tipus), ha jól csalódom.
Szerk: vagy inkább 65535, (int32_t tipus)?
- A hozzászóláshoz be kell jelentkezni
Az ((unsigned short)0xfffe)
attól függően lép elő int-re vagy unsigned int-re, hogy az int mit tud ábrázolni:
If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int.
Ha olyan platformon vagy, ahol az INT_MAX
értéke 32767, akkor az előlépés unsigned int-re történik. Ha viszont olyanon, ahol az INT_MAX legalább 65535, ott az előlépés int-re történik. Az összeadás eredménye mindkét esetben 65535; utóbbi esetben azért, mert (a feltételünk szerint) az INT_MAX legalább 65535, előbbi esetben pedig azért, mert a szabvány minimum 65535-öt követel meg UINT_MAX-nak.
(Unsigned short mindenképpen belefér unsigned int-be, ld. 6.2.5p8:
For any two integer types with the same signedness and different integer conversion rank (see 6.3.1.1), the range of values of the type with smaller integer conversion rank is a subrange of the values of the other type.
és a short rangja kisebb, mint az int-é. Tehát az előléptetett (új) típus az int tartományától függ, és az eredmény mindenképpen 65535.)
- A hozzászóláshoz be kell jelentkezni
Oké, nem mondtam kifejezetten, hallgatólagosan L64-I32-S16
architektúrára gondoltam. Most teszteltem, kijött a 0xffff(int32_t)
Off: közben néztem egy ilyet, szépen kiírja, hogy az összeg 75000, mivel a short-ot automatikusan int-re lépteti elő:
{
int16_t x= 20000, y= 25000, z= 30000;
printf ("\nsumming shorts\n");
printf ("x(%d)+y(%d)+z(%d)=%d\n", x, y, z, x+y+z);
}
- A hozzászóláshoz be kell jelentkezni