Na, de miért?
while (isupper((unsigned char) p[j])) buff[j++] = p[j];
Az '=' operátor right to left asszociatív. Vagy dönthet a fordító úgy, hogy a magasabb precedenciájú kifejezéseket akármilyen sorrendben értékelteti ki futásidőben, s majd a részeredmények felhasználásával lesz az értékadás jobbról balra? Mert én úgy gondoltam, amikor ezt a sort leírtam, hogy előbb meghatározza p[j] értékét, majd utána ezt értékül adja buff[j]-nek, majd j-t növeli eggyel.
Ezt a warningot egyébként a Qt Creator zúdítja rám. Miért nem mcedit-tel írom a forráskódot? Akkor boldog tudatlanságban élhetnék... :)
Megoldás. Illetve megkerestem szemmelveréssel - elratott egy darabig, amíg végignéztem a
grep -anF '++' *.[ch] | less
eredményeit. Egyetlen helyen használtam ezt a csúnya megoldást. Kijavítottam. Már úgy értem, abban a kódban, amelyikben szólt a Qt Creator mögött lévő analizátor, a nyitóban idézett volt az egyetlen ilyen, de van egy sokkal fontosabb kód, amiből termék lesz, abban nem szeretnék bugot. Abban volt egy ilyen csúnyaság.
- 762 megtekintés
Hozzászólások
>Ezt a warningot egyébként a Qt Creator zúdítja rám.
Nem a fordítóból veszi? Saját értelmezője és warningjai vannak? Nem hinném. Nézz utána, hogy milyen parancsot futtat, gondolom szigorúbb warningokat kér magától, mint ahogy te szoktad paraméterezni a fordítót.
IMHO ez a j++ indexben már önmagában is zavaró, én a falra mászok ezektől. Nálam ez így nézne ki: {buff[j] = p[j]; j++;} Minek terheljem az olvasót azzal, hogy akkor ez posztfix, tehát kiértékelés után lesz inkrementálva és satöbbi? Leírnám úgy ahogy történik sorban és kész. Ezeket már tényleg kibogozza a fordító optimálisra akárhogy is írod az ekvivalens kifejezéseket.
- A hozzászóláshoz be kell jelentkezni
Igazából az a kérdésem, hogy ez a sor egyértelmű a C-ben, vagy a fordítható fordíthatja-e úgy, hogy az értékadás bal oldalával foglalkozik előbb, ott inkrementálja az indexet, majd a jobb oldal ezt a növelt értéket használja. Szerintem ezt nem teheti a fordító - ebben tévedhetek -, egyértelműnek és jónak látom ezt a sort. Azokkal itt nem kívánok foglalkozni, akik kényszeresen kiteszik oda is a zárójelet, ahova nem kell, hátha a fordító rosszul emlékszik a precedenciára, és hasonlók. A mask << x + 4 is egyértelmű, de ha valaki zárójel fetisiszta, az írhatja felőlem, hogy mask << (x + 4), csak ne szaladjunk abba bele, hogy muszáj, mert biztos bugos a fordító, vagy annak a feje, aki olvassa a kódot.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
>Szerintem ezt nem teheti a fordító - ebben tévedhetek -, egyértelműnek és jónak látom ezt a sort.
>Azokkal itt nem kívánok foglalkozni, akik kényszeresen kiteszik oda is a zárójelet, ahova nem kell, hátha a fordító rosszul emlékszik a precedenciára, és hasonlók.
Az a baj, hogy aminek egyértelműnek kellene lenni szerinted - nevezetesen, hogy mi a kiértékelési sorrend - az a jelek szerint nem is egyértelmű a szabvány szerint. (Én se tudtam.) Ezért mondom, hogy mindenki sokkal jobban jár, ha sorba le van írva, hogy mit kell csinálni. Fel se merülne a probléma, ha az inkrementálás külön sorban lenne leírva úgy, ahogy ésszerű volna.
Persze a C nyelv egy trükkhalmaz, még akkor is megviccelheti az embert, ha mindent háromszor körüljár. De ésszerűbb legalább a triviális komplexitást elkerülni. Az olyanokat, mint például ez, hogy indexben inkrementáljuk a változót. Szép trükkös, ismerem én is az egysorosokat, de már nem írok le ilyeneket többé. Jobb a békesség.
- A hozzászóláshoz be kell jelentkezni
Az indexben inkrementálással nincs baj, itt a gond abból lehet, hogy ezt az indexet máshol is felhasználom, s kérdés, az még az inkrementálás előtt vagy után történik-e. Az asszociativitásból az jönne, hogy előbb, de valóban, ahogy NevemTeve írta is, lehet ezt úgy fordítani, hogy rossz legyen. Ugye, lehet ilyet is csinálni:
a = b = c = 5;
Ha nem lehetne használni az indexben inkrementálást, akkor felesleges lenne a pre- és postincrement. Azért van, hogy használjuk. Engem az lepett meg, hogy van arra esély, hogy ez úgy forduljon le, hogy rossz legyen.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
buff[j++] = p[j]
A derék fordító maga dönthet arról, hogy ebből mit generál:
#1:
char *tmp= &buff[j];
++j;
*tmp = p[j]
#2:
buff[j] = p[j];
++j;
- A hozzászóláshoz be kell jelentkezni
A jövőálló megoldás nyilván két index használata:
k= j;
while (isupper((unsigned char) p[j])) {
buff[k++] = p[j];
}
Ha mondjuk kiderül, hogy az elején lévő számjegyeket át kell lépni:
k= j;
while (isdigit((unsigned char) p[j])) ++j;
while (isupper((unsigned char) p[j])) {
buff[k++] = p[j];
}
- A hozzászóláshoz be kell jelentkezni
Mármint nem j, hanem j++.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Hát ebben van igazság, bocsi.
Így egymás közt azt is bevallhatom, hogy gyakran így csinálom a hasonlókat:
for (--j; isspace(from[++j]););
for (--j; ++j<limit && isdigit(from[j]); )
into[k++]= from[j]
- A hozzászóláshoz be kell jelentkezni
Itt lehet bővebben tájékozódni:
https://en.wikipedia.org/wiki/Sequence_point
- A hozzászóláshoz be kell jelentkezni
#1 egy reális veszély? Azért nyomasztó, mert néhány helyen használtam ilyesmit kódban, s elég kellemetlen nagyon sok tízezer sorban az összes ++-ra keresni, s egyesével átnézni. Abból indultam ki, hogy az értékadás miatt előbb lesz kiértékelve a jobb oldal, s csak utána a bal, mert ez a #2, s akkor jó a kód.
Szerk.: Abban lehet bízni, hogy egy adott fordító legalább következetesen #2-t fordítja minden hasonló esetben? Vagy kétségbeesetten kezdjem felkutatni azt a kevés helyet a jó nagy kódban, ahol ilyen van?
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Vannak különféle checker/linter programok, például maga a gcc (latest: 15.1), különösen a -fanalyzer opcióval.
Szerk: most látom, hogy a -fanalyzer -t a -O2 -vel kombinálva még éberebb. (Felteszem, hogy magasabb optimalizációnál pláne.)
- A hozzászóláshoz be kell jelentkezni
https://en.cppreference.com/w/cpp/language/eval_order
"Order of evaluation of any part of any expression, including order of evaluation of function arguments is unspecified (with some exceptions listed below). The compiler can evaluate operands and other subexpressions in any order, and may choose another order when the same expression is evaluated again.
There is no concept of left-to-right or right-to-left evaluation in C++. This is not to be confused with left-to-right and right-to-left associativity of operators: the expression a() + b() + c() is parsed as (a() + b()) + c() due to left-to-right associativity of operator+, but c() may be evaluated first, last, or between a() or b() at runtime"
Eleg egyertelmu, nem kell ezt tulragozni.
- A hozzászóláshoz be kell jelentkezni
Nem egyszerű azért ez a dolog, pl. az első oké, a második tilos a fordítónak:
val = a[i++] - b[j++] -> val=a[i]-b[j]; ++i; ++j;
val = a[i++] || b[j++] -> tmp=a[i]; val= tmp? tmp: b[j++]; ++i;
- A hozzászóláshoz be kell jelentkezni
Nem írtam, hogy ez C, viszont attól még ugyanígy igaz. Nagyszerű. :( Kereshetem a forrásban a nem sok, de létező ilyen megoldásaimat. :((
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Még mindig a linter/checker programokat ajánlom.
gcc pl. tudja, hogy ez nem jó:
char oda[5];
sprintf(oda,"%u", n);
ez meg jó:
char oda[5];
sprintf(oda,"%u", n%10000);
- A hozzászóláshoz be kell jelentkezni
Ja, hogy kiszúrja, ha nem fér bele...
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Itt ez ugyan valami gcc, de PIC32, MIPS core-ra. Erre nem tudom, van-e szerszám. A másik topikban felvetett nem blokkolós I/O az viszont linuxos PC-n futó program.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Úgy szokták ezt orvosolni, hogy legalább a program legnagyobb részét meg lehet úgy írni, hogy mindkét platformon forduljon, és akkor a toolok is mind végigmennek rá.
- A hozzászóláshoz be kell jelentkezni
Van egy néhány megabyte-os include file, amely leírja a mikrokontroller regisztereit, hardware-ét. Már itt baj lesz szerintem.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Néha az a legjobb kezdet, ha csak úgy elkezdjük, és meglátjuk, mi lesz.
- A hozzászóláshoz be kell jelentkezni
Ez így elég átláthatatlan, ez az a fajta hekkes kódírás, amit nem kéne C-ben tolni, sokan az ilyen agymenések miatt utálják a C-t. Nekem az jön le, hogy egy átlag C fordító előbb megnöveli a j értékét, így a p[j]-ben a j már egyel meg lesz növelve a kifejezés végén. Semmit nem nyersz, ha ilyen tömör vagy, használj egy átmeneti változót, és írd úgy a kódot, hogy egyértelmű lépései legyenek a kiértékelésnek, így a kódod az összes fordítóval azonosan fog működni. Ezen így semmit nem nyersz, hogy megúszol ugyan egy átmeneti változót, de közben a kód kétértelműségbe, meg előre láthatatlan hibákba fut, ez inkább nekem szándékosan önszopatásnak tűnik.
“The world runs on Excel spreadsheets.” (Dylan Beattie)
- A hozzászóláshoz be kell jelentkezni
Á, nem tudod te, mi a jó!
int8_t asciihex2nibble(char c, uint8_t *x) {
int8_t err;
err = 0;
if (c >= '0' && c <= '9') {
*x = c - '0';
} else if (c >= 'A' && c <= 'F') {
*x = c - 'A' + 10;
} else if (c >= 'a' && c <= 'f') {
*x = c - 'a' + 10;
} else {
err = 1;
}
return err;
}
int8_t asciihex2byte(char **p, uint8_t *x) {
int8_t err;
uint8_t data;
err = asciihex2nibble(**p, &data);
if (!err) {
err = asciihex2nibble(*++*p, x);
if (err) {
--*p;
} else {
*x |= data << 4;
++*p;
}
}
return err;
}
Figyelmedbe ajánlom a *++*p kifejezést! :)
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Na az pont egy performanciacsökkentő intézkedés... inkább így:
int8_t asciihex2byte(char **pptr, uint8_t *x) {
register char *p= *pptr;
...
*pptr= p;
return err;
}
- A hozzászóláshoz be kell jelentkezni
A fordító ilyen helyzetben minden egyes esetben megcsinálja a kétszeres dereferálást? Nem jön rá, hogy ideiglenes változóban tarthatja átmenetileg a karakter címét?
Mikrokontrolleren - de az is gcc - valami ilyesmit írtam, hogy valami[i].akármi[bármi.idx].bigyó, ezt sokszor. Aztán amikor átírtam, hogy p = &valami[i].akármi[bármi.idx]; p->bigyó, majd mindig a p-vel hivatkoztam, a forráskódom ugyan szebb lett, de byte-ra pontosan ugyanakkorára fordult a kód. Nyilván az utolsó member már változott, szóval p->másvalami, és így tovább.
Csak azt akarom mondani, a fordító simán megcsinálta, pedig csak -O1 volt.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Hát, az optimalizáló compiler élete nem csak játék és mese. Például arra sincs garancia, hogy a `p` és az `x` nem ugyanoda mutat, ez korlátozza az optimalizációt.
Ezen segít a 'restrict' módosító: https://en.m.wikipedia.org/wiki/Restrict
- A hozzászóláshoz be kell jelentkezni
Figyelmedbe ajánlom a *++*p kifejezést! :)
Mondjuk ez így ránézésre is sima netto önszopatás. Bőven elég fos undefined behavior van a kedvenc nyelvetekben magától is, hogy elég problémát generáljon, minek ilyeneket csinálni? Mi a jó ebben azon kívül, hogy rá lehet rántani, hogy mennyire okos vagy?
- A hozzászóláshoz be kell jelentkezni
Meta: annál azért jobb érv kellene, mint hogy 'laikusoknak nem intuitív'.
Itt van például egyes nyelvekben a 'lambda' (beágyazott névtelen függvény): én ugyan utálom, mert nekem kicsit sem intuitív, de attól még szívesen elismerem, hogy egy fasság, amit kár volt kitalálni.
- A hozzászóláshoz be kell jelentkezni
Laikusként is értem (érteni vélem), hogy mit csinál pár sec gondolkodás után (előszedi a következő elemet valami listából), ettől még nehezen tudom elképzelni, hogy ezt ne lehetne olvashatóbban leírni alig kevesebb több* karakterből, amitől nem rándul egy pillanatra görcsbe az agya az olvasónak.
*szerk
- A hozzászóláshoz be kell jelentkezni
A beágyazott névtelen függvény nagyon cool cucc. Például arra is jó, hogy ne kelljen nevet adni valaminek aminek felesleges, és hogy egy helyen legyen ami összetartozik. Eseményvezérelt programozáshoz pótolhatatlan.
Van ahol szerintem is túlzás. Például a Java stream API nem a kedvencem, legalábbis amikor eleve kész gyűjteményekre használjuk, mert nekem érthetetlenebb mint az imperatív megközelítés és általában a teljesítménye is rosszabb.
- A hozzászóláshoz be kell jelentkezni
Nagyon, például egy debuggolásnál, hibajelentésnél, újrafelhasználásnál: hát ott a 'foo' függvény 65-70 százalékánál van a beágyazott függvény, amit hagyományos észjárással egy külön 'neves' függvényben csináltunk volna.
- A hozzászóláshoz be kell jelentkezni
Hibajelentéshez csak be kell pasztázni a stacktrace-t, abban benne van, hogy melyik sorból jött. Nem értem ezzel mi a probléma. Azzal lehet gond, hogy össze tudd kötni, hogy annak az eseménykezelőnek mi volt az eredendő oka. De ez ugyanúgy fennáll akkor, ha van neve, általánosságban igaz a callbackes rendszerekre, hogy néha nehéz a callbackből visszakövetni, hogy mégis ez mire válaszként fut éppen.
- A hozzászóláshoz be kell jelentkezni
Külön viccess ilyennel "menőzni" egy olyan topic-ban ami alapján egyértelmű, hogy tudásbeli hiányosságok vannak a nyelv alapvető működésével kapcsolatban, ráadásul érintőlegesen pont a pre/post increment kapcsán...
- A hozzászóláshoz be kell jelentkezni
Nem menőzésnek szántam, hanem példának, mert szerintem ez kifejezetten szép a C-ben. Azért indítottam a topikot, mert nem tudtam, hogy a precedencia és a műveletek végrehajtási sorrendje függetlenek egymástól. Abban, amit példaként hoztam, szerintem nincs ilyen potenciális bug.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Hogy írtad volna egyszerűbben? Szerintem épp a *++*p az, ami áttekinthető. Van a karakterre mutató pointerem, aminek megkaptam a címét, ezt dereferálom, majd növelem a karakterre mutató pointert, ami után az általa mutatott karaktert átadom a függvénynek. Épp ez az, ami tiszta, világos, nem pedig az, amikor egy rakás soron keresztül kell ezt bogozni.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Meg szerencse, hogy a junior kollegad kodjat prodba kerules elott elkaptad code review-n, igy nem lett belole kulonosebb baj!
A strange game. The only winning move is not to play. How about a nice game of chess?
- A hozzászóláshoz be kell jelentkezni
A példát látod bugosnak? Vagy az a problémád, hogy bízunk egymásban, nem vagyunk software-fejlesztő cég, s így nem nézegetjük egymás kódjait?
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
TL;DR: a compiler warningok általában jogosak, de legalábbis érdemes kivizsgálni miért jönnek. (Na mondjuk a 'dereferencing type punned pointer' az fasság: azért warning-ol, mert különböző tipusú pointerek között type-cast-olsz. Igen, mert ha azonos típusúak lennének, akkor nem kellene a type-cast.)
- A hozzászóláshoz be kell jelentkezni
Ilyen tényleg van? :) Ez valóban idiotizmus. Más a tévedés, és más az, amikor a programozó tudja, hogy mit csinál. Például legyen egy struktúra, amelyre CRC-t kell számolni. Ha nem packed, akkor gondolni kell arra, hogy a hézagokban lehet RAM szemét, szóval előbb memset(), utána a struktúra arra a területre, majd a CRC számítása, természetesen type-cast-olt pointerrel.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni