Pretypovani ukazatele struktury na bajt
David Obdrzalek
David.Obdrzalek na mff.cuni.cz
Úterý Únor 25 16:34:34 CET 2020
Jo, když se to důsledně otypuje, tak to pak je lepší, to mi je jasný :-)
Nerozumím ale tomu, že 0xADDE přiřazeno do 32bit unsigned se roztáhne do 32bit
unsigned bez znamínka (zde implicitně), ale 0xDE se po posunutí o 8 doleva a pak
přiřazení do 32bit unsigned (opět implicitně) roztáhne znamínkově.
Pokus: hdr.ident = (uint32_t)(0xAD << 8); // přiřadí se 0xFFFFAD00
zatímco hdr.ident = (uint32_t)(0xAD00); // přiřadí se 0x0000AD00
Zrada se tedy zdá být v chování toho shiftu.
Ale to s tím hextem že je vždy znamínkový se mi nějak nezdá, měl jsem za to, že ten
postup je "když se vejde do int tak int, když ne, tak unsigned int, když ne, tak
long int atd.". Jo a to by asi sedělo - 0xAD se vešlo do int, tedy je signed a
rozšíření na 32bit je signed a nějak se to tam pomele. 0xAD00 se nevešlo do int,
ale do unsigned int jo, takže je unsigned a tedy rozšíření na 32bit je unsigned bez
ztráty kytičky. Kdybych napsal rovnou 0xADu, tak si vynutím unsigned a dál to už
známe.
Malinko mi tam lavíruje to, že se shiftem o 8 stalo z 0x00AD (což byl kladný int)
něco záporného, ale třeba je to jen nedefinované (tj. shift intu tak, že se do intu
nevejde) a shodou náhod to je takhle. No a to teď ověřil pokus, shift doleva se
zjevně v mém avr-gcc dělá naprosto jednoduše tak, že se to prostě shiftne bez
ohledu na znamínko a když zrovna ve výsledku padne jednička do nejvyššího bitu, no
tak máme záporné číslo :-)
hdr.ident = (uint32_t)(0xAD << 7) ... 00005680 = 16 nul a 01010110 10000000
hdr.ident = (uint32_t)(0xAD << 8) ... FFFFAD00 = 16 jednicek a 10101101 00000000
hdr.ident = (uint32_t)(0xAD << 9) ... 00005A00 = 16 nul a 01011010 00000000
hdr.ident = (uint32_t)(0xAD << 10)... FFFFB400 = 16 jednicek a 10110100 00000000
hdr.ident = (uint32_t)(0xAD << 11)... 00006800 = 16 nul a 01101000 00000000
hdr.ident = (uint32_t)(0xAD << 12)... FFFFD000 = 16 jednicek a 11010000 00000000
hdr.ident = (uint32_t)(0xAD << 13)... FFFFA000 = 16 jednicek a 10100000 00000000
hdr.ident = (uint32_t)(0xAD << 14)... 00004000 = 16 nul a 01000000 00000000
Hurá :-)
A podle toho bych si tipnul, že shift doleva záporného intu uloženého zase do intu
je nedefinovaný, ale prakticky se prostě shiftne a vznikne něco kladného nebo
záporného podle toho, jestli do nejvyššího bitu padla 0 nebo 1.
D.O.
On 25 Feb 2020 at 15:15, Josef Štengl wrote:
> Hmm. Protože se při posunu před tím provede typová konverze. Zkuste si, co to
> udělá, když napíšete u (jako unsigned) za
> 0xAD. Hexadecimální číslo bez u je bráno jako signed. U dekadických to
> neplatí, to může být signed nebo unsigned podle
> kontextu, pokud není uvedeno u na konci nebo přetypováno.
>
> Pro eliminaci takových to záhad je vhodné čísla důsledně přetypovávat,
> pokud jsou uveden v aritmetických operacích. Má to
> svoji logiku ale kdo by si ji pamatoval. A většina té logiky je závislá na
> implementaci.
>
>
>
> Dne 25. 02. 20 v 15:01 David Obdrzalek napsal(a):
> > Ale proč to funguje s 0xADDE, které má taky nejvyšší bít nahozený?
> >
> > D.O.
> >
> > On 25 Feb 2020 at 13:58, Hynek Sladky wrote:
> >> To vypada na znamenko...
> >> 0xAD << 8 je 16-bitovy int se zapornou hodnotou
> >> Pak se to implicitne pretypuje na uint32_t (podle dalsich clenu), ale
> >> protoze to je zaporna hodnota, tak se znamenko expanduje do vyssich bytu
> >> jako 0xFFFF0000.
> >>
> >> Mohlo by pomoct zapisovat konstanty s priponou u, tedy napr. 0xADu << 8
> >>
> >> Hynek Sladky
> >>
> >>
> >> Dne 25.2.2020 v 13:28 David Obdrzalek napsal(a):
> >>> ALE když odmažu přetypování prvních dvou bytů, tak tam vyjde:
> >>> hdr.ident = 0xDE | 0xAD << 8 | ((uint32_t)0xBE) << 16 | ((uint32_t)0xEF) <<
> 24;
> >>> 4c6: 8e ed ldi r24, 0xDE ; 222
> >>> 4c8: 9d ea ldi r25, 0xAD ; 173
> >>> 4ca: af ef ldi r26, 0xFF ; 255
> >>> 4cc: bf ef ldi r27, 0xFF ; 255
> >>> 4ce: 80 93 b1 01 sts 0x01B1, r24 ; 0x8001b1 <hdr+0x2>
> >>> 4d2: 90 93 b2 01 sts 0x01B2, r25 ; 0x8001b2 <hdr+0x3>
> >>> 4d6: a0 93 b3 01 sts 0x01B3, r26 ; 0x8001b3 <hdr+0x4>
> >>> 4da: b0 93 b4 01 sts 0x01B4, r27 ; 0x8001b4 <hdr+0x5>
> >>>
> >>> Vůbec nachápu, proč ty dvě FF ??
> >>>
> >>> hdr je deklarované takhle:
> >>> typedef struct {
> >>> uint16_t magic;
> >>> uint32_t ident;
> >>> } _hdr;
> >>> _hdr hdr;
> >>>
> >>> Překladač avr-gcc (GCC) 5.4.0 na Win.
> >>>
> >>>
> >>> Jo tak teď jsem ještě zmatenější, protože mě napadlo zkusit:
> >>> hdr.ident = 0xADDE | ((uint32_t)0xBE) << 16 | ((uint32_t)0xEF) << 24;
> >>> 4c6: 8e ed ldi r24, 0xDE ; 222
> >>> 4c8: 9d ea ldi r25, 0xAD ; 173
> >>> 4ca: ae eb ldi r26, 0xBE ; 190
> >>> 4cc: bf ee ldi r27, 0xEF ; 239
> >>> 4ce: 80 93 b1 01 sts 0x01B1, r24 ; 0x8001b1 <hdr+0x2>
> >>> 4d2: 90 93 b2 01 sts 0x01B2, r25 ; 0x8001b2 <hdr+0x3>
> >>> 4d6: a0 93 b3 01 sts 0x01B3, r26 ; 0x8001b3 <hdr+0x4>
> >>> 4da: b0 93 b4 01 sts 0x01B4, r27 ; 0x8001b4 <hdr+0x5>
> >>>
> >>> a tohle prošlo jak bych původně čekal!
Další informace o konferenci Hw-list