zakerna zahada GCC

Jan Waclawek konfera na efton.sk
Středa Únor 7 18:26:25 CET 2018


Kuzelne.

Dovod ste uviedli hned na zaciatku:

> Uz vim, ze je to v principu spatne a prvni cast si sahne mimo 
> rozsah pole, ale ze by to prekladac poresil takto?

Moze, pretoze nasledok je nedefinovane spravanie. A co nie je zakazane, je
dovolene; a nerobit nieco, co netreba, je optimalne... ;-)

V tomto konkretnom pripade je to nasledok poslednej vety C99 6.5.6#8 (co
spolu s nasledujucim odstavcom je definicia toho co bezne nazyvame
smernikova aritmetika, a ktora je tu aplikovana vdaka tomu ze v C
neexistuju polia, 6.5.2.1#2):

If the result points one past the last element of the array object, it
shall not be used as the operand of a unary * operator that is evaluated.

(slovicko "shall" tu vedie kvoli 4#2 na nedefinovane spravanie)

To "one past" je taka kuriozita, ktora dovoluje smerniku nadobudat hodnotu
1 za polom. Tym sa dovoluje optimalizujuci postinkrement po pristupe k
prvku pola v cykle. Nesmie sa samozrejme takyto smernik dereferencovat,
ale moze sa pouzit napriklad na porovnanie alebo sa moze dekrementovat (co
zase dovoluje predekrement pri opacnom cykle). (Musite samozrejme zabudnut
na naivnu predstavu ze hodnota smernika *je* cislo, a uz vobec nie adresa.
Moze byt, ale nemusi - a norma predpisuje vseobecne spravanie, nie nejaku
jednu konkretnu implementaciu, co je vlastne zaruka portability. Moze to
byt ciste hardwarovo implementovane, s hardwarovou implementaciou ochran
proti vylezeniu z pola. Do urcitej obmedzenej miery nieco take MMU vlastne
robi.).

Bezne ukazanie niekam uplne mimo pola vseobecne riesi popis operatora * v
6.5.3.2#4:
If an invalid value has been assigned to the pointer, the behavior of the
unary * operator is undefined. 

Mimochodom, zaujimalo by ma, ci bol vypisany nejaky suvisiaci warning,
najma v suvislosti s -Warray-bounds (pripadne ak sa jedna o novsiu veriu
gcc, tak -Warray-bounds=2
https://gcc.gnu.org/onlinedocs/gcc-7.3.0/gcc/Warning-Options.html#index-Warray-bounds
)

wek


PS. 
> definuj text[19] ako volatile,
Nie, to tu nemusi pomoct, lebo pre tuto "optimalizaciu" prekladac
nepotrebuje poznat *hodnotu* prvku z pola, kedze vopred *vie*, ze citanie
z nej ma nedefinovany nasledok, pretoze to uz nie je prvok pola.

*Moze* to pomoct, lebo sa bavime o nedefinovanom spravani, ale nemusi.

Ale mas pravdu, vhodne nastavene volatile pomoze: celkom iste pomoze
kvalifikovat idx ako volatile, pretoze vtedy prekladac nemoze vediet, aku
hodnotu ma idx na zaciatku toho porovnania, a teda akakolvek
"nedefinovanost" spravania je mimo moznosti detekcie kompilatorom a je
dana uz len konkretnym hardwarom, na ktorom to pobezi.



>Am 07.02.2018 um 17:09 schrieb Jaroslav Buchta:
>> Me jde o pripad, kdy je leva podminka splnena a idx uz ma hodnotu
>> treba 19, 20... coz podminku nesplnuje (pokud jsem neco neprehlednul)
>> a iterace se provede. Dle disassebleru neni druha cast podminky vubec
>> zohlednena, testuje se jen znak v retezci na 0.
>> Kopirovani tady musim provadet po znaku, pridavaji se do dynamicky
>> alokovaneho pole s nejakou inteligenci a tady neni kvalt.
>> S dosazenim funkcnosti si poradim, spis si to potrebuju vysvetlit,
>> tento problem se muze vyskytnout i jindy a jinde.
>>
>> Dne 07.02.2018 v 16:15 Jindroush napsal(a):
>>> Dobry den,
>>> nejsem si ted jist, jestli jsem pochopil spravne problem, ale podle
>>> mne mluvite o vlastnosti C, tj. short-circuitingu AND operatoru.
>>> Staci vyhodnotit levou cast a jde se dal, na poradi podminek zalezi.
>>> To je dulezite si uvedomit i u funkci, ktere maji vedlejsi efekty.
>>>
>>> Jinak ten vas kod nezaruci zero-termination, nebo ano?
>>>
>>> Fungovat v debugu by to nemelo, jen to nesletelo, protoze tam byla
>>> jinak a jinde alokovana pamet.
>>>
>>> Co vam brani pouzit bezpecnou knihovni alternativu - strlcpy?
>>> (Pripadne ji nekde obslehnout).
>>>
>>> J.
>>>
>>> On 7.2.2018 14:56, Jaroslav Buchta wrote:
>>>> Mam datovy typ:
>>>>
>>>> typedef union
>>>> {
>>>>      struct {
>>>>          uint8_t b[32];
>>>>      } ba;
>>>>
>>>>      struct {    //common all records (chained records exception)
>>>>          uint32_t id;            // id MSb == 0
>>>>          uint32_t tstp[2];
>>>>      }cmn;
>>>>
>>>>      struct {    //Text Single or Start Record
>>>>          uint32_t id;            // id MSb == 0
>>>>          uint32_t tstp[2];        //timestamp in miliseconds, record
>>>> type FLCLOG_RT_* at 4 MSbs
>>>>          uint8_t  sendRq;        //send to server request
>>>>          uint8_t  text[19];    //content, max length
>>>>      }recTxt;
>>>>
>>>> ...
>>>>
>>>> }FLCLOGRECORD;
>>>>
>>>> Pokud napisu podminku
>>>>
>>>> for (int idx=0; rec.recTxt.text[idx] != 0 && idx <
>>>> sizeof(rec.recTxt.text); idx++)
>>>>
>>>> tak se to vykasle na druhou cast a index klidne leze na 19 a vic
>>>> dokud neni v pameti nahodou nula (kopiruje to strin ktery muze ale
>>>> nemusi koncit 0) Uz vim, ze je to v principu spatne a prvni cast si
>>>> sahne mimo rozsah pole, ale ze by to prekladac poresil takto?
>>>>
>>>> Kdyz podminky prehodim
>>>>
>>>> for (int idx=0; idx < sizeof(rec.recTxt.text) &&
>>>> rec.recTxt.text[idx] != 0; idx++)
>>>>
>>>> Tak to funguje OK ale uz jsem nemel cas zkoumat disassembler. V
>>>> neoptimalizovanem kodu to fungovalo i pro prvni variantu.
>>>>
>>>> Vysvetli to chovani nekdo?
>>>>



Další informace o konferenci Hw-list