zakerna zahada GCC
Pavel Hudecek
edizon na seznam.cz
Středa Únor 7 22:33:31 CET 2018
Tak ono v závislosti na platformě, může při vhodných okolnostech čtení mimo
meze vyvolat nějaký memory access error. Takže tohle pořadí prostě není
správné.
Ale že překladač na tento potenciální problém zareaguje nezkoumáním druhé
podmínky, mi přijde jako něco naprosto absurdního. Nejlíp asi reportovat
jako error.
PH
-----Původní zpráva-----
From: Jaroslav Buchta
No ja to chapu tak, ze se vyrazy vyhodnocuji zleva doprava a pokud je
test na hodnotu prvni, tak se provede. Asi podobne, jako se bezne pise
napr.: if (s != NULL && s[x] != 0).... Coz mi bylo dlouho proti srsti
ale zrejme to odpovida pravidlum.
Ale tohle me fakt prekvapilo, samozrejme je to chybny zapis ale pri
respektovani zapisu vyrazu by nevedl k chybne funkci programu, pri
prekroceni rozsahu pole jsem predpokladal ze je hodnota v danem miste
pameti nevyznamna, nelogicke vynechani casti podminky pak vedlo k chybne
funkci.
Dne 07.02.2018 v 21:11 Jindroush napsal(a):
> Je mozno si to vyzkouset:
> https://godbolt.org/
> x86-64, -m32 -O99 -Wall
>
> #include <cstdlib>
>
> char text[19];
> char text2[19];
>
> int main()
> {
> for( size_t idx = 0; text[idx]!=0 && idx < sizeof( text ); idx++)
> {
> text2[idx]=text[idx];
> }
> }
>
> Opravdu tu druhou podminku eliminuje a uplne tomu vysvetleni nerozumim.
> Kdyby idx<sizeof(text) bylo vzdy true, tak bych tomu rozumel. Protoze
> podle normy idx smi legalne nabyvat pouze hodnot mezi 0 az sizeof(text)-1,
> tak skutecne podminka je vzdy true a smi se eliminovat.
> Ale to by prece platilo i pro ten opacny pripad, kdy ji napisu jako prvni?
>
> J.
>
> On 7.2.2018 18:26, Jan Waclawek wrote:
>> 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
>> )
>>
>> 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