Pretypovani ukazatele struktury na bajt
David Obdrzalek
David.Obdrzalek na mff.cuni.cz
Úterý Únor 25 13:28:27 CET 2020
Mi tak dneska v tramvaji volným uvažováním došlo, že ta funkce napln není správně -
mělo by tam být explicitní přetypování e, f na uint32_t, protože jinak se to spočte
v intech, kde e<<16 i f<<24 vyjde 0 a teprv na závěr se to uloží do většího.
Co mě ale zaskočilo: pro ověření, jak se to chová blbě při nepřetypování, jsem to
prohnal gcc, ale pro opravu bylo potřeba přetypovat na větší typ i první dva členy,
jinak se to pořád uřízlo. Proč? Myslel jsem, ze by mělo stačit, když na větší
přetypuju ty, co by přetekly, tím část výrazu bude celkově větší než int a proto se
i ostatní taky natáhnou.
S explicitním přetypováním všeho se pro AVR vygeneruje docela uvěřitelný kód
(zkusil jsem i konstanty):
hdr.magic = 0xAB | 0xBA << 8;
4ba: 8b ea ldi r24, 0xAB ; 171
4bc: 9a eb ldi r25, 0xBA ; 186
4be: 90 93 b0 01 sts 0x01B0, r25 ; 0x8001b0 <hdr+0x1>
4c2: 80 93 af 01 sts 0x01AF, r24 ; 0x8001af <hdr>
hdr.ident = ((uint32_t)0xDE) | ((uint32_t)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: 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>
hdr.magic = a | b << 8;
hdr.ident = ((uint32_t)c) | ((uint32_t)d) << 8 | ((uint32_t)e) << 16 |
((uint32_t)f) << 24;
518: 6b 81 ldd r22, Y+3 ; 0x03
51a: 2c 81 ldd r18, Y+4 ; 0x04
51c: 9d 81 ldd r25, Y+5 ; 0x05
51e: 8e 81 ldd r24, Y+6 ; 0x06
hdr.magic = a | b << 8;
520: 49 81 ldd r20, Y+1 ; 0x01
522: 5a 81 ldd r21, Y+2 ; 0x02
524: 50 93 b0 01 sts 0x01B0, r21 ; 0x8001b0 <hdr+0x1>
528: 40 93 af 01 sts 0x01AF, r20 ; 0x8001af <hdr>
hdr.ident = ((uint32_t)c) | ((uint32_t)d) << 8 | ((uint32_t)e) << 16 |
((uint32_t)f) << 24;
52c: 60 93 b1 01 sts 0x01B1, r22 ; 0x8001b1 <hdr+0x2>
530: 20 93 b2 01 sts 0x01B2, r18 ; 0x8001b2 <hdr+0x3>
534: 90 93 b3 01 sts 0x01B3, r25 ; 0x8001b3 <hdr+0x4>
538: 80 93 b4 01 sts 0x01B4, r24 ; 0x8001b4 <hdr+0x5>
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!
D.O.
On 24 Feb 2020 at 17:57, David Obdrzalek wrote:
> Souhlasím. A podle mě šikovnej kompilátor i to plnění nakonec udělá kulturně
> optimalizovaně - na platformě, co nepotřebuje zarovnaný přístup, to nasype po
> bajtech jako kdyby se to tam sypalo jednotlivě takhle přes ten inkrementovaný
> pointr, na platformě, co jí líp vyhovujou větší jednotky, si to připraví a pak
> flekne kam patří a jak patří. A co je důležité, v obou případech se správnýma
> endianama, aniž bych se o ně musel já starat.
>
> void napln(uint8_t a, uint8_t b,uint8_t c,uint8_t d,uint8_t e,uint8_t f)
> {
> hdr.magic = a | b << 8;
> hdr.ident = c | d << 8 | e << 16 | f << 24;
> }
> (samozřejmě musím vědět, co v těch datech je MSB a co LSB)
>
> Tipnul bych si, že to, že se uložilo jen kousek, bude nějakou optimalizací, kdy se
> nevygeneruje něco, co se později nijak nepoužije (např. v modelovém případě, kdy se
> ta struktura jen naplní ale pak už nečte). Starý kompilátor třeba jen
> neoptimalizoval tak agresivně.
>
> A ještě, zaklínadlo volatile jste zkusil?
>
> D.O.
>
> On 24 Feb 2020 at 9:06, Josef Štengl wrote:
> > Jinak správný způsob je naplnit tu strukturu, ne ji přetypovat a spoléhat se,
> > že to vyjde. Pak nevíte co udělá jiný
> > kompilátor, nebo optimalizace.
> >
> > Dne 23. 02. 20 v 17:13 Libor Konečný napsal(a):
> > > Zdravim osazenstvo, predevsim zdejsi programatory.
> > >
> > > S novym kompilatorem (ten stary to nejakou zahadou umel) resim pretypovani
> > ukazatelu.
> > >
> > > Mam funkci (zjednodusene)
> > > void funkce (UINT8 * from)
> > > {
> > > *from++=0x01 ;
> > > *from++=0x02 ;
> > > *from++=0x03 ;
> > > *from++=0x04 ;
> > > *from++=0x05 ;
> > > *from=0x06 ;
> > > }
> > >
> > >
> > > A pak strukduru
> > > struct {
> > > UINT16 magic;
> > > DWORD ident;
> > > } hdr;
> > >
> > > A potrebuji ji naplnit,
> > > tedy volam
> > > funkce ((UINT8*)&hdr);
> > >
> > > ale kompilator to naplni vzdy jen jednim znakem
> > >
> > > tedy vysledek je ze hdr.magic ma jen prvni bajt spravny.
> > >
> > > Pokud pouziji strukturu
> > > struct {
> > > UINT8 pole[6];
> > > } hdr;
> > >
> > > Pak se naplni vsech 6 bajtu spravne.
> > >
> > > Jak ji volat spravne aby se struktura prepisovala bajt po bajtu ? (little a
> big
> > endian zanedbejme)
> > > Je zvlastni, ze stary kompilator fungoval spravne.
> > >
> > > Dekuji za nakopnuti.
> > > LK
>
Další informace o konferenci Hw-list