RE: RE: C: co lze oèekávat

Miroslav Draxal evik na volny.cz
Čtvrtek Září 1 11:48:10 CEST 2016


Díky za rozbor. Zase to má tu výhodu, že si tohle budu už navždy pamatovat. Vlastně díky všem za reakce. Míra

-----Original Message-----
From: Hw-list [mailto:hw-list-bounces na list.hw.cz] On Behalf Of Jan Waclawek
Sent: Wednesday, August 31, 2016 10:58 PM
To: HW-news
Subject: Re: RE: C: co lze oèekávat

Aha, zaujimave.

Takze na uvod zhrniem to, co uz bolo povedane:

- znamienkovost char (bez explicitneho signed/unsigned) je implementation-defined, overil som si v manuali XC8 ze to je vzdy unsigned

- pretecenie u unsigned premennych je legalne, sposobi wraparound (t.j.
char x = 0; x--; vysledok je x == 0xFF)

- index pri pouziti prvku pola je vyraz, tento sa vyhodnocuje ako vsetky ostatne vyrazy t.j. podlieha "usual arithmetic conversions" co v tomto pripade znamena konverziu na int, ten je 16-bitovy, t.j. [x + 1] je index na 0x100-ty prvok pola

- spravna oprava Vasho kodu je urobit maskovanie (alebo pretypovanie na unsigned char) toho indexu


Teraz vysvetlenie toho co vidime: zacnime tym, ze podla 6.5.2.1#2: 

The definition of the subscript operator [] is that E1[E2] is identical to
(*((E1)+(E2))) 

pricom jeden z E1/E2 musi byt smernik a druhy celociselny vyraz. Takze sa jedna o tzv. smernikovu aritmetiku, a klucom je jej presny popis v 6.5.6#8 (ospravedlnujem sa za dlhsi citat, ale je to v tom):

When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integer expression. In other words, if the expression P points to the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N (where N has the value
n) point to, respectively, the i+n-th and i?n-th elements of the array object, provided they exist. [...] If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.

(Ta vynechana cast sa tyka toho, co je spomenute aj v poslednej vete, ze je dovolene aj to, aby vysledny smernik ukazoval na jeden prvok presne za koncom pola, ale nesmie sa dereferencovat; vysvetlenie pre tuto zvlastnost najdete v mojej oblubenej knihe od Dereka Jonesa :-) ).

Takze nielenze dereferencovat pointer ukazujuci mimo pola, ale dokonca co i len *pocitat ho* (!), ma za nasledok "undefined behaviour" (t.j. aj &A[x+1], co nic nedereferencuje, je pre male pole nespravne).

"Undefined behaviour" znamena pochopitelne uplne cokolvek, od spadnutia prekladu cez spadnutie programu, necakaneho vysledku, nerobenia nic (t.j.
totalneho vyoptimalizovania, to je moje oblubene nedefinovane spravanie
:-) ) cez spravny vysledok dosiahnuty nespravnym postupom a spravny vysledok len pre niektore vstupy, az po spravny vysledok vzdy.

"Undefined behaviour" treba v skutocnosti chapat ako pokyn pre programatora "toto nerob"; pre tvorcu prekladaca to vsak znamena "toto mozes pouzit, ak sa Ti to hodi na optimalizaciu". XC8 je jeden z najlepsich, ak nie vobec najlepsi prekladac pre 8-bitaky, a teda taketo veci bez vahania vyuziva v hojnom mnozstve.

Takze v tych dvoch Vasich pripadoch prekladac vysiel z predpokladu, ze kedze pole ma 3 prvky, nikdy index nebude vacsi ako jeden byte (a ked bude, tak "undefined behaviour", je to problem programatora). V tom prvom pripade pravdepodobne vysiel z este dalsieho predpokladu, pole on sam alokoval na nizkych adresach a tym mohol predpokladat, ze vsetky prvky pola maju jednobytovu adresu - to pravdepodobne suvisi s existenciou jednobytovych smernikov v XC8 (pri tejto prilezitosti zopakujem SMERNIK NIE JE ADRESA, NIE JE ADRESA, NIE JE ADRESA). V tom druhom pripade sice vypocet dopadol z formalneho pohladu dobre, naozaj sa ukazuje na 0x100-ty prvok pola aj ked neexistuje; ale vypocet je jednoduchsi, konstanta +1 je pripocitana vopred k adrese pola. Je to sice spravne ale ktovie ako by to dopadlo pre nejaky komplikovanejsi pripad, ci by sa nestal nejaky problem ak by prekladac aj nadalej vychadzal z predpokladu ze pole je dostatocne male aby akykolvek ofset bol v ramci jedneho byte. Ten treti priklad je spravny urcite, vypocet sa robi poctivo najprv ako 16-bitove scitanie x +
1 a potom sa 16-bitovo pricita adresa pola.


wek





----- Original Message ---------------

>char arrayA[0x103] ;
>
>int main(void) {
>    volatile char x = 0;
>    volatile char a = 3;
>    x--;
>    if (a != arrayA[x + 1]) {
>        __builtin_software_breakpoint();
>    }
>    return 0;
>
>!int main(void) {
>!    volatile char x = 0;
>0xFFCC: MOVLW 0x0
>0xFFCE: MOVWF x, ACCESS
>!    volatile char a = 3;
>0xFFD0: MOVLW 0x3
>0xFFD2: MOVWF a, ACCESS
>!    x--;
>0xFFD4: DECF x, F, ACCESS
>!    if (a != arrayA[x + 1]) {
>0xFFD6: MOVF x, W, ACCESS
>0xFFD8: MOVWF __pcstackCOMRAM, ACCESS
>0xFFDA: CLRF 0x2, ACCESS
>0xFFDC: MOVLW 0x1
>0xFFDE: ADDWF __pcstackCOMRAM, F, ACCESS
>0xFFE0: MOVLW 0x0
>0xFFE2: ADDWFC 0x2, F, ACCESS
>0xFFE4: MOVLW 0x7D
>0xFFE6: ADDWF __pcstackCOMRAM, W, ACCESS
>0xFFE8: MOVWF FSR2, ACCESS
>0xFFEA: MOVLW 0xE
>0xFFEC: ADDWFC 0x2, W, ACCESS
>0xFFEE: MOVWF FSR2H, ACCESS				// arrayA @0xE7D,
>FSR2= 0xF7D
>0xFFF0: MOVF a, W, ACCESS
>0xFFF2: XORWF POSTINC2, W, ACCESS
>0xFFF4: BTFSC STATUS, 2, ACCESS
>0xFFF6: GOTO 0x0
>0xFFF8: NOP
>!        __builtin_software_breakpoint();
>!    }
>!    return 0;
>!}
>0xFFFC: GOTO 0x0
>
_______________________________________________
HW-list mailing list  -  sponsored by www.HW.cz Hw-list na list.hw.cz http://list.hw.cz/mailman/listinfo/hw-list



Další informace o konferenci Hw-list