Nézzük az alábbi C kifejezést:
unsigned int *ptr;
if (ptr && *ptr && --*ptr == 0) timeout();
Egyfelől logikus, amit csinál. Megnézi, a pointer nem NULL-e, utána megvizsgálja, a ptr által mutatott unsigned int változó nagyobb-e nullánál, ha ez is igaz, csökkenti azt, majd megnézi, nullává vált-e. Ha igen, hívja a timeout() függvényt. Ha a ptr == NULL, vagy érvényes, de az általa mutatott változó nulla, akkor nem nyúl semmihez. Ha a ptr által mutatott érték nem nulla, csökkenti azt, de csak akkor hívja timeout() függvényt, ha elérte a nullát.
Ilyesmit szoktam írni, de egy pillanatra megrémültem. A -- precedenciája nagyobb, mint az && operátoré, így akár az is lehetne, hogy előbb minden vizsgálat nélkül csökkent, s csak utána értékeli ki az &&-ek által határolt tagokat. Bár, ha így lenne, szinte semmire sem lenne jó az, hogy amint a logikai kifejezésről tudható annak értéke, nem értékeli ki azt tovább, hanem kilép.
Gyakorlatiasan, az elvárt módon csinálja, de ha szigorúan nézem a precedenciát, akkor szerintem nem működne ez jól.
- 2515 megtekintés
Hozzászólások
Akkor ez már a második tagra is igaz: a * operátornak is nagyobb a precedenciája, mint a &&...
(ergo ha ptr==NULL, akkor exception)
- A hozzászóláshoz be kell jelentkezni
Ez így van, eszembe is jutott, de akkor is furcsa. Tegyük hozzá, a nyelv így használható, nem pedig úgy, ha a precedencia azt az értelmezést kapná, amit említettem.
Másik érdekesség, hogy char s[2][256]; left to right asszociatív, amit úgy lenne logikus értelmezni, hogy s[2]-ből van 256 darab, holott valójában s[256]-ból van 2 db. Ez szintén logikus furcsaság, hiszen s[i] így tud hivatkozni az első vagy második egybefüggő 256 byte-os területre.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
A && kiertekelesi pont, a precedencianak semmi koze hozza, jo a kodod.
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
Egyfelől értem, de az && ugyanúgy egy operátor, mint a többi, csak elég alacsony precedenciával. Így szoktam írni, csak belémhasított egy rossz érzés, nem kell-e esetleg szétszedni több if-re az ilyen szerkezeteket. Mindamellett megnéztem a Compiler Explorer oldalon, mivé fordítja a gcc, s azt láttam, amire számítottam.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
A precedencia arról szól hogy hogyan zárójelezd a kifejezést. Cukorka hogy elhagyhass pár zárójelet.
Kiértékelési sorrend az attól függ.. ..nem nagyon függ semmitől, ha odaér a kód, akkor kiértékeli, előbb nem.
- A hozzászóláshoz be kell jelentkezni
és
És az első oldalról egy idézet: "Precedence and associativity are independent from order of evaluation."
- A hozzászóláshoz be kell jelentkezni
Precedence and associativity are independent from order of evaluation.
Azt hiszem, ez a lényeg, ezt nem tudtam. Köszönöm.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
off
A nicknév a zabból jön, nem a cukorból.
on
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Szerintem nem téged nevezett cukorkának, hanem a https://en.wikipedia.org/wiki/Syntactic_sugar-ra gondolt.
- A hozzászóláshoz be kell jelentkezni
Azt hittem, hogy a „szereti, mint paci a kockacukrot” mondás alapján jött ez ki a nickemre. :)
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Attol, hogy valakinek uldozesi maniaja van, meg nem lehetunk benne biztosak, hogy nem uldozik.
:)
- A hozzászóláshoz be kell jelentkezni
Cukorka hogy elhagyhass pár zárójelet.
A "modern", fiataloknak szóló nyelvekhez illik lint-et használni. Ez pedig alap configban beszól, hogy ha egy && és || van egy kifejezésben, de nem zárójelezed a &&-t. Nem hiba, csak stílusbeli kötelezőség, hogy az új generáció nehogy ne értse meg. Hol van a 3.60-as kenyér az hogy az ASM compiler-t is érteni kellett, nem volt egy alap nodejs tudás.
- A hozzászóláshoz be kell jelentkezni
Szerintem, ha fórum bejegyzést is kell nyitnod róla, akkor még neked sem elég egyértelmű, nem hogy másoknak, tehát szét kell szedned.
- A hozzászóláshoz be kell jelentkezni
Nem azért nyitottam a topikot, hogy cinikus megjegyzéseket helyezz el benne. Az világos, hogy hogyan csinálja, mert amikor az && előtt már false lesz, kilép, semmi szükség a további kiértékelésre. Ugyanakkor számomra az is logikus lenne, hogy az
a && --a == 0
kifelyezésben megnézi, minek van a legnagyobb prioritása. A --a lesz az, nosza, csökkentsük az a-t, s mivel ez preincrementes, még minden más előtt tehetné. Ha az a korábban 0 volt, most már nem az, így az && előtti rész igaz lesz.
Örülök, hogy nem így csinálja, csak felvetődött bennem, hogy akár ez is történhetne.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Nem azért nyitottam a topikot, hogy cinikus megjegyzéseket helyezz el benne.
Szerintem semmi cinizmus nincs benne: ha egy sima timeout változó dekrementálásán gondolkodni kell, akkor az nem jó kód, főleg nem, ha nem csak magadnak írod, hanem csapatban dolgozol, ahol más is hozzányúl a kódhoz.
- A hozzászóláshoz be kell jelentkezni
C kódban mindig igyekeztem tömörnek lenni. Nem cél, hogy más is első olvasásra megértse a kód működését. A C előnye éppen a rövidség, tömörség; ereje az összetett kifejezésekben rejlik.
Alapszabály volt, hogy más függvényét sosem tákoljuk. Ha hozzá kell nyúlni, újraimplementáljuk az egész függvényt.
Az is alapszabály volt, hogy ne nagyon legyenek egy képernyőnél hosszabb függvények.
Sokszor még a magam kódjához se nyúltam hozzá. Inkább újraírom a kérdéses függvényt, és lefuttatom rajta az egységteszteket. A tákolásból, javítgatásból mindig csak a gond van.
Ez alól kivétel, ha nagyon jól beazonosítható hiba van, kb. typo szintű. Azt természetesen ,,ér" javítani.
A topikindító kérdésre visszatérve: ilyen esetben mindig meg kell nézni, hogy az aktuálisan használt C szabvány (pl. C89) hogyan rendelkezik, definiált-e a kiértékelési sorrend, vagy hogy egyáltalán a kifejezés minden tagja kiértékelődik-e. Mert lehet, hogy valami működni fog az adott fordítóval az adott architektúrán, de ha ezt a szabvány nem garantálja, akkor tud ám meglepetést okozni egy másik fordítóval, esetleg más arch-ra való fordítás.
- A hozzászóláshoz be kell jelentkezni
Nem cél, hogy más is első olvasásra megértse a kód működését.
vs
A C előnye éppen a rövidség, tömörség; ereje az összetett kifejezésekben rejlik.
Mi előnye van a "rövidség, tömörség"-nek, ha nem cél, hogy "más is első olvasásra megértse a kód működését"?
Kevesebbet kell gépelni egyszer? ( Ami előnyt az újraírással el is veszted. )
Az is alapszabály volt, hogy ne nagyon legyenek egy képernyőnél hosszabb függvények.
Ezt sem értem, hogy miért kell, ha senki más nem kell, hogy megértse.
- A hozzászóláshoz be kell jelentkezni
Mi előnye van a "rövidség, tömörség"-nek, ha nem cél, hogy "más is első olvasásra megértse a kód működését"?
A tömör C kód minden, csak nem könnyen olvasható. Az egyetemen gyakran volt olyan feladat, hogy rácsodálkozz: egy fél sornyi kód mennyi mindent tud csinálni, mennyi trükkös nyelvi elem lehet benne felhasználva. Minket ebben a szellemben tanítottak C-ben programozni.
> Az is alapszabály volt, hogy ne nagyon legyenek egy képernyőnél hosszabb függvények.
Ezt sem értem, hogy miért kell, ha senki más nem kell, hogy megértse.
Mert a hosszabb függvények működését két hét múlva már magad sem érted :-D
Azért szerettük nagyjából képernyőnyi függvényekre bontani a feladatot, mert (a mellett, hogy egy képernyőnyi kódot könnyen átlátsz,) így a függvények nem lesznek annyira speciálisak, és mindegyik jól körülhatárolható részfeladatot végez. Gyakran kiderül, hogy egy-egy ilyen függvény némi generalizálás után felhasználható más problémák megoldása során is.
Egyébként sem volt szokás egymás kódját tákolgatni. Mindenki a maga részének a gondozásáért volt felelős.
- A hozzászóláshoz be kell jelentkezni
Ez az egy képernyős kvázi szabály elég nagy hülyeség. Van egy mérés vezérlés, benne switch () case szerkezet az egyes state-ekre, de azon is belül vannak alállapotok. Igen sok sor, 14 képernyőoldal - most számoltam meg. Lineáris vezérlés, ezt aligha lehet értelmesen szétszedni, sőt, attól válik érthetetlenné, ha nem lineárisan látod, mi következik egymás után.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Mi nem mikrokontrollerre fejlesztettünk. Más volt a use case, a komplexitás, mások voltak a problémák.
Az egy képernyős szabály egyébként is nagyságrendet jelöl ki: lehet jó függvény a három soros és a négy képernyős is. A lényeg, hogy monster függvények ne jöjjenek létre, hanem bontsuk tovább a feladatot.
switch() case is tud olvashatóbb lenni inline-okkal pl.
Kivétel pedig mindig minden alól van.
- A hozzászóláshoz be kell jelentkezni
Erre találták ki a state pattern OOP-ben. De akár C-ben, OOP nélkül is alkalmazható. Egyébként az, hogy valami kerülendő, az nem jelenti azt, hogy néha nem szükséges. Az összes design pattern mellett pl. fel van sorolva egy méretes lista, hogy mikor ne használd.
- A hozzászóláshoz be kell jelentkezni
Ez az egy képernyős kvázi szabály elég nagy hülyeség.
Egyáltalán nem hülyeség, de ennek felismerésére el kell jutni egy szintre.
ezt aligha lehet értelmesen szétszedni
Szinte biztosan szét lehet értelmesen szedni kisebb részekre. ;-)
- A hozzászóláshoz be kell jelentkezni
Szét lehet szedni, ha nem akarod látni, hogy a mérésvezérlés melyik állapota után melyik jön, s azt sem akarod látni, melyik állapotában mit csinál. Tehát, ha az a cél, hogy elveszítsd a fókuszt, mert ahelyett, hogy helyben átlátnád, hogyan működik az egész, vadul lapozgatsz más file-okban, de mire oda jutsz, ahova akartál, elfelejted, mi az ördögöt keresel, akkor valóban szét lehet szedni. Csak nem érdemes.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Az az igazság, hogy ilyen mindenféle megfelelőségek és szép patternek kedvéért borzasztóan el lehet bonyolítani egyszerű dolgokat is. Amikor már el van bonyolítva, akkor a 15 fős csapatból senki nem fogja érteni, hogy pontosan hogy működik az egész és így soha nem fog kiderülni, hogy egyetlen egy fájban sorban leírva az egészet összesen lenne mondjuk ezer sor, amit egy hét alatt azért be tud egy ember cache-elni, és el tud igazodni benne.
Ott van az óriási hatékonyság ugrás, amikor van egy program amit egy ember átlát, vagy van egy csapat, ami bele tud nyúlkálni, amint kimeetingelték, hogy ki az aki az adott részhez ért: ha egy ember átlátja, akkor egy változáskérést 15 perc alatt ki lehet szolgálni prototípus szinten, persze teszteléssel együtt lesz belőle 1-2 nap is mire rendesen elkészül. Ha nem látja át egy ember, akkor a csapat 2-3 hónap alatt talán megcsinálja ugyanazt.
- A hozzászóláshoz be kell jelentkezni
Az az igazság, hogy ilyen mindenféle megfelelőségek és szép patternek kedvéért borzasztóan el lehet bonyolítani egyszerű dolgokat is.
A 14 oldalnyi kód az biztosan nem könnyen átlátható, egyszerű kód. Persze, egyszerű kódokat el lehet bonyolítani, bonyolultakat még bonyolultabbakká lehet tenni, de bonyolult kódokat is lehet egyszerűsíteni. A megfelelő absztrakciós szintek kialakítása a kulcs az egészhez. Pl. 14 rövid mondatban (egy pár utasítás mondatonként) leírni, hogy mit csinál a 14 képernyőnyi kód, az egy-egy mondat részletei meg egy szinttel lejjebb vannak.
Ott van az óriási hatékonyság ugrás, amikor van egy program amit egy ember átlát,
14 mondatnyi szöveget könnyebb megérteni vagy 14 oldalnyi szöveget? 14 oldalnyi szöveget könnyebb átlátni vagy egy oldalnyit?
Persze lehetnek kivételek, de 14 oldal az nagyon soknak tűnik.
- A hozzászóláshoz be kell jelentkezni
Akkor is ott van "mögötte" a 14 oldalnyi szöveg. 14 oldal nem annyira sok, és annak is van előnye, hogy minden egy helyen van. A szerkezetből adódóan meg vannak jelölve az állapotok (felteszem, hogy tabulátorokat még locsemege is használ :-), ugyanúgy látod, hogy hol vannak a logikai részek mintha külön metódusok lennének. Egy bizonyos méretig lehet ez a hatékonyabb megközelítés.
Pont ez a lényeg, hogy el kell dönteni, hogy az adott csatába melyik fegyvereket vigyük, nem mindig ugyanaz a legjobb választás.
- A hozzászóláshoz be kell jelentkezni
Általánosságban nehéz vitatkozni, jó lenne egy olyan példa, ami hasonló nagyságrendű.
Én még nem láttam ilyet, ezért is tudom nehezen elképzelni, hogy van ilyen.
- A hozzászóláshoz be kell jelentkezni
Egyet tudok, ami hasonló lenne, ha én írnám: architektúra emulátor vagy 100 utasítás emulálásával, értelemszerűen gyorsra megírva computed goto-val.
De ez nem az a mindennapos feladat.
- A hozzászóláshoz be kell jelentkezni
A computed goto csak egyszerű demultiplexer, de locsemege ezt írta:
a mérésvezérlés melyik állapota után melyik jön, s azt sem akarod látni, melyik állapotában mit csinál
Az ilyen - egyszerűbb esetben - leírható switch használatával. Csak annyi kérdés marad, hogy elegendő-e a switch, vagy több switch és define: egyszerű. ;)
Létezik olyan technológia, ami gyors, jól áttekinthető és minden esetben stabil működést eredményez. Ez a végesállapotú gép (FSM), avagy automata. A feladattól függően lehet speciális esete is, pl. lézernyomtato - ahol a papír mindig végighalad a mechanikán, vagy adatgyűjtés - ahol mindig ugyanazt a feladatsort kell végrehajtani. De ebbe a kategóriába tartozhat a hálózati kapcsolat felépítése és a regexp feldolgozása is. Egy automatával bármit le lehet írni, és utána egészen kezdők is tudják az elemeit programozni, hiszen csak ki kell tölteni az akciókat. Általános szerkezete:
állapot
esemény
akció -> új állapot
...
...
Ha valakinek még nem áll kézre akkor a Libero lehet a kezdő lépés. Természetesen az állapotgépet meg lehet írni támogatás nélkül is.
Ha jól rémlik, a Libero alap sztorija szerint VMS alatt szerettek volna irdatlan mennyiségű terminált kezelni, amihez persze az időosztás kevésnek bizonyult. Ráadásul userspace programra volt szükség. Ekkor rakták le a Libero alapjait, majd később ezzel a tecnológiával készült a nagy teljesítményű Xitami webszerver is. Az vezérlő szerkezet az assemblertől a shellen keresztül az sql-ig bármilyen programnyelven működik.
Alkalmas talán még mérésvezérlésre is. ;)
- A hozzászóláshoz be kell jelentkezni
Nem cél, hogy más is első olvasásra megértse a kód működését. [...] Sokszor még a magam kódjához se nyúltam hozzá. [...] A tákolásból, javítgatásból mindig csak a gond van.
Szerintem te úgy írsz C programot, hogy nem tudsz programozni.
- A hozzászóláshoz be kell jelentkezni
Ítéljék ezt meg a kollégák és az ügyfelek. Mindenesetre ha valóban nem tudok programozni, akkor kurva nagy májer vagyok, mert ahhoz képest elég jól fizettek. Még szerencse, hogy a főnök nem jött rá :o
Sosem tartottam magam fősodorbeli szoftveresnek, kizárólag alacsonyszintű, hardverközeli dolgokkal foglalkoztam. Később nem-ip-alapú hálózatokkal is.
Kis cég volt, kevés emberrel, speciális (nem mainstream) feladatokkal.
Nálunk le volt osztva, ki melyik modulért felelős. Ennek is van előnye.
Távozott kollégák kódját kellett néha átvenni, meg amikor behúztunk külső forrást, annak is egy személy lett a felelőse.
Nálunk ezek voltak az elvek, hogy decomposition kb. képernyőnyi függvényekre, és függvényen max. pici fix lehet, ha elbukik teszten vagy gyanús, és ha tíz percen belül nem jövünk rá a bajára, akkor újraírjuk. Jól definiálható feladatot végző, kis komplexitású függvényeknél szerintem ez jó döntés volt.
A meglévő függvényeket igyekeztünk generalizálni és minél több feladat elemeként felhasználni.
A kódjaink sokáig futottak, jó esetben évekig nem volt megbootolva eszköz, vagy restart-olva service.
Mindezt bár x86 alapú, de beágyazott környezetben, nem szokványos hardveren.
Túlzás lenne azt mondani, hogy nálunk nem voltak lowlight-ok. Voltak.
Mégis merem azt állítani, hogy összességében működött.
12 éve kiszálltam és másba kezdtem. Most agile alapon, de nem szoftvert fejlesztek.
- A hozzászóláshoz be kell jelentkezni
Nem cél, hogy más is első olvasásra megértse a kód működését. [...] Sokszor még a magam kódjához se nyúltam hozzá. [...] A tákolásból, javítgatásból mindig csak a gond van.
Én erre reagáltam. Ez egyszerűen nevetséges, sőt, bizonyos szempontból szánalmas. Komolyan. Mindegy, hogy mekkora cég és milyen területen dolgozik, ezek egyszerűen védhetetlen kijelentések.
- A hozzászóláshoz be kell jelentkezni
Nem értem, mi ezen a vállalhatatlan. Faszom gondol bele még egyszer, miért úgy írtam meg valamit egyszer régen. Rövid, jól definiált függvényt újraírok inkább a faszba. Hogy jobban értsd.
- A hozzászóláshoz be kell jelentkezni
Nem cél, hogy más is első olvasásra megértse a kód működését.
És code reviewen nem dobják vissza a kollégák? Van egyáltalán normális code review?
C előnye éppen a rövidség, tömörség;
Eddig úgy tudtam, hogy az, hogy kb. egy hordozható assembly.
Alapszabály volt, hogy más függvényét sosem tákoljuk.
Ezzel szemben a realitás az nálunk épp, hogy 2 másik csapat 5 másik kollégája küld nekem mostanság PR-eket ahhoz a servicehez, amihez az 5-ből 4-en sosem nyúltak.
De igen, abban valóban igazad van, nem tákolnak ők sem, fejlesztenek. :)
hogy az aktuálisan használt C szabvány
Szabvány egy dolog, de ahoz még tartozik egy halom best practicle is.
- A hozzászóláshoz be kell jelentkezni
És code reviewen nem dobják vissza a kollégák? Van egyáltalán normális code review?
Vagy vannak-e egyáltalán kollégák?
Itt sok kommentet elolvasva az az érzésem van, hogy ott éppen nincsenek.
- A hozzászóláshoz be kell jelentkezni
Nem volt code review.
Sőt, szerintem egységtesztelésre se fordítottunk elegendő erőforrást.
Ezért mindenki igyekezett a saját szakállára tesztrutinokat írni, hogy lehetőleg még nála derüljön ki, ha gebasz van, ne a rendes tesztelési szakaszban. Mert az sokkal kellemetlenebb volt, mások munkáját is akasztottad vele.
Számomra a nap azzal fejeződött be, hogy feltettem az aznapi munkámat egy tesztkörnyezetre és ráeresztettem a rutinjaimat éjszakára fingatni. Reggel meg nagylevegő a memory map megnyitása előtt.
Sokan azt gondolják, hogy csak az aktuális multinál ezerrel tolt ideológia létezik. Pedig van egy csomó kis (és nagy!) cég is, ahol konzervatívan vagy alternatívan gondolkodnak. Ez múlik a tulajdonosi körön, a cég evolúcióján, adott esetben egy-egy manager berögződésein. E mellett minden iparágnak megvannak a sajátosságai.
Itt van pl. lócsemege, aki nem gondolnám, hogy kezdő. Mégsem strukturál át egy 14 képernyős switch() case-t. Ettől még nem gondolom, hogy ,,nem tud programozni". Lehet egy csomó körülmény, amit nem ismerek, nem ismerünk.
Az emberek automatikusan a saját esetükre vetítenek mindent, a saját helyzetük szemüvegén keresztül ítélik meg a dolgokat, és ezt az (elő)ítéletet generalizálják. Aztán gyakran kiderül, hogy máshogy is van élet.
- A hozzászóláshoz be kell jelentkezni
Ezzel egyetértek!
> Sokan azt gondolják, hogy csak az aktuális multinál ezerrel tolt ideológia létezik. ... ahol konzervatívan vagy alternatívan gondolkodnak.
Ugye a kurrent divatra szokás úgy tekinteni mint szent tehénre, amit még véletlenül sem szabad megkérdőjelezni. Pedig egyáltalán nem ártana, sőt szükséges volna mindin mindent megkapargatni, hogy vajon tényleg az-e a legjobb megoldás és kísérletezni is más irányok felé.
> Itt van pl. lócsemege, aki nem gondolnám, hogy kezdő. Mégsem strukturál át egy 14 képernyős switch() case-t.
Ugye ha átstrukturálod akkor mi lesz belőle? Lesz mondjuk kb 30 függvényed, amiben ugyanaz lesz leírva mint a switch-case-ben. Mitől lett jobb? Pláne ha valóban egymás utáni állapotokról szól a switch-case! Akkor a leges legjobban úgy lehet átlátni, ha valóban egymás alatt sorban jönnek a case-ek.
Hasonlóan, ha valaki egyedül dolgozik és tényleg biztosan tudja, hogy mire értékelődik ki, akkor írjon csak mellékhatásos for ciklust! Pláne ha mondjuk nagyjából determinisztikus a dolog, ha egyszer jól futott le akkor mindig jó lesz! És ha egyszer egy másik embernek meg kell értenie, akkor az majd veszi a fáradságot és megérti! Nem kell a dogmát bigottságig erőltetni.
- A hozzászóláshoz be kell jelentkezni
Ugye ha átstrukturálod akkor mi lesz belőle? Lesz mondjuk kb 30 függvényed, amiben ugyanaz lesz leírva mint a switch-case-ben. Mitől lett jobb?
Erről van szó. Simán lehet olyan körülmény, amiről nem tudok vagy nem látom be, neki mégis valamiért így jobb.
Meg embere is válogatja. Én szeretek mindent agyonstrukturálni, hogy a végén legyenek ,,törzsfüggvényeim" és átlássam csak a lényeget. Ilyenkor nem érdekel, mi történik lentebb. Az egy másik nap problémája, hogy ott is minden fasza legyen, és fentről nézve már ne kelljen ezen gondolkodnom. Fejben könnyen ugrálok, egyben viszont kevés dolgot látok át. Más meg pont fordítva van vele.
- A hozzászóláshoz be kell jelentkezni
Legtöbbször tényleg olyan a feladat, hogy jó a strukturáltság. Meghívod megfelelő paraméterezéssel, s tudod, hogy jól meg fogja csinálni, már rég nem törődsz vele, hogy hogyan. Amikor megírtad, még tudtad. De egy lineáris szekvencia, egy állapotgép, vezérlési struktúra nem ilyen. Eleve a call és ret felesleges stack, felesleges futásidő. Jó, akkor legyen static inline. De rosszabb, mert oda kell lapozni, ahelyett, hogy egyből látnám, mi történik. Tehát ilyenkor nem segít.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
De rosszabb, mert oda kell lapozni, ahelyett, hogy egyből látnám, mi történik.
Ha jól van elnevezve, akkor tudod, hogy mit csinál, csak a részleteket nem tudod, hogy pontosan hogyan csinálja. Ha arra is kíváncsi vagy akkor egy gombnyomással oda tudsz ugrani, egy gombnyomással vissza, nem hiszem, hogy ez olyan nagyon problémás.
- A hozzászóláshoz be kell jelentkezni
Az értelmes elnevezés és ha nem elég akkor esetleg egysoros komment mellé csodákra képes.
- A hozzászóláshoz be kell jelentkezni
Azért nem másolom be, mert munka. Képzelj el egy hatalmas switch () case szerkezetet. Az állapotok egy enum konstansai, az állapotváltozó globális, mert a külvilág felé is hirdetni kell az állapotot, a külvilág mérés alatt nem nagyon ficereghet.
Minden case-en belül van egy újabb állapotgép, egy újabb switch () case szerkezet. Ennek már static uint8_t az állapotváltozója. A külvilágnak semmi köze hozzá. Azért van szétszedve több állapotra, mert természetesen egy multitask vezérlés egyetlen nem blokkolós task-járól beszélünk, így ha várni kell valamire - pl. ürüljön ki az USB buffer -, akkor visszatérünk, s nem lépünk a következő állapotra, ennélfogva legközelebb újra vizsgáljuk, hogy várnunk kell-e még. Persze van timeout, hibakezelés, logolás, toronyóra lánccal. Egy dolog nincs mögötte: operációs rendszer. Tehát van a csupasz fenekünk, a mikrokontroller RAM-ja, FLASH memóriája, a hardware, egy gcc és valamiféle glibc-szerűség. Ha valami nincs implementálva, akkor megírom én. Ilyen volt pl. az stpcpy().
#ifdef MY_STPCPY
char *stpcpy(char *dst, const char *src) {
if (src && dst) {
while (*src) *dst++ = *src++;
*dst = '\0';
}
return dst;
}
#endif
Viiszatérve a 14 oldalas függvényhez, az már magas szintű függvényeket hívogat, illetve faék egyszerűségű részeket tartalmaz, de így átlátható. Lényegében a külső switch () case enum konstans nevei azok, amelyek magyarázzák a funkciót.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
> Eddig úgy tudtam, hogy az, hogy kb. egy hordozható assembly.
Random off-topic:
- A hozzászóláshoz be kell jelentkezni
Nem állt szándékomban cinikusnak lenni. Lehet nem fejeztem ki magam elég ügyesen. Ilyesmire gondoltam: Nem elég egyértelmű ahhoz, hogy mások is könnyen, egyértelműen megértsék később.
Sajnos, amire rájön idővel az ember, az az, hogy a jó kód UNALMAS. De nem csak a kifejezésében, hanem a bonyolultságában is. Tehát a bonyolult dolgokból egyszerű dolgokat állítasz elő, és ez az ÉRTÉK.
Abból érdemes kiindulni, hogy egy átlagos kódot kb 10x annyiszor olvasnak mint írnak, vagy valami ilyesmi az arány. Bár, nem tudom, hogy mérték ezt le.
- A hozzászóláshoz be kell jelentkezni
Ez már-már igaz, de ha ezt túltoljuk, akkor egész egyszerűen a túl sok sor, a túl sok „zaj” felfogása, értelmezése teszi nehezen érthetővé az egészet. Ha például a *p++ = value; kifejezés helyett azt írod, *p = value; p++;, az egész egyszerűen értelemzavaró, nehezebben emészthető, pedig szét lett szedve.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Nem érdemes egyszerűen egy kis zárójelezéssel egyértelműbbé tenni?
(*p)++
Ami egyébként a legjobban zavar engem a * operátorban, az az, hogy nagyon más a jelentése, mint a két változós * operátornak, és akkor néha elkövet az ember olyan kódrészleteket (bocs, C++, C-t nem használtam már vagy 15 éve):
auto [minPosn, maxPosn] = minmax_element(v.begin(), v.end());
var x = (*minPosn) * (*maxPosn);
Ez [fixme] a második sorban lévő zárójelek nélkül ugyanazt csinálná, de sokkal zavaróbb lenne.
- A hozzászóláshoz be kell jelentkezni
Nem, mert a példában tényleg a pointert post incrementálom. :)
Ja, ilyen poénokat lehet csinálni:
x = a ** b;
Ami ugye valójában x = a * *b;
Valódi kódban a legelkanászodottabb, amit írtam, talán ez volt:
valtozo.tag = *--*p;
Meg van az, amikor a függvény a pointer címét kapja meg azért, hogy a pointert is tudja mozgatni:
x = *(*p)++;
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
OK, ha a pointert inkrementálod, akkor
*(p++)
A lényeg, hogy aminek nem teljesen megszokott a kiértékelési sorrendje, azt szerintem érdemes zárójelezni.
- A hozzászóláshoz be kell jelentkezni
Igen, de a *p++ annyira megszokott jelölés a C-ben, hogy egyszer elgondolkodsz azon, hogyan működik, mivé fordul, utána rááll a szemed, s már fáj látni azt a sok felesleges zárójelet.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
'Rövidzár kiértékelés' a dolog neve: ha az && baloldala nulla, akkor a jobboldalt nem is számoljuk ki, hasonlóan a || esetén: ha a baloldal nem nulla, akkor a jobboldalt nem számoljuk ki.
- A hozzászóláshoz be kell jelentkezni
Én például azon a néven ismerem, hogy "lusta kiértékelés" (lazy evaluation). És ha precízek akarunk lenni, akkor _logikai_ kifejezés esetén szépen balról jobbra haladva annyit (és csak annyit) számol ki, amenyi feltétlenül szükséges a kifejezés igaz vagy hamis értékének eldöntésére. Azaz && esetén ha baloldalt HAMIS, akkor nem vacakol a jobboldallal. || esetén pedig ha a baloldal IGAZ, akkor hagyja abba.
- A hozzászóláshoz be kell jelentkezni
Shellben ugyanez szintén jól jön:
$ mkdir /nincsilyen/semilyen 2>/dev/null || echo "nem jött létre"
$ mkdir /tmp/teszt_letrejon 2>/dev/null && echo "létrejött"
- A hozzászóláshoz be kell jelentkezni
Ez "shortcut evaluation" or "short-circuit evaluation" angolul. A "lazy evaluation" teljesen mas. Az jellemzoen funkcionalis nyelvek sajatja, es arrol szol, hogy pl ha egy valtozot egy kifejezessel inicializalsz, az nem ott rogton ertekelodik ki, hanem majd ha arra a valtozora szukseg lesz. (Ha egyaltalan.)
- A hozzászóláshoz be kell jelentkezni
Más dolgot is nevezhetnek "lazy evaluation"-nak, de ezt is. Google első:
The Free Pascal compiler compiler directive
{$boolEval}
determines, whether short-circuit evaluation (also known as “lazy evaluation”) of Boolean expressions is performed.
- A hozzászóláshoz be kell jelentkezni
Nekem nincs Pascal a talalatok kozott. Az elso ez:
- https://en.wikipedia.org/wiki/Lazy_evaluation
A rovidzar kiertekeles oldal pedig ez:
- https://en.wikipedia.org/wiki/Short-circuit_evaluation
Ahogy lathato, az utobbi szol pontosan a logikai kifejezesek kiertekeleserol.
Az elobbin egy helyen szerepel a "short-circuit" kiefejezes, ott is azt mutatja be, hogy azokban a nyelvekben, ahol van lusta kiertekeles, egyszeruen definialhatok a (rovidzar) logikai operatorok es az if utasitas, a szokasos szemantikaval.
Szoval a short-circuit-et lusta kiertekelesnek forditani hibas.
- A hozzászóláshoz be kell jelentkezni
Nem, lásd eggyel feljebb.
- A hozzászóláshoz be kell jelentkezni
De.
És láttam, mit írtál, meg is válaszoltam.
- A hozzászóláshoz be kell jelentkezni
Mielőtt megölnétek egymást, itt sok-sok nyelvre találhattok példát a short-circuit-re és szerepel benne a lazy kifejezés is.
Utána pontozásos alapon lehet tovább okoskodni. ;)
- A hozzászóláshoz be kell jelentkezni
2019-ben így használta valaki.
- A hozzászóláshoz be kell jelentkezni
En a definiciokrol beszelek. A belinkelt wiki-ben is a szokasos definiciok vannak:
Ezek mas fogalmak.
- A hozzászóláshoz be kell jelentkezni
- A hozzászóláshoz be kell jelentkezni
Igen, ez megvan, tudom, használom is. A gondom az volt, hogy nincs-e, nem lehet-e olyan szabály, hogy a műveleti sorrendben előrébb lévő dolgok kiszámításával kezdjük, mert az megváltoztathatja az operandust, s akkor a műveleti sorrendben hátrább lévőhöz jutva már megváltoztak a feltételek. De szerencsére nem így van.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Van még egy bűvös szó, a szekvenciapont: az && és a || is ilyen; ez köznapi szóval azt jelenti, hogy a baloldal kiértékelését (mellékhatással mindennel együtt) be kell fejezni, mielőtt a jobboldal kiértékelését elkezdenénk (lásd még az előző pontot: lehet, hogy a jobboldal kiértékelése teljes egészében kimarad). Ilyen például a vesszőoperátor is [de a függvényparaméterek elválasztó vesszőre ez nem vonatkozik], meg a függvényhívás, pl.:
y= f(x++), g(x++);
Itt a fordítóprogram be kell tartsa az alábbi sorrendet:
$xtmp=x
x++
f($xtmp)
$xtmp=x
x++
y=g($xtmp)
- A hozzászóláshoz be kell jelentkezni
Köszönöm az infót. Sohasem tanultam a C-t, csak amikor kellett, elkezdtem C-ben programozni, így ezek az egyébként fontos apró részletek teljesen kimaradtak. Azt sem tudtam például, hogy signed változó jobbra shiftelése arithmetikai shifteléssé fajul, s unsigned-dé kell cast-olnom, ha logikait szeretnék. Megszívtam egyszer, kidebugoltam, megtanultam. Azóta tudom.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Half-topic
Valamikor az őskorban, a freepascal, turbopascal 6.0 érában volt egy kapcsoló, aminek full-evaluation volt a neve. Pont arra szolgált, hogy az OR kifejezésekben mindkét oldalt kiértékelte még akkor is ha az első már false volt. Mert hátha az OR utáni rész egy olyan fgv, amelyet mindenképp meg kell hívni. Ez a 90-es évek vége volt, más gondolkodás. C és Cpp volt, ha találtunk 256byte ramot akkor örültünk.
- A hozzászóláshoz be kell jelentkezni
Vagyis két programozási nyelvet kaptál egy áráért. Máshol ezt úgy oldották meg, hogy van külön `and` meg `or` és `and then` meg `or else`, az utóbbi kettő a rövidzáras kiértékelésű.
- A hozzászóláshoz be kell jelentkezni
Bezzeg a mostani trend szerint már kerülik a Kernighan&Richie-féle fifikás, viszont rutin hiányában félreolvasható kifejezéseket.
Rust-ban például a pre-dekrementáló operátor hiányzik, ott csak így körülírva tudod megoldani: https://rust.godbolt.org/z/hT5xKaGYf
Bár a tömör íráshoz egy dec() függvénnyel ez is orvosolható: https://rust.godbolt.org/z/v7c3PdY3v
Viszont a legyártott assembly kód ettől nem lesz lassabb futású, ellenben a forráskód nem lesz a rutintalanoknak sem félreérthető.
- A hozzászóláshoz be kell jelentkezni
Ott is random true/false-re matchelés : https://github.com/rust-lang/rust/issues/93883 https://fasterthanli.me/articles/a-rust-match-made-in-hell
- A hozzászóláshoz be kell jelentkezni
Ez a hozzászólás picit tömör lett számomra. Bővebben kifejtenéd?
- A hozzászóláshoz be kell jelentkezni
Tetszik ez a megoldás.
- A hozzászóláshoz be kell jelentkezni
Az operátor precedencia szabályok arra vonatkoznak, hogy amikor nem egyértelmű, akkor hogyan kell fává alakítani a sorban leírt kifejezéseket, vagy másképpen hogyan kell kitenni a kispórolt zárójeleket. Pl:
a+b*c = a+(b*c)
A te eseted leegyszerűsítve arra ami a kérdés:
a && --b
Ezt csak így lehet zárójelezni, mivel a -- be van ékelve az && és a b közé: a && (--b)
Ha ezt írnád, akkor lehetne gondolkodni rajta: a&&b--, ez már lehetne kétféle: (a&&b)-- vagy a&&(b--), mivel a -- precedenciája nagyobb, ezért az utóbbi lesz. De ez pont hogy lehetővé teszi azt, hogy a b-- ne legyen kiértékelve, ha az a hamis.
Én egyébként nem vagyok híve a mellékhatásos kifejezések feltételbe írásának, én ezt így valósítanám meg:
if(ptr!=NULL)
{
if(*ptr>0)
{
(*ptr)--;
if(*ptr==0)
{
timeout();
}
}
}
Nem kedvelem az implicit pointer->boolean vagy integer->boolean konverziót sem, a mellékhatásos feltételt sem, és pláne nem, ha a "rövidzár kiértékelés"-en belül van mellékhatásos kifejezés. Persze sokkal több sor, de egyrészt minden sorra gondolkodás nélkül meg tudjuk mondani, hogy mit csinál, másrészt ha esetleg debuggolásra kerül sor, akkor soronként látjuk a lépéseket. Release módban fordítva a kódot pedig pontosan ugyanaz lesz az eredménye, mint az összezanzásított kifejezésnek, futásidőben mindegy.
Szerk.: és még a (*ptr)--; sorba is tettem egy zárójelet, hogy egyértelmű legyen, hogy nem a pointert csökkentem, hanem azt amire a pointer mutat.
- A hozzászóláshoz be kell jelentkezni
Ez a zárójel nem az egyértelműségmiatt kell, hanem azért, mert a *ptr--; valóban a pointert decrementálná, hiszen a post decrement magasabb precedenciájú.
Engem jobban zavar a részletes kiírás, áttekinthetetlenebb, gondolkodósabb. A tömör írásmód szerintem világosabb. Nem vagyunk egyformák. :)
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Nem, nem vagyunk egyformák. Nem egyszer fordult elő velem, hogy valamit debuggoltam, és akkor döbbentem rá, hogy ezt a sort szét kellene szedni 3-ra.
Sokszor elsőre én is tömör sorokat írok, aztán később alakítom át többre, vagy ha nincs vele probléma, akkor úgy marad.
Például Java-ban ha Null Pointer Exception-t kapsz, akkor a logba bekerül, hogy melyik fájl melyik sorából jött az exception. Igen ám, de ha két referencia feloldás is van abban a sorban, akkor nem lehetsz biztos benne, hogy melyik volt a hibás! Ezért referencia feloldásból is csak egy lehet egy sorban. Persze általában én is csak akkor írom így át, amikor először gond van az adott sorral...
- A hozzászóláshoz be kell jelentkezni
Igen ám, de ha két referencia feloldás is van abban a sorban, akkor nem lehetsz biztos benne, hogy melyik volt a hibás!
Ez már lassan két éve nincs így: https://openjdk.java.net/jeps/358
Megmondja neked, hogy minek a kiértékelése okozott NPE-t.
- A hozzászóláshoz be kell jelentkezni
\o/ Jó tudni, nekünk még nem jött el a 14-es JDK.
- A hozzászóláshoz be kell jelentkezni
Engem
Ez egészen addig érv, ameddig a kódhoz más nem nyúl. Pl. egy junior.
A tömör írásmód szerintem világosabb
Akkor miért született meg egyáltalán a topic, ha egyértelmű és nem kell rajta gondolkodni?
áttekinthetetlenebb
Ld. előző kommentben a debugolás. Van pl. ez a kód:
var p = ...;
var foo = new Foo(new Bar(p), new Baz(p), p);
Szép tömör, Foo-nak átadod mindkét paramétert. Majd kapsz egy ArgumentNullException-t, amiből annyi derül ki, hogy a p nevű paraméter hiányzik (tfh. mindhárom osztálynál ugyanaz a neve). Mondd meg kérlek, hogy most a Foo, a Bar vagy a Baz osztály ctora dobta.
var p = ...;
var bar = new Bar(p);
var baz = new Baz(p);
var foo = new Foo(bar, baz, p);
Ez lehet, hogy kevésbé tömör, több változó lesz benne, de már egy logban lévő call stackból egyértelműen látni fogod, hogy melyik dobta. Mert közel sem biztos, hogy mindegyik elvárja, hogy a p ne legyen null. De az is lehet, hogy valamelyik ArgumentOutOfRangeExceptiont-t fog rá dobni. Stb. Ezen kívül debugolni is egyszerűbb, hisz mire itt a negyedik sorig eljutsz ott lesz neked a local variable-kben a p-re a bar-ra meg a baz-ra egy egyszerűen használható referencia, amit bármely modern IDE-ben egy hoverrel megnézel kb.
- A hozzászóláshoz be kell jelentkezni
Azért, mert villamosmérnök vagyok, nem pedig programozó matematikus. Sohasem tanultam hivatalosan a C-t, valami rejtélyes oknál fogva Fortrant és Pascalt tanítottak, de ezeket sem alaposan. Majd nézegetve a kódomat, szöget ütött a fejembe, hogy a precedencia nem okozhat-e problémát. Tehát ez kötelezően viselkedik úgy, ahogy megszoktam, vagy csak a gcc kegyes hozzám, s egy másik fordítóval megbánhatnám-e ezt az írásmódot.
Például egy function(buff[idx++], buff[idx++]); már nyilván csinál valamit, csak ki tudja, mit. Simán lehet, hogy a második paraméter a buffer korábbi elemét kapja meg, meg az is lehet, hogy az első, szóval ilyet nem szabad csinálni. Ennek kapcsán méláztam ezen. De választ kaptam, rendben van a nyitóban írt forma.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Azért, mert villamosmérnök vagyok, nem pedig programozó matematikus.
Viszont szoftvert állítasz elő. Édesmindegy, hogy mikrokontroller vagy szuperszámítógép. És teljesen mindegy, hogy mit tanítanak meg "hivatalosan", egyetemen, OKJ-n rengeteg szoftverfejelsztéssel kapcsolatos dolgot nem tanítanak meg, amit kellene. (Amit tanítanak, annak a felét is helyből el kell felejteni, vagy legalább kontextusában helyére tenni.)
Majd nézegetve a kódomat, szöget ütött a fejembe
Igen, általában ez az amikor egy juniorból medior lesz. Senior szint meg az, amikor eljut az ember oda, hogy ha eleve nem írunk olyan kódot, amin gondolkodni kell, sideeffectes, shared adatok több szál között (tudom, mikrokontrollered van, blablabla) stb., akkor elő sem fordulhat egy csomó olyan probléma, ami ezzel igen. Egy sima inkrementálás/dekrementálás helyett lehetne ott egy komplexebb függvényhívás, amit lehet, hogy valaki egyszer át fog írni, stb.
- A hozzászóláshoz be kell jelentkezni
Közben módosítottál. A C++-t hagyjuk, mikrokontrollerre épeszű ember nem ír objektumorientált nyelven szerintem. Jellemzően assembly vagy C jöhet szóba. PC és efféle egzotikumok nem érdekelnek, ezekre programozzon az, akinek software-es a szakmája. :)
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Próbaképpen végigtoltam Rust-ban STM32-re egy céges projektet. Átadtam, működik. Nem reménytelen az irány, részemről belevágnék a következőbe is Rust-ban.
C viszont 8 biteseknél a legkisebb low endtől a 32 bites dual core mikrovezérlőig (például stm32h7) még mindig jó választás. Rust viszont a 8 bites low end mikrovezérlőkre nem igazán praktikus szerintem.
Viszont ne feledd, napjainkban gyakran mikrovezérlő helyett/mellett a beágyazott elektronikánál beáll a sorba a single board computer is. Itt általában Linux feletti programozásra lehet számítani. Egyúttal színesedik a programozási paletta. Több esetben találkoztam szkriptnyelvekben megírt főfeladattal is. SBC-k esetén mondjuk jobban szeretem a Rust nyelvet, C-hez hasonlóan a kevés erőforrásból sokat ki tudsz a lapkákból hozni.
- A hozzászóláshoz be kell jelentkezni
A C++-t hagyjuk
Ez igazából Java/C# volt, De lehetne C++ is. Vagy akár C is, C-ben is lehet (és szoktak) objektum orientált kódot írni. De látom nem értetted meg a lényeget: kapsz egy hibaüzenetet, ami mellé kapsz egy call stacket. Hogy fogod megmondani abból egyszerűen, hogy melyik része halt meg a kódnak, ha 3-4-5 különféle dolog történik ugyanabban a sorban?
PC és efféle egzotikumok nem érdekelnek, ezekre programozzon az, akinek software-es a szakmája. :)
Értem én, csak erre most hadd idézzek egy volt kollégámtól, aki szintén dolgozott már mikrokontrollereket is felvonultató környezetben, estefele írta nekem:
Lolcsemege topikot olvasom
MISRA C:2004- et javaslom neki, hogy amit a nyelv megenged arra szabad a pálya
A fosa olyan helyen ahol minőséget követelnek egyszerűen tiltott, automatizált static check írja ki a nevét a szégyenfalra
- A hozzászóláshoz be kell jelentkezni
Mármint a bevezetőben írt írásmódot tiltják egyes cégek minőségbiztosítási szabályai? Hála a Teremtőnek, még nem ment el mindenkinek teljesen az esze, ennélfogva a munkahelyemen
1) nincs dress code
2) nincs gendersemleges WC
3) nincs szabályba foglalva, hogyan szólíthatjuk meg a hópihéket, sárga pelyhes kiskacsákat, stb., illetve szerepelhet függvénynévben, commentben master, slave
4) nem mondják meg, mit írj a kódba, az sokal fontosabb, hogy működjön, az sem baj, ha Fortran emlékek miatt 'i'-vel kezded a ciklusszámlálók elnevezését
5) mégis van jó pár ISO szabványnak megfelelés, de ez a világ márcsak ilyen
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Hála a Teremtőnek, még nem ment el mindenkinek teljesen az esze
Értem, szóval minőségbiztosítási szabályokat felállítani, veszélyes, könnyen hibát okozható kódfordulatokat kerülni = mindenkinek teljesen elment az esze. Nincs több kérdésem. Főleg, hogy ezt párhuzamba állítod a dress codeal meg a gendersemleges wc féle, szakmához nem igazán kapcsolódó ökörséggel. (Gondolom kifogytál az érvekből, aztán jött a szalmabáb).
Mindegy, van ahol nem elégséges szint az, hogy "csak működjön", az is kell, hogy holnap is karban lehessen tartani és a kód valóban azt csinálja, amit szeretnénk tőle, mást ne.
Viszont egyvalamit elfelejtesz: attól, hogy más nézi a kódom code review alkalmával az nem jelenti azt, hogy én ne nézném ugyanúgy más kódját és "ne szólnék bele", hogy hogy csinálja. Tudod, csapatmunka. Megértem, hogy van, aki erre alkalmatlan, de az inkább menjen a rétre kecskét legeltetni.
- A hozzászóláshoz be kell jelentkezni
könnyen hibát okozható kódfordulatokat kerülni = mindenkinek teljesen elment az esze
Ezzel ugyanaz a baj, mint a liberalizmussal. Vannak valakik, akik megmondják, mi a jó, s ha megfeszülsz, sem tudod elmondani nekik, hogy te másként gondolod, teszem azt, ahogy írtam is, számomra könnyebben olvasható az általam bevezetőben írt kód, mint a terjengős, sok soros trágya, amibe belefáradok, a fókuszt is elveszítem, mire elolvasom.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Eléggé logikai bukfenc liberalizmussal példálózni, miután te akarod itt individuálisan felülírni egyéni érdek mentén azt, amire a szakma már rájött, hogy antipattern. Mert NEKED jobban olvasható. Képzeld, van olyan, ami nekem is jobban olvasható, aztán mégsem úgy írom, mert felfogom, hogy mi vele a baj. (Egyébként szerintem te a liberál-fasizmusra gondoltál, de mindegy.)
De hadd kérdezzek valamit: ha vezetékeket, kábeleket kell kötögetni, figyelembe veszed a szakma színkódolásait vagy "nekem így tetszik" alapon tolod azt is? UTP kábeleket is saját, neked kényelmes színsorrendben krimpeled?
- A hozzászóláshoz be kell jelentkezni
a szakma már rájött
Erről beszélek. A woke kultúra is rájött, hogy pusztítsunk el minden értéket, ami eddig volt.
Ha valamit szabvány ír elő, akkor igazodom, de ha én tervezem a gép elektronikáját - és ez a helyzet -, akkor élek a tervezői szabadságommal.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Különbség annyi, hogy a woke mögött nem szakmai érvek állnak, míg ezek mögött igen.
De további kellemes szakmábozást.
- A hozzászóláshoz be kell jelentkezni
Épp ezt mondom, hogy vannak, akik azt mondják, ők a szakma, ők rájöttek, s amit ők mondanak, az úgy jó. Aki ettől eltér, az mind marginalizálandó hülye. Ugyanaz, mint amikor megmondják, mit kell gondolni a történelemről, mert van hivatalos álláspont, másként ne merészeld gondolni a megtörténteket, mert az tabu.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Megmondják ideológiai alapon vs érvek mentén eljutottak oda, hogy az egyiket szignifikánsan többször lehet elbaszni, mint a másikat.
Hülye szalmabábok helyett vissza tudunk térni oda, hogy szakmailag mi az indok a side effectes kód írására azon túl, hogy neked olvashatóbb? Elárulom, szerintem is van olyan kód, ami szerintem olvashatóbb máshogy, mégse úgy írom, mert tudom, hogy mi vele a baj.
- A hozzászóláshoz be kell jelentkezni
Abban az egy sorban mi a mellékhatás? Minden hatása szándékos, így az főhatás, nem mellék.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Az, hogy adatot módosítasz egy feltétel-vizsgalat során. A feltétel-vizsgalatodnak van mellékhatása konkrétan.
- A hozzászóláshoz be kell jelentkezni
Viszont ez szándékos. Éppen ezért érzem túlzásnak a mellékhatás kifejezést. A mellékhatás pejoratív, úgy hangzik, mint valami hiba, amelyre nem számítottunk. Abban a sorban az lenne a hiba, ha a feltételben ez az adatmódosítás nem következne be.
Egyébként a feltétel vizsgálata a kiértékelt kifejezésre vonatkozik, a kiértékelés pedig tartalmazhat adatmódosítást.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Ha szándékos, legyen egyértelműen leírva, ne rejtsd bele egy feltétel-vizsgálatba. Tudod, a férfi legyen férfi a nő legyen nő és ne transzgenderkedj, ha már ennyire agyadra ment a téma.
- A hozzászóláshoz be kell jelentkezni
Már hogy a viharba ne lenne genderspecifikus!
i=0;
f(++i;++i;++i);
Itt az átadott értékek lehetnek (1,2,3), (3,3,3), (3,2,1)!!, sőt több...
Ennek az az oka, hogy a cpu lehet big/low/pdp endian, a stack frame állhat lefelé és felfelé, valamint a C fordító is viselkedhet akárhogy.
Azok, akik képesek pártot alapítani arra, hogy a tojást melyik végén törik fel és ezen elvek szerint képesek akár vérre menő küzdelmet folytatni, az már eléri a genderspecifikusság fogalmát. (Swift idején még nem volt PDP. :-D) Ugyanerre létezik Molnár Ferenc óta egy gyönyörű magyar kifejezés: gittegylet.
A gittegylet/endianess/gender tilos abban az esetben, ha C-ben egyértelmű kódot szeretnél írni. Ha valaki ezt nem érti, akkor csatlakozik valamelyik párthoz. Az ilyeneket pedig nem lehet saját hülyeségükről meggyőzni. :(
- A hozzászóláshoz be kell jelentkezni
Igen , pejoratív, éppen azért hangzik úgy, mint egy hiba, mert az egy hiba.
A feltétel vizsgálatnak a hatása a feltétel kiértékelésének eredménye kell legyen és semmi másnak. Itt ellenben még érték változtatások is történnek, ami mellékhatás.
- A hozzászóláshoz be kell jelentkezni
Mondom, hogy szándékos. Szoktál te assemblyben programozni? Ott igazán szép, fantáziadús megoldásokat tud csinálni az ember. C-ben legalább van type cast, assembly-ben viszont észrevétlenül kezdheted ugyanazt az adatot más értelemben, más típusként használni, vagy tényleg bármi.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
A C-t régen struktúrált assemblernek hívták. Mindazt, amit magyarázni próbálsz, ugyanúgy megteheted C-ben is. Csak egy kicsit tanulni kell hozzá. ;)
Barátod először az union és az align packed. Utána jöhet a bit és bitfield definíció.Ezek után nem kell mindenféle zárójeles castolásokkal foglalkozni, viszont (részben) hordozható lesz a kód. Ha nincs eszközkészlet, akkor még mindig szúrhatsz be asm utasításokat - mint pl. a nagyobb processzoroknál az altivec és a cache direkt kezelése.
Sőt, a telefonszámokat unsigned int30+2 jogosultsági bit struktúrában tároltam, egyéb biteket meg 2 bit x soksok elemű tömbben. Ehhez assemblerben már programozni is kell. ;) De semeddig sem tart a 4 karakteres ASCIIZ stringben tárolt irányítószámokat int32-ként összehasonlítani.
A C és assembler között csak annyi a külöbség, hogy az előbbi esetben meg kell mondanod mit csinálsz. Ennek köszönhetően valamivel jobb minőségű ellenőrzést ad a fordító.
A side effect használatáról az én történetem:
- Programoztam 19 évig POWER-en IBM C-ben. Ott a cpu big endian, a stack frame "egyenes". A deklarált side effect ellenére a fordító+cpu mindent pontosan olyan sorrendben hajtott végre, amilyen sorrendben leírtam. Mottó: Nem akarom én sehova hordozni a kódot! :-D
- Majd 15 évvel ezelőtt az Intel/linux platformon ugyanezek a side effectek már rosszul fordultak, mert a cpu little endian és a stack frame "fejjel lefelé áll" és mas a fordító.
- Manapság már harmadik féle módon fordul hibásan ugyanerre a platformra.
Eldöntheted, hogy ugyanezt az utat szeretnéd bejárni, vagy programozol normálisan.
De bármilyen fondorlatosan kérdezed, a side effectet tartalmazó kód nem egyértelmű, tehát kerülendő. Az ilyenek a jó dokumentációban egyértelműen le vannak írva. Ha ennek ellenére teszeled, jól működik, akkor használd, de ne várd el, hogy dícsérjek is. :-D
- A hozzászóláshoz be kell jelentkezni
Szerintem tárgyilagos. A C-ben nincs szubrutin, csak függvény, a függvény pedig, ha precízen értelmezzük, veszi a változóit, abból kiszámol egy eredményt, és azt visszaadja. Ez a hatás, és ha valami más is történik (eltér a C függvény a matematikai értelemben vett függvénytől), az mellékhatás. Matematikailag az operátor is csak jelölés egy függvényre ( tehát x-- értéke x, és a mellékhatása az, hogy x-et csökkentjük). Ez nem jelenti azt, hogy az x---ban nekünk esetleg nem fontosabb és hasznosabb a mellékhatás, mint a függvényérték. Ugyanez van a printf()-el, általában senkit nem érdekel, hogy mit ad vissza, az a fontos, hogy kiírja a kiírnivalót.
Ez mind nem igaz az olyan nyelvekre, amiben van szubrutin és van függvény (pl. a Fortran). A szubrutinnak az a dolga, hogy veszi az argumentumait, csinál valamit azokkal, majd visszatér. C-ben ilyen nincs, van helyette a void függvény. És ez nyilván semmi más, mint terminológia.
Van olyan nyelv, amiben nincs szubrutin, és mellékhatás sem. Egy csomó hiba elkövetését megnehezíti, de az I/O implementálását is.
- A hozzászóláshoz be kell jelentkezni
Értem, de én ezt a világ romlásaként, a szabadság beszűküléseként élem meg. Lehessen egy programban bármit csinálni. Én dolgom, én felelősségem.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Akkor a következő lecke: pragma isolated_call. Sajnos a gcc-ben sokkal rondábban írják, de azért nézz utána!
Mire mindent IS megtanulsz, szebben és jobban fogsz programozni!
- A hozzászóláshoz be kell jelentkezni
Az miért jó nekem, ha nem érhetem el a globális változóimat meg a volatile-okat?
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Mert nehezebb egy csomó mindent garantálni a globális változókkal. Nehezebb garantálni, hogy más nem nyúl hozzá, nehezebb tesztelni, stb. És akkor még el sem jutottunk a multithreadingig meg a multicore-ig.
- A hozzászóláshoz be kell jelentkezni
Globális változó hátrányai:
- átláthatatlan az "életútja". Függvényben pattogtatva következőbb, hogy mikor ki és mit akar a változóval/struktúrával.
- multithread, multicore ... jöhet a "mit ír ki" című klasszikus programozó vizsgafeladat
Globális változó két előnye:
- legkisebb 8 bites architektúráknál, amikor azért a nyeszlett 1..2 órajelért küzdesz, fixen allokált memóriacímre direktben tudja az adatelérést fordítani a fordító, ezáltal kimarad egy indirekciós lépés.
- valós rész és interrupt közötti adatátadás ... de óvatosan. Amikor valós részből piszkálod, tiltsd le az IRQ-t arra a néhány utasításnyi időre, ellenkező esetben képbe jön a side effect.
Rust esetén globális írható változó unsafe nyelvi kiterjesztésben használható, értelemszerűen a nyelv erre semmiféle biztonsági garanciát nem ad.
Viszont mikrovezérlőre (stm32) is használtam már Mutex-et a főprogram és IRQ közötti adatcserénél. Tapasztalat vele: korrekt, kétirányú szemafor implementáció. Viszont mivel 100 000 IRQ/másodperces tempóval dolgoztam, ott már kimutatható volt, hogy néhány órajellel lassabb egy sima IRQ tiltásos, egyirányú lock módszernél. Viszont vannak olyan esetek, ahova tuti a Mutex-et építeném be, mert kell a kétirányú lock. Mikrovezérlőre egy Rust Mutex példát itt láthatsz: https://docs.rust-embedded.org/book/concurrency/
- A hozzászóláshoz be kell jelentkezni
Nem kell IT tiltás, ha atomikus a műveleted.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Erről egy vicc jut eszembe.
Egy felhőkarcoló legfelső emeletén áll egy tábla: Emberek! Ne dohányozzatok! Ne felejtsétek az 1906-os nagy San Franciscó-i tűzvészt! - Valaki melléirta: Emberek! Ne köpködjetek! Ne feledjétek a Mississippi 1927-es nagy áradását!
Ennyi hasznos tanács láttán bennem is felmerül: Emberek! Ne programozzatok! Bízzátok inkább olyanra, aki átlátja a feladatot és az azt megvalósító rendszert!
- A hozzászóláshoz be kell jelentkezni
Azért globális változó, hogy más is hozzáférjen. :) Ha azt akarom, hogy ne látszódjék kívülről, akkor lokális. Ha csinálok egy volatile változót, amelyet megszakítás rutinnak is el kell érnie, meg alapszintről is, akkor ez is szándékos, nem tréfából hoztam így létre. Hasonlóképpen nem optimalizálhatja ki a fordító, ha hardware regisztert írok, amelyet látszólag sehol sem használok később. Az a hardware-t vezérli.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Pont más a szerepe. Legalábbis az IBM C-ben letiltotta azt a fícsört, hogy "úgy is hülyén programozol, ezért minden lokális változót IS lementek, hátha elrontod". Tehát megadhatod, hogy a függvényed side-effect mentes. Avagy nem is az, de direkt csinálod. :-D
Ezzel tudatosan lehet módosítani a lokális és globális változókat is, de overhead nélkül. Ha az egyszerűség miatt külön függvényben írtam le egy inline-olt tevékenységet, akkor potosan és csak az a kód fordult be, amit akartam.
Igaz, azt is írták, hogy a rengeteg regiszteres processzornál ne használd a register direktívát. ;) Mégis sikerült olyan helyen alkalmaznom, ahol lényegesen gyorsított.
- A hozzászóláshoz be kell jelentkezni
Igaz, azt is írták, hogy a rengeteg regiszteres processzornál ne használd a register direktívát. ;) Mégis sikerült olyan helyen alkalmaznom, ahol lényegesen gyorsított.
Persze. Többnyire egy-egy feature nem véletlenül van egy nyelvben. Lehet is őket használni. Csak a probléma ott kezdődik amikor a "néha jó, de egyébként inkább ne csináld" dolog válik az általánossá.
- A hozzászóláshoz be kell jelentkezni
Ide vonatkozó kedvencem a gets() kriminalizálása. :)
Hasonló a shellben a var=NULL => var=0, 50 éve használatos fícsör nagy büdös lila warningokkal ellátása. Az alkotó nyilvánvalóan az egyik kezét a seggébe dugta, miközben a másikkal kódolt és azon elmélkedett: Mekkora szabványos zseni vagyok én! :-D
- A hozzászóláshoz be kell jelentkezni
(Ez annyira frappánsra sikerült, hogy egy szót sem értek belőle. Ezt nem panaszként mondom.)
- A hozzászóláshoz be kell jelentkezni
char *gets(char *s);
Sok évtizedig frankó volt, mert faék egyszerű és működött, működik a gets().
Ellenben ma már magától értetődő az a kérdés, hogy mekkora változóméretet foglalj, hogy a bemenettel ne tudj a változón kívüli memóriára írni? Erre pedig gets() esetén nincs válasz, látszik hogy nem tehető biztonságossá.
- A hozzászóláshoz be kell jelentkezni
Eddig jó és igaz, a gets
"broken by design".
- A hozzászóláshoz be kell jelentkezni
Jó, de igazából szakmai érvet nem tudsz felhozni azon túl, hogy "a nyelv engedi".
Pl. a C# is engedi a goto-t, mert néha hasznos (nagyjából életemben 2x kellett), ettől még a közmegegyezés mégiscsak az, hogy inkább ne.
- A hozzászóláshoz be kell jelentkezni
C-ben én is alig használok goto-t, de hibával kiszálláshoz jól jött már.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Azért éled meg így, mert a gyógyszerek esetén megszoktad, hogy a mellékhatás mindig rossz. Kivéve, amikor nem, pl. az eredetileg lázcsillapításra szánt aszpirin vérhígító hatása. Attól még, hogy a nyelv tervezői úgy döntöttek, hogy függvénynek nevezik ezeket az alprogramokat, és azoknak a fő hatása az eredmény visszaadása, te még használhatod a mellékhatásukért. Nincs ezzel semmi baj, csak tudd, hogy valami olyat csinálsz, amit a nyelv tervezői nem a "szabálynak" szántak, hanem a "kivételnek". A C-ben ezek használata nincs megtiltva, csak sokak tapasztalata szerint nem érdemes használni, hacsak nincs rá valami jó okod. (Ha úgy gondolták volna, hogy soha nem szabad használni, akkor kivették volna az újabb szabványból. Még akkor is maradhatnál a régi változatnál (-std=c89), vagy használhatnál olyan compilert, ami megengedi ennek ellenére (Fortran-ban van egy-két deleted feature, és egy csomó obsolescent, az előbbire hibát adhat, az utóbbira warningot kell adnia a compilernek, de attól még szinte minden compilernek van olyan opciója, hogy továbbra is fogadja el.))
Amúgy a protected mode is a szabadság beszűkülése, ill. a világ romlása? Vagy a szigetelt vezeték?
- A hozzászóláshoz be kell jelentkezni
Azért éled meg így, mert a gyógyszerek esetén megszoktad, hogy a mellékhatás mindig rossz.
Épp írod is, hogy nem mindig ;-) A mellékhatások is hatások, csak éppen olyanok, hogy nem mindenkinél, nem minden esetben jönnek elő. Épp ez a rossz bennük, mert nem célszerű a mellékhatások miatt használni, mert ki tudja, hogy jön-e vagy sem. Az aszpirin vérhígító hatása pont rossz példa, mert az rendes hatás, az mindenkinél jelentkezik, pl. aszpirin protect (kis dózisú aszpirin).
Épp ezért sokkal jobb olyan eszközöket használni, aminek nincs mellékhatása és csak kevés számú hatása van, lehetőleg csak 1 (pl. vitaminok, ásványi anyagok, immutable adatok, pure függvények, ...). ;-)
- A hozzászóláshoz be kell jelentkezni
A mellékhatások is hatások, csak éppen olyanok, hogy nem mindenkinél, nem minden esetben jönnek elő.
A gyógyszereknél a hatás és a mellékhatás is sokszor valószínűségi, van, akinél a gyógyszer hat, van, akinél nem. Nyilván annál "jobb" egy gyógyszer, minél inkább biztos, hogy hat. A különbség szerintem a hatás és a mellékhatás között az, hogy az egyik az, amire eredetileg szánták, a mellékhatás pedig az, ami még mellesleg történik. Az is igaz, hogy ha valaminek csak hatása van és mellékhatása nincs, akkor könnyebb vele tervezni, célozni. De lehet olyan helyzet, amikor egyáltalán nem baj egy mellékhatás (ha tudod, hogy arra is szükség van, és arra használod, hogy két legyet üss egy csapásra, pl. ha egy tömb elemein kell végigmenni egy ciklussal, mindaddig, míg teljesül rájuk egy feltétel, akkor hasznos lehet egy for(i=0; i < arraysize, feltetel(array[++i])) { ... ).
A mondatot tényleg rosszul írtam, azt kellett volna írnom, hogy "mert a gyógyszerek esetén megszoktad, hogy a mellékhatások, amikről sokat beszélnek, mindig rosszak."
- A hozzászóláshoz be kell jelentkezni
> Akkor miért született meg egyáltalán a topic, ha egyértelmű és nem kell rajta gondolkodni?
Mert kíváncsi volt, van-e garancia arra, hogy a logikai-és(&&) baloldala teljes egészében kiértékelődik, mielőtt a jobboldal kiértékelése elkezdődik. Szerencsére van.
Az igaz, hogy Nyomosek Bobó compiler-íróként sokszor úgy optimalizál, hogy abban nincs sok köszönet, például, ha Bobó meglát egy ilyen programrészt:
unsigned n= strnlen(NULL, 0);
akkor boldogan felkiált: NULL-pointer! UB-optimization! és szándékosan rossz kódot generál.
Vagy ha én használat után memset
-tel felülírnám a privátkulcsomat a memóriában, ő kioptimalizálja a memset
-et, hiszen már úgysem használom azt a memóriaterületet.
Más: a lokális változók ilyen nemhasználatát Java-kódolóknál szoktam néha látni, szerintem ez valami olyan babona, hogy ha sok a lokális változó, akkor lassabb lesz a GC gráfbejáró algoritmusa.
- A hozzászóláshoz be kell jelentkezni
A memset-es történetedre a volatile nem megoldás?
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Jó kérdés, nem tudom. Mindenesetre lett egy explicit_memset. Mármint egyes platformokon. Mondjuk ez annyiban jó lehet, hogy [remélhetőleg] tartalmaz memory-barrier-t, vagyis nem csak az aktuális thread aktuális CPU-jának gyorsítótárában lesz nullázva a terület, hanem a RAM-ban is.
- A hozzászóláshoz be kell jelentkezni
Azert ez massziv csusztatas!
1, ki rakja uj sorba a { jelet?
2, Minek egyaltalan { ?
3. nem kell minden kifejezest az if-ben kulonszedni.
Amire gondolsz, azt normalis ember igy irna:
if (ptr && *ptr) {
(*ptr)--;
if(*ptr == 0) timeout();
}
- A hozzászóláshoz be kell jelentkezni
1. Jelen. De tény, hogy megfigyelhető eltérés nyelvenként
2. Jobban vezeti a szemet, egyértelműbb, hogy melyik utasítás hol van. Láttunk már olyan bugot, hogy valaki leírt egy if (...) a(); b(); -t, és if (...) { a(); b(); }-t akart írni.
3. Amit írtál példakódot, az szerintem is korrekt. Bár én kiírnám, hogy if (NULL == ptr && 0 < *ptr), egyértelműsítve, hogy mi a cél. (Meg a timeoutot új sorba, zárójelezve).
- A hozzászóláshoz be kell jelentkezni
Nézzük másik oldalról!
Pl. a function(x,--x) bizony nem a -- operátor precedenciájáról szól, hanem side effectnek hívják. Általában a C fordítók tiltják az ilyen megfogalmazást.
Szerintem ilyen agyalás helyett azt kéne írni a programba, amit szeretnél csinálni! Igaz, akkor nem lehetne beszélgetni róla. ;)
Gondolom, azért jutnak ilyenek az eszedbe, mert assemblerisztikusan gondolkozol. :-D
Ha az alábbi módon írod le, akkor sokkal olvashatóbb és nincsenek kérdések. És hidd el, egy mai C fordító ugyanazt a kódot fogja előállítan!
if ( ptr != NULL )
if ( *ptr > 0 )
{
*ptr--;
if ( *ptr == 0 )
timeout();
}
- A hozzászóláshoz be kell jelentkezni
A function(x, --x)
teljesen valid, mindössze az a gond vele, hogy a függvényparaméterek kiértékelésének sorrendjét nem rögzíti szabvány, platformfüggő. Az is teljesen valid, ha ugyanaz a fordító hol így, hol úgy csinálja.
- A hozzászóláshoz be kell jelentkezni
Tehát ez az a kétméteres bot, amelynek nem ismerjük a méretét, de bármekkora, akár több féle méretű is lehet. Az a lényeg, hogy valid.
Ez az állapot a Freud szerint a skizofrénia. :-D
Ha a "valid" azt jelenti, hogy a fordító nem ad hibaüzenetet, akkor igazad van.
Ha olvastál már C fordító manuált, ... na abban kiemelik, hogy nem szabad ilyet csinálni.
Bár a fordítók is változhattak, mióta utoljáta olvastam...
- A hozzászóláshoz be kell jelentkezni
Kihívás: írjunk olyan programot, ami mennél több undefined behaviour-t használ, de mégis helyesen működik!
- A hozzászóláshoz be kell jelentkezni
Van egy népmozgalom a fordítóprogram-készítők között 'UB optimization' néven, aminek a lényege az, hogy ha a compiler meglát egy ilyen 'undefined behaviour'-t, akkor jogosultnak érzi magát, hogy szándékosan hibás kódot generáljon. Például a memcmp(NULL, NULL, 0)
az egyesek szerint UB, tehát például konstans 77-et lehet behelyettesíteni visszatérési értéknek.
Pl.: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=30475
- A hozzászóláshoz be kell jelentkezni
Ebben mi a hiba? Nem definiált a viselkedés, akár generálhatna olyan kódot is, ami éppen kilépteti a programot. Ha UB-t használsz (ami nem azonos a platformfüggő dolgokkal, például az int méretével), akkor ne számíts semmilyen értelmes viselkedésre a programban utána. Ez pontosan így van jól, normális programban nincs UB kód. Amiben meg van, ott nem szabad meglepődni semmin.
- A hozzászóláshoz be kell jelentkezni
memcmp(NULL, NULL, 0)
A nulla hosszt kellett volna először ellenőrizni, és konstans nullával ('egyenlőek') helyettesíteni az egészet.
A másik meg az, hogy a nulla, mint memóriacím, egyes platformokon (Aix, Bs2000), spec. esetekben lehet valós, használható cím. Tehát eleve nem "UB"-nek kellett volna besorolni, hanem "platformfüggő"-nek. (Természetesen ahol az van a leírásban [például egy láncolt listánál], hogy a NULL jelentése 'nincs' vagy 'nem használt' ott azt nem gondolom felkavarni, de olyan alacsonyszintű műveleteknél, mint a mem??? igen.)
- A hozzászóláshoz be kell jelentkezni
A nulla hosszt kellett volna először ellenőrizni, és konstans nullával ('egyenlőek') helyettesíteni az egészet.
Nem. Egész egyszerűen azért nem, mert nem ezt írja elő a szabvány. Ha a memcpy első két argumentuma átlapolódó területre mutat, akkor az UB. Ilyenkor akár olyan kódot is fordíthat a fordító, amelyik leállítja a processzt. Megteheti, mert megengedted neki. Függetlenül attól, hogy mi a harmadik argumentum értéke, azt figyelembe se kell venni, hogy UB-t kapjunk.
Akár fordíthat végtelen ciklust is, meg van neki engedve.
- A hozzászóláshoz be kell jelentkezni
> Egész egyszerűen azért nem, mert nem ezt írja elő a szabvány.
Persze, igen, ez a végső kérdés: hogy miért nem olyanok csinálják a szabványt, akik képesek és hajlandók rendesen átgondolni a dolgokat?
Off: érdekes lenne egy lista az ilyenek erről. Sőt, egyszer már el is kezdtem. (Kettő a durvábbak közül: miért nincs benne a timegm(3) a standardban? miért nincs visszatérési értéke a syslog(3) függvénynek? (Még be kellene vennem a PHP-ból a htmlspecialchars-t, amit valamikor a fejlesztés során kiegészítettek egy 'és utf8-validság ellenőrzés' funkcióval, de úgy, hogy hibaüzenet sincs, csak üres string a válasz.))
Kieg: egyszerű feladat azoknak, akik szeretnék érteni a felületes gondolkodás és az alapos gondolkodás közötti különbséget: a magyar nyelvben melyik évszak nevében van csupa azonos magánhangzó?
- A hozzászóláshoz be kell jelentkezni
Kieg: egyszerű feladat azoknak, akik szeretnék érteni a felületes gondolkodás és az alapos gondolkodás közötti különbséget: a magyar nyelvben melyik évszak nevében van csupa azonos magánhangzó?
Kérdésed sajnos aluldefiniált. Először is, definiálni kéne, hogy melyek a magyar nyelvben az évszaknevek. Ugyanis a kontinentális éghajlat évszakait így hívják: "tavasz", "nyár", "ősz", "tél". A trópusi és szubtrópusi éghajlat évszakait így hívjuk magyarul: "esős", "száraz". Más éghajlaton "meleg", "nedves" és "hideg" évszakok vannak. Ha az ókori Egyiptom évszakairól beszélünk magyarul, akkor az "áradás", "vetés", "aratás" évszakneveket használjuk. Ha azt szeretnéd mondani, hogy a "tavasz", "nyár", "ősz", "tél" legyen évszaknév, akkor fogalmazz pontosabban.
Más kérdés a "csupa azonos" kifejezés jelentése, ezt a magyar nyelv nem definiálja pontosan. Érthetjük alatta azt, hogy "mind megegyeznek" (ebbe belefér, hogy valamiből egy vagy nulla darab van), és azt is, hogy "legalább 2 darab, amelyek mind megegyeznek". Embere válogatja, hogy ki hogyan értelmezi, a magyar nyelv aluldefiniált ezen a téren. Emberi nyelven megfogalmazott kérdésre sok esetben nincs pontos válasz, ha azt géppel akarod elvégeztetni, akkor pontosan definiálni kell, hogy mit értesz alatta.
hogy miért nem olyanok csinálják a szabványt, akik képesek és hajlandók rendesen átgondolni a dolgokat?
Hidd el, ők alaposan átgondolják a dolgokat. A memcpy definíciója teljesen jó, és pont azért van a nyelvben sok UB, hogy rengeteg architektúrára hordozható legyen hatékonyan. A lényeg, hogy azokban az esetekben, amikor nem UB és nem platformfüggő a viselkedés, teljesen jól van definiálva, hogy hogyan működik, a kód hordozható. Aki pedig mást használ, az magára vessen, ha az általa elképzelt viselkedés, és a fordító/futtatókörnyezet által implementált viselkedés eltér egymástól.
A te esetedben például a memcpy(NULL, NULL, 0)-nak miért kéne 0-t visszaadnia? 0-t nem tudhat visszaadni, mert ha visszad valamit, az az első paramétere kell legyen, ami esetünkben NULL, ami nem egyezik meg a 0-val, hiszen a NULL-nak más a típusa, mint a 0-nak, nem lehet azonos a kettő. Az egy dolog, hogy a 0 void*-ra cast-olva ekvivalens kell legyen a NULL-lal, mindkettő az adott platform null pointer konstansa. De ettől még a NULL nem ugyanaz, mint a 0, mivel más típusúak.
- A hozzászóláshoz be kell jelentkezni
@persicsb kérlek nézd át még egyszer a postodat: nem `memcpy` hanem `memcmp` volt a kérdés.
- A hozzászóláshoz be kell jelentkezni
ugy hivtak ezt hogy ms-dos meg win31 :)
- A hozzászóláshoz be kell jelentkezni
Ebben az a gyönyörű, hogy ha az x esetleg egy pointer, akkor még a variable aliasing-et is meg lehet valósítani vele (ez gyakoribb probléma olyan nyelvekben, amik nem érték szerint adnak át, pl. Fortran-ban). Ha írsz egy szép nagy függvény,
function(whatever *x, whatever *y){
...
...(*x)
...
*y = ....
}
Akkor lehet, hogy az optimalizáláskor a fordító a *x-et felhasználó dolog elé veszi a *y-nak értékadót. Nem tudom, hogy a C szabvány szerint sír-e amiatt, ha egy ilyen függvényt meghívsz function(&x, &x) módon, de Fortran-kódot láttam már, ami emiatt adott más eredményt különböző optimalizálási opciókkal fordítva. Egy, a hetvenes évek óta sokak által használt matematikai szubrutingyűjtemény tesztjében volt a bug. Vagy valami viszonylag új gcc volt az első compiler, ami ennyire átrendezte a kódot, vagy mindenki bízott a kódban és a compilerében 50 éve, és nem futtatta a tesztet.
- A hozzászóláshoz be kell jelentkezni
Erre van egy eszköz, a restrict, amit a függvény pointer típusú paramétereinél adhatunk meg módosítóként, és olyasmit jelent, hogy a 'hívó felelőssége, hogy az így megjelelölt paraméterek mind különböző objektumokra mutassanak, a compilernek nem kell ezen aggódnia'. Például a 'memcpy' esetén esetleg mondhatjuk ezt, de a 'memmove' esetén nem, annak működnie kell átfedő tartományok esetén is.
- A hozzászóláshoz be kell jelentkezni
Szerintem ez sokkal áttekinthetetlenebb, mint amit a nyitóban írtam.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
És még egy hiba is van benne. :(
Talán a sok && helyett próbáld meg így olvasni:
if timer defined
if timer active
decrement timer
if timer inactive
do timeout
Függőlegesen olvasva egyértelmű, hogy mikor mi történik.
Ezt a folyamatábrát tetszőleges nyelvre át lehet ültetni, és ugyanarra fordul, mint amin okoskodni kell.
- A hozzászóláshoz be kell jelentkezni
Igen, a pointert csökkentetted, nem az általa mutatott értéket.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Látom, elcsípted a lényeget. ;)
Történetesen az utóbbi időben főként assemblerben programoztam. Ennek ellenére egyes algoritmusokat awk-ban vagy C-ben írtam és teszteletem. Utána csak be kell emelni az awk vagy C kódot a programba és azonnal át lehet emelni assembly/assemblerbe.
if ( ptr )
if ( *ptr ) {
--*ptr
if ( *ptr==0 )
timeout(); }
( FSR0==ptr)
movf FSR0, w ; if ( ptr )
iorwf FSSR0+1, w
bz NoTimer
movf INDF0, f ; if ( *ptr )
bz NoTimer
decfsz INDF0, f ; if ( --*ptr )
bra NoTimeout
call Timeout ; timeout()
NoTimer
NoTimeout
Kis processzorra nem engedem meg a ptr pazarlást, hanem inkább inline (macro) oldom meg.
if ( timer )
if ( --timer==0 )
timeout();
( movlb timer)
movf timer, f ; if ( timer )
bz NoTimer
decfsz timer, f ; if ( --timer )
bra NoTimeout
call Timeout ; timeout()
NoTimer
NoTimeout
Ez utóbbinak további előnye, hogy nem csak az FSRx, hanem a W regiszter használatát is megspórolod. Meg gyorsabb is a kód. Több timer esetén macro, mert elfér és a futásidő is rövidebb.
Ha ez szerinted áttekinthetetlen, akkor nem tudod mire fordul a kód. ;)
Megjegyzem, amikor még fiatal voltam, az öregek kifejezetten utálták az ilyen flag/pointer változó megoldást. Mint a timer==0 ->inaktív, timer>0 ->számláló. A ptr==NULL helyett meg egy flag lenne a helyes, pedig én is pont így használom.
- A hozzászóláshoz be kell jelentkezni
Viszont gondolkodás nélkül olvasható, oda lehet adni egy juniornak is a kódot, az is érteni fogja. És leginkább: nem kutyul össze műveletet feltétel vizsgálattal. Jobb helyeken ez már helyből red flag egy code reviewen, ha egy feltételvizsgálatban adatot módosító művelet vagy függvényhívás van.
- A hozzászóláshoz be kell jelentkezni
Ezzel nagyon nem értek egyet. A nyelv megengedi. Amit a nyelv megenged, azt szabad. Gondolod, amikor assembly-ben vagy C-ben programozok mikrokontrollerre, ahol futásidő is, memória is kritikus, érdekelni fog egyesek kényeskedése, de legalább vacak lesz a kód? Ha fontos a futásidő, akkor 256 byte lesz a buffer, uint8_t az index, aztán a túlcsordulás megoldja, hogy a vége után az eleje jöjjön. Igen, tudom, hogy nem általános, de nincs mindig idő arra, hogy az index vizsgálata sizeof()-ra, meg efféle lassú megoldások.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Miből feltételezed, hogy hosszabb lesz a kód azáltal, hogy ugyanazt a kifejezést több sorban írod le? Konkrét példa itt van pár hozzászólással feljebb: https://hup.hu/comment/2743278#comment-2743278 (Mivel a két megoldás számomra műveletekben ekvivalens - bugoktól eltekintve - , számomra kétséges, hogy egyáltalán más kód fordulna csak azért, mert több sort írtál a C kódba.)
Másrészt a fordítókat is a tipikus kódfordulatokra készítik fel, azzal, hogy te valamit megpróbálsz nagyon trükkösen megfogalmazni, koránt sem biztos, hogy tömörebb vagy optimálisabb kódot csinálsz, mert lehet, hogy pont azzal teszel keresztbe a fordító optimalizálójának. Láttam már több helyen ilyen "agyonoptimalizált" kódot, ami miután meg lett írva "hagyományos" módon, egy junior is el tudta olvasni és még gyorsabb is lett vele, mert nem tolt ki a fordítóval.
Igen, tudom, hogy nem általános, de nincs mindig idő arra, hogy az index vizsgálata sizeof()-ra, meg efféle lassú megoldások.
De ki beszélt sizeofról? Arról van szó, hogy szedd külön sorba azt, ami módosít és azt, ami összehasonlít.
- A hozzászóláshoz be kell jelentkezni
Nekem mást jelent az érthetőség, de ez azért van, mert sokkal többet kódoltam assembly-ben, mint bármilyen más nyelven. A C nagy könnyebbség az assembly-hez képest, de assembly szemlélettel nagyon könnyen megtanulható és érthető a nyelv. Vedd úgy, hogy C-ben is assembly-ben programozok, csak C szintaxissal.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Kérlek, mutasd meg a különbséget a két generált ASM kód között:
- A hozzászóláshoz be kell jelentkezni
Azt értem, hogy jó a fordító optimalizálása, de ha előttem több betű van, több sor, azt nekem nehezebb megértenem, mint a tömör írásmódot. A tömör írásmód kifejezi, mit szeretnék, a több soros meg habosítás, bonyolítás, amely mögött hátsó szándékot sejtek, de a katarzis elmarad. Ez ízlés kérdése. Írom kényelmesen, áttekinthetően, úgy, ahogyan szerintem elegáns, egyszerű. Attól nem lesz érthetőbb a kód, ha óvodás a stílusa. De tudom, minden szakmára jellemző ma már, hogy nézzünk másokat is ostobának, meg magunkról is feltételezzük.
Látod, én inkább megkérdeztem, mert meg akartam tanulni, hogy ez garantált működés, vagy csak szerencsém volt. Azért akartam megtanulni, hogy magabiztosan használhassam. Workaround lehetett volna, ha több sorba írom, s akkor kérdeznem sem kell, de ha így járok el, buta maradok. Ugyanúgy irritál a sok soros írásmód, mint a felesleges zárójelezés, mintha nem lenne a műveleteknek precedenciája. Jó, néha zárójelezzen az ember, de attól nem lesz olvashatóbb a kód, amikor a sor vége felé van nyolc záró zárójel.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Attól nem lesz érthetőbb a kód, ha óvodás a stílusa.
Már hogy a viharba nem? Pont attól lesz egyértelmű: nem keveredik a módosítás és az összehasonlítás. Nem fordulhat elő side-effect. Nem kell rajta gondolkodni.
de a katarzis elmarad
Wtf. katarzis? Terméket állítunk elő, nem pedig katartikus élményeket keresünk.
Azért akartam megtanulni, hogy magabiztosan használhassam.
Ezzel nincs gond. Csak hosszú távon a magad doglát nehezíted, ha side effecttes kódot írsz.
Jó, néha zárójelezzen az ember, de attól nem lesz olvashatóbb a kód, amikor a sor vége felé van nyolc záró zárójel.
Erre ismét az a megoldás, hogy szét kell szedni. Tudod, keep it simple, stupid.
De tudom, minden szakmára jellemző ma már, hogy nézzünk másokat is ostobának, meg magunkról is feltételezzük.
Mert bakker, emberek vagyunk, hibázunk. Van, hogy fáradtak vagyunk már nap végén. Vagy mert nem gondol mindenki mindenre. Velem is volt olyan, hogy ebéd után még eszembe volt, hogy valahova be kell tennem egy inicializálást, elkezdtem megírni a kódot, tesztet, közben válaszoltam két kolléga kérdésére, aztán a nap maradék nagy részében azzal szenvedtem, hogy miért nem megy. Másnap standupon mondom kollégáknak, mire egyik csapattag "nem felejtetted el az inicializálást?" Bakker, de. (Aztán a megoldás az lett, hogy fél óra alatt megoldottam, hogy keresse meg automatikusan, mit kell inicializálni.)
- A hozzászóláshoz be kell jelentkezni
Ha már C fordító, a GCC és CLANG -O2 kapcsolóját ne hagyjuk ki, ha már belerakták:
https://godbolt.org/z/ez1Mxzbfq
https://godbolt.org/z/7Wr51s7Ta
- A hozzászóláshoz be kell jelentkezni
Ja, amúgy jogos.
- A hozzászóláshoz be kell jelentkezni
mert sokkal többet kódoltam assembly-ben, mint bármilyen más nyelven.
Remek, de ha C-ben vagy kódolj C-ben, ne ASM-ben.
Egyébként pont a topic léte mutatja azt, hogy miért nem jó az, hogy ha egy sorban írod le az egészet: még te is elgondolkodtál, hogy most előbb dekrementál, vagy előbb vizsgál. Ha nem írod egyben, hanem szétválasztod a logikailag nem összetartozó dolgokat rendesen (és ismétlem: mivel meg kell csinálni mindkét lépést, ASM kódot nem valószínű, hogy befolyásolja*), akkor fel sem merülhet olyan probléma, hogy most melyik fog előbb történni. Ezt jobb esetben még egy junior is megérti.
Azt érzem, hogy te azt hiszed, hogy azért, mert valami tömörebbnek tűnik optimálisabb is lesz. De ennél bonyolultabb a helyzet. Eleve mire optimalizálsz? Memóriára? Tárhelyre? Processzoridőre? Munkaidőre? Általában ezek egymásnak ellentmondó dolgok. Elkezdesz beépíteni egy cachet, több memóriát fogsz igényelni és a kódod is nagyobb lesz. Elkezdesz memóriára optimalizálni, lehet, hogy valamit többször kell kiszámolni, lassabb lesz a kód.
* Ha mégis, ráérsz vele foglalkozni akkor.
- A hozzászóláshoz be kell jelentkezni
(i < 0) ? printf("i negatív\n") : printf("i nem negatív\n");
Jó, hülye példa, így szebb:
printf("i %snegatív\n", (i < 0) ? "" : "nem ");
:)
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Aztán add oda egy fordítónak.
Egyébként így írnám. Azon se lepődnék meg, ha gyorsabb lenne, de most nem érek rá méricskélni. :P
printf( i > 0 ? "nem negatív\n" : "negatív\n");
Mondjuk ha nem csak logolás eredménye, még az is lehet, hogy kiszedném egy változóba, egyszerűbb debugolni.
- A hozzászóláshoz be kell jelentkezni
A nyelv megengedi. Amit a nyelv megenged, azt szabad.
Ez csak akkor lenne igaz, ha a nyelv tökéletes lenne. Ráadásul most, a valóságos nyelvek pedig még akkor sem voltak tökéletesek, amikor kitalálták vagy szabványosították őket, és még csak nem is minden ötlet öregedett szépen. Pl. a Fortran COMMON blockjai vagy EQUIVALENCE-e a maga idejében jó ötlet volt, csak eljárt felette az idő. Az aritmetikai GOTO valószínűleg már akkor sem. Mondjuk szerintem a () ? : vagy a ++ és a -- a C-ben jó ötlet, ma is az, de nem kell mindenütt, feltétlenül használni.
A trükkök, amiket mondasz, akkor jók, ha muszáj úgy megírni (mert különben nem bírná a hardware), de pl. ha az uint8_t-t azért használnám, hogy a túlcsordulással visszaérjen az elejére, akkor azért valahogy úgy írnám meg, hogy
typedef uint8_t SmallPtr;
#define SMALLPTRMAX ...
.
.
SmallPtr k;
.
.
... var[k % SMALLPTRMAX]...
.
.
.
aztán megnézném, hogy a fordító optimalizálta-e a cuccot. Így legalább érteni lehet, hogy visszamegyek az elejére anélkül, hogy nagyon gondolkoznék, évek múlva, álmosan olvasva is. És aztán ha a kódrészletet újra felhasználod más gépen, más hosszúságú számokkal, akkor is menni fog.
- A hozzászóláshoz be kell jelentkezni
Ha valós időben kell mérned, feldolgoznod hárommillió mérési eredményt másodpercenként egy mikrokontrollerrel, és az eredményeket elküldeni, akkor szerintem nem írsz holmi % jelet, modulo-t. :) Persze, akár sqrt()-t is írhatnál, ha matematikailag indokolt lenne, csak minek. :) Működni garantáltan nem fog.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Azt mondod, nincs annyi esze a compilernek, hogy eltakarítja azt a modulo-t, ha éppen az adott változó maximális értékével modulo-zok? Akkor is meg lehet csinálni valami #define-nal, hogy ott is legyen (olvasva), meg ne is (futtatva).
- A hozzászóláshoz be kell jelentkezni
Eltakarítja azt a modulót, sőt ennél még okosabb. Te hogyan csinálnál 10-es osztást?
A div hardverben is nagy késleltetésű művelet, gondoltál erre? Na így kell 10-zel osztani ARM mikrovezérlőn:
https://c.godbolt.org/z/cqTEaxcz3
Assemblyből látszik, hogy amire jobban érdemes figyelni, szükséges-e foglalkozni negatív számokkal, vagy csak pozitív számra lesz?
Rust esetén ugyanezt látod (csak x86 assembly fordító van a godbolt-on), de le van kezelve a nullával való osztási hiba, észrevétlenül nem maradhat a hibás eredmény.
https://rust.godbolt.org/z/G83fcMdY9
Persze ha felhasználod és ott tutira nem nulla az osztó, akkor nincs mit hibakezelni, a fordító kihagyja a hibakezelést végző kódokat.
- A hozzászóláshoz be kell jelentkezni
Amit a nyelv megenged, azt szabad.
Ha soha többé nem kell a kódhoz nyúlni, akkor igen, egyébként érdemes ezt átgondolni egy kicsit. :)
- A hozzászóláshoz be kell jelentkezni
Ezzel nagyon nem értek egyet. A nyelv megengedi. Amit a nyelv megenged, azt szabad.
Hadd találjam ki: keveset vagy soha nem dolgoztál csapatban, ahol adott esetben óráid vagy szélsőséges esetben perceid vannak arra, hogy megértsd és átlásd más valaki kódját, mert áll minden és akár percenként több ezer dollár a veszteség.
Elegendő erőforrás általában van, legyen olyan a kód, aminek első ránézésre is teljesen egyértelmű a működése, akkor is, ha ez miatt több sorból áll leírni. Ráadásul a legtöbb fordító megoldja végül azokat az optimalizálásokat, amelyek olvashatatlanná teszik a forrást és ugyanaz lesz a végrehajtás erőforrásigénye.
Ha fontos a futásidő, akkor 256 byte lesz a buffer, uint8_t az index, aztán a túlcsordulás megoldja, hogy a vége után az eleje jöjjön.
Ha fontos a futásidő, akkor mindez legyen odaírva kommentként, bő magyarázattal.
- A hozzászóláshoz be kell jelentkezni
Azért ezt árnyaljuk: a kódnak nem feladata, hogy kezdőket (pláne kívülállókat) programozni tanítson. (Vagy elhitesse velük, hogy tudnak programozni, ha nem tudnak.)
Például egy if (hajo && *hajo) printf ("Hajó neve='%s'\n", hajo);
szerű sornak a jelentése első ránézésre evidens kell legyen egy C-programozó számára, különben nem C programozó.
Az autógyártó sem ragaszt 'ezt tekerni kell' feliratú címkét a kormányra, hanem elvárja, hogy aki nem ért hozzá, az ne akarja vezetni a járgányt.
- A hozzászóláshoz be kell jelentkezni
Bezzeg a "Macskát ne rakd a mikróba!" ...
- A hozzászóláshoz be kell jelentkezni
Azért ezt árnyaljuk: a kódnak nem feladata, hogy kezdőket (pláne kívülállókat) programozni tanítson. (Vagy elhitesse velük, hogy tudnak programozni, ha nem tudnak.)
Bocsánat, de itt most egy hete megy a rugózás és a gittrágás azon, hogy minden C fordító ugyanazt fogja-e érteni-e az adott soron és elvilen senior C/C++ fejlesztők n+1 év tapasztalattal rugóznak ezen, hogy jó-e ez így és érthető-e ez így. Ha éles helyzetben van erre öt perced egy ismeretlen kódbázisban, akkor hogyan akarod lekezelni ezt a helyzetet?
Az autógyártó sem ragaszt 'ezt tekerni kell' feliratú címkét a kormányra, hanem elvárja, hogy aki nem ért hozzá, az ne akarja vezetni a járgányt.
Nyilván lehet eltartott kisujjal elmélkedni azon, hogy ki az igazi C programozó, de a helyzet az, hogy nincs elég bármilyen tudású programozó és úgy néz ki, hogy ez nem igazán fog változni a közeljövőben. Ha pedig nincs elég ember, akkor úgy érdemes dolgozni, hogy még egy balfasz is meg tudja érteni és hatékonyan tudjon dolgozni a kódbázison, még ha az a munka valami favágás is. Nem beszélve arról, hogy stresszes esetben is sokat segít, ha nem kell ilyen faszságokon agyalni, hogy vajon mit is csinál az adott sor. Szóval legalább legyen felette egy sor megjegyzés, hogy mi a lófaszra gondolta a költő és milyen side effect van itt kihasználva és ezzel megbaszva az olvashatóság a teljesítmény oltárán.
Feltéve, hogy csapatmunkáról van szó, mert úgy néz ki, hogy a 'jóvanazúgy' hozzáállásúak magányos farkasok, akiket nem lehet csapatba tenni, mert öt perc alatt öngyilkos lesz a csapat fele és megáll a munka, szóval hiába a szakmája csúcsa az illető, ha csapatban való munka esetén nullával való szorzás a teljesítménye.
- A hozzászóláshoz be kell jelentkezni
Azért ezt árnyaljuk egy kicsit: amióta elhangzott a két varázsszó (hogy rövidzár kiértékelés és szekvenciapont), azóta nincs több kétség az ügyben, hogy egyértelmű-e a topinyitó kódja. Azóta már csak arról van szó, hogy kinek mi tetszik. Ami szintén fontos és kérdés, de azért valahogy súlyozni kellene azzal, hogy az illető mennyire van benne a C-programozásban.
- A hozzászóláshoz be kell jelentkezni
Azért ezt árnyaljuk egy kicsit: amióta elhangzott a két varázsszó (hogy rövidzár kiértékelés és szekvenciapont), azóta nincs több kétség az ügyben, hogy egyértelmű-e a topinyitó kódja.
És ez bruttó-nettó mennyi idő után dőlt el? Öt perc? Fél nap? Két nap? Ki fizeti majd egy cégnél mindezt?
Ha ugyanezt három külön if-ben írja le, akkor első ránézésre látszik, hogy a mialófasz történik és a fordító igen nagy eséllyel amúgy ugyanazt fogja fordítani mind a két esetben.
Azóta már csak arról van szó, hogy kinek mi tetszik. Ami szintén fontos és kérdés, de azért valahogy súlyozni kellene azzal, hogy az illető mennyire van benne a C-programozásban.
Továbbra is kihangsúlyoznám: ha csapatban dolgozol, akkor sokkal-sokkal-sokkal fontosabb a kód bárki számára bármikor és bármilyen körülmények között való egyértelmű értelmezése, mint az, hogy kinek mekkora a C-fasza és azt mennyiszer mutogatja azt a kódbázisban.
Ha valaki ilyen kódot ír csapatban, akkor rövid idő alatt eljut oda, hogy tulajdonképpen negatív munkát tesz a projektbe, mert minden egyes kódmódosítás során emberórák mennek a szemétbe annak értelmezésével, hogy mit írt és az mit csinál.
- A hozzászóláshoz be kell jelentkezni
De milyen mélységig akarsz lemenni azon a létrán, hogy bárki, bármikor, bármilyen körülmények között tudja olvasni? Mivel én K&R C-n nőttem fel, lehet, hogy nekem az ANSI C16 (hasból mondtam) minden sallangja nincs meg, de számomra pl. fenti && és || szépen olvasható (és olvashatóbb, mint a 3x egymásba ágyazott if). És láttam olyan C-programozót, aki meg a pointerekkel volt nagyon rossz viszonyban, és amikor ponterek tömbjére mutató pointerek jelentek meg, befelhősödött a tekintete.
Szerintem itt kezdődik a C
main(){printf(&unix["\021%six\012\0"], (unix)["have"]+"fun"-0x60);}
(A Korn-shell-féle David Korn IOCCC-beli egyik kis gyöngyszeme. Én nagyon szeretem.)
- A hozzászóláshoz be kell jelentkezni
De milyen mélységig akarsz lemenni azon a létrán, hogy bárki, bármikor, bármilyen körülmények között tudja olvasni?
Amilyen átlagos emberanyag szembejön egy átlagos projekten. Szerintem te is évek óta egyedül dolgozol, ha ilyen kérdést felteszel...
Szerintem itt kezdődik a C ...
Na, ez a faszméregetős része, aminek egy élő, aktív és sok ember által fejlesztett kódban semmi-de-semmi keresnivalója. Ha az általam vezetett bármelyik projekten ilyen jellegű kódot adnál le, akkor simán repülnél a csapatból, mert hiába te vagy a legnagyobb faszú a környéken, ezek miatt a csapat összteljesítménye egyszerűen rosszabb, mint nélküled. És végeredmény az, hogy a csapat összességében olcsóbb és gyorsabb, ha nem dolgozol.
Ezen lehet évekig keseregni minden egyes korsó sör felett, hogy milyen világ van már, csak amíg K&R idejében a teljes UNIX forráskódja 20 ezer sor alatt volt és egy jó képességű ember egy év alatt meg tudta írni, addig jelenleg olyan komplex feladatok vannak, amire több száz emberév elmegy és itt kiemelkedő képességű senior emberekről van szó. És nincs elég ember.
- A hozzászóláshoz be kell jelentkezni
De mint magad is utalsz rá, az emberanyag gyengül, azaz a ma még az átlagnak jó konstrukciók 5 év múlva simán emészthetetlennek fognak tűnni - holott reményeim szerint 5 év alatt nem fog teljesen eltűnni minden ma fejlesztett szoftver.
Pont ez a bajom ezzel: ha elkezdünk tetszóleges nyelvből tetszóleges konstrukciókat elhagyni, akkor nincs megállás.
(Ami pedig a fenti printf-et illeti: szerintem egy fejlesztőnek igenis látnia és megértenie is kellene - az általa preferált nyelven - hasonló kódokat. Ez a holtig tartó tanulás része kéne legyen. Ha már C-ről beszélünk itt, akkor olvasson régi IOCCC versenyfeladatokat. Az se baj, ha a magyarázatát is elolvassa / megérti. )
Ja, ez lemaradt:
azzal egyetértek, hogy egy sokfejlesztős kódba ilyesmi nem való. Viszont jó, ha egy sokfejlesztős gárdában van 30-50 %, aki képes megérteni.
- A hozzászóláshoz be kell jelentkezni
De mint magad is utalsz rá, az emberanyag gyengül
Nem gyengül, ugyanannyi vagy még több zseni van a területen, mint volt 50 éve, csak kb. százezerszer több a feladat, amihez kb. százezerszer több zseni kellene, mint amennyi van. De mivel nincs százezerszer több zseni, ezért be kell érjük pár millió kevésbé zsenivel és pár tízmillió balfasszal. A többi következtetésed ezen a rossz alapfeltevésen vérzik el.
- A hozzászóláshoz be kell jelentkezni
Továbbá ne hagyjuk szó nélkül azt sem, hogy az internetre csatlakozó szinte minden miatt a szoftverbiztonság felértékelődött.
Ezzel együtt ma a kód olvashatósága, auditálhatósága fontosabb lett, mintsem a "húdetrükkösvoltam" kódolási stílus.
- A hozzászóláshoz be kell jelentkezni
szerintem egy fejlesztőnek igenis látnia és megértenie is kellene
Kell látnia és megértenie, csak a fizetést nem a minél elmésebb kód után szokás adni, hanem az után, hogy megoldja a feladatot és karbantartható kódot ír valaki.
- A hozzászóláshoz be kell jelentkezni
gcc version 11.1.0 (GCC) azt írja ki, hogy:
unix
Ezért igazán kár volt faxnizni :-) Viszont bevallom nem értem miért ezt írja ki. Elmagyarázza valaki? A "unix" szimbólumot eleve nem tudom mit takar.
- A hozzászóláshoz be kell jelentkezni
Felteszem, a preprocesszor definiálja 1-nek. De én is úgy látom, hogy ez az egofényezés kategória (szépen fogalmazva;)
Szerk: Ha valami haszna van, az az, hogy felhívja a figyelmet arra, hogy a preprocesszor kimenetét is érdemes néha a hibakereséskor megtekinteni.
cpp -dDI unix_demo.c unix_demo.E
- A hozzászóláshoz be kell jelentkezni
Ilyen egyszerű részekkel érdemes kezdeni a feldolgozását, hogy, mi a fenét jelent C-ül az, hogy ize["ecet"], vagy épp mire lehet ezt átalakítani; vagy épp mi az eredménye annak, hogy "szöveg" + 3. (Anno volt olyan AIX-es gép, amin muszáj volt a cc-nek odaadni a -Dunix paramétert a sikeres fordításhoz. (Rossz nyelvek szerint ez is azt bizonyította, hogy a kék rózsa rendszere nem igazi Unix :-) )
A kód Unix-like rendszereken és ASCII kódolást (vagy annak valami kibővített verzióját) feltételezve működik. Működése a fent linkelt www.ioccc.org oldalon lépésről lépésre le van vezetve.
- A hozzászóláshoz be kell jelentkezni
Nem az egymás utáni && -ekkel van a gond, hanem azzal, hogy értelmes ember nem tesz IF-be olyan kódot, ami módosít valami adaton. Ha feltételt vizsgálsz, vizsgálj feltételt, ha műveleteket hajtasz végre, műveleteket csinálj. Ha valakinek magyarázni kell, hogy miért baj a side effect, az inkább ne menjen szoftverfejlesztőnek.
Szerintem itt kezdődik a C
Persze, ez tök jó egy kávé/tea mellé fejtörőnek, ha egy ilyen kód jön szembe code reviewen, akkor reflexből megy rá a reject, hogy ne húzza az időmet a kolléga.
- A hozzászóláshoz be kell jelentkezni
Igazad van, DE.
Nem csak projektmuka és kezdő programozók esetében. Az ember csinálja meg "szépre" és könnyen olvashatóra a kódot, már csak a maga kedvéért is!
Egyszer a heapsort-ot tanulmányoztam, míg eljutottam az igazi gurukig, akik két sorban is meg tudták fogalmazni a feladatot. :-D
- A hozzászóláshoz be kell jelentkezni
Minél nehezebben olvasható a kód, annál drágább lesz a cégnek az a programozó, aki tovább tudja majd fejleszteni a programot, amikor a mostani programozó már megunta.
- A hozzászóláshoz be kell jelentkezni
Eddig jó. Most jön a nehéz kérdés: Melyik a nehezebben olvasható:
#1
if (hajo) {
if (*hajo) {
printf ("hajo=%s\n", hajo);
}
}
#2
if (hajo && *hajo) {
printf ("hajo=%s\n", hajo);
}
- A hozzászóláshoz be kell jelentkezni
A kérdéskör nem erre szólt, hanem erre a részére:
... && --*ptr == 0
Másrészt, én személy szerint a nested if invertálását szeretem jobban:
if (!ptr) return; if (*ptr == 0) return; *ptr--; if (*ptr == 0) timeout();
Ezt beleteszed egy inline függvénybe és bárki, bármikor és bármilyen körülmények között ránéz, látja, hogy mi és milyen sorrendben történik, a fordító pedig pont ugyanazt a tényleges kódot generálja, ami az egysoros megoldás, ami azért rossz, mert még egy ezer éve C nyelven fejlesztő senior ember is azzal nyit egy fórumtémát, hogy "Ilyesmit szoktam írni, de egy pillanatra megrémültem." Egyszerűen Q.E.D., hogy ez a fajta forgalmazás nem egyértelmű, ami kockázat, kerülendő.
- A hozzászóláshoz be kell jelentkezni
*ptr--;
Inkább:
--*ptr;
vagy
(*ptr)--;
Nem a pointert csökkentjük a dereferálás és semmire sem használás után.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Ahja, több mint két évtizede nem írtam C programot.
- A hozzászóláshoz be kell jelentkezni
"Ah so" -- ahogy a művelt francia mondja.
- A hozzászóláshoz be kell jelentkezni
Nem a konkrét nyelv a téma itt, ebben a szálban, hanem a clean code elvek vs faszméregetés; illetve az, hogy ha ebből a kérdéskörből egy feltehetően több évtizedes tapasztalattal rendelkező senior C fejlesztő fórum témát kreál a kétségei miatt, akkor igencsak sántít az, hogy ez márpedig jó így és mindenki számára teljesen egyértelmű.
- A hozzászóláshoz be kell jelentkezni
Bocsánat, ebben a topikban durván számolva egy profi [= pénzért csinálja és ez a fő profilja] C-programozó pörög, mindenki más mellékesen, vagy hobbyból, vagy 'régebben egy kicsit' C-programozó.
- A hozzászóláshoz be kell jelentkezni
Na, ez a profi [= pénzért csinálja és ez a fő profilja] C-programozó kreálta a témát és vannak kétségei, mondhatni teljesen jogosan, mert kiélezett helyzetben könnyen félremehet az értelmezése.
A többiek meg kibicelnek és méregetik a faszukat az asztalon, hogy ki tud trükkösebbnél trükkösebb kódot írni, ami minél több embert szopat meg és elismerést keresve nézelődnek körbe-körbe.
- A hozzászóláshoz be kell jelentkezni
- A hozzászóláshoz be kell jelentkezni
Te magad definiáltad pedig: pénzt kap érte?
- A hozzászóláshoz be kell jelentkezni
És elfelejtettem odaírni, hogy "és ez a fő profilja"? Akkor öregszem.
- A hozzászóláshoz be kell jelentkezni
Látszólag a C nyelvű programírás része a fő profiljának, pénzt kap érte, nem hobbiból csinálja, de felőlem legyen igazad, ha úgy jobban alszol.
- A hozzászóláshoz be kell jelentkezni
Műszer fejlesztésénél nincs vége a munkának a hardware megtervezésével, a firmware-t is meg kell írni, szóval igen, munkaidőben, munkabérért szoktam C-ben kódolni.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Én programozok C-t is a mai napig pénzér. Legalább ketten vagyunk.
- A hozzászóláshoz be kell jelentkezni
Végülis a GCC ma már gyakran szól (-Wall), hogy valami félrement például a *ptr--; kapcsán, de nem a valós problémáról szól illetve gyakran egyáltalán nem is szól. Ezért erre sem lehet alapozni.
Lássunk egy példát, amely egy szemléletes példája a pointerrel elkövetett információszivárgásnak: https://code.sololearn.com/cR2cs4Usrn0i
Itt mindössze az alábbi warning jön, de ettől lefordul és fut. A probléma, hogy mindeközben a "titkos" változó tartalma is kiírásra kerül.
teszt.c:14:5: warning: value computed is not used [-Wunused-value]
14 | *ptr--;
| ^~~~~~
Kontrasztként a Rust, mint C vetélytársa normál (safe) módjában nem engedi, hogy varázslattal a változód által foglalt memóriaterületen kívül olvass vagy írj. Lehet próbálkozni.
- A hozzászóláshoz be kell jelentkezni
Ha a pointerrel rácímzel a másik változóra, nyilván el tudod rontani azt, meg hozzá tudsz férni. Én ebben nem látok problémát, senki sem mondta, hogy túl kell címezni tömbön, rá kell címezni olyan változóra, amire nem szeretnénk. Szedtem én már fel bufferből 24 bites változót úgy, hogy uint32_t-be tettem, majd maszkoltam 0xffffff értékkel, hiszen a legfelső byte-jában már a következő változó alja volt ezáltal. Ez teljesen legális, semmi baj nincs vele, ha tudod, hogy mit csinálsz.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
senki sem mondta, hogy túl kell címezni tömbön
Nézd globálisabban. Az a probléma, hogy az internetes eszközöknél a low level sérülékenységek >70%-a mégis ilyen túl lesz címezve problémából fakad.
Szerintem ezeknek a programozóknak se mondták, hogy címezzenek túl a tömbön, ha több adat érkezne. Mindössze nem figyeltek mindenhol oda a korlátozására és elég csak egyetlen helyen figyelmetlennek lenni.
Fordítsuk meg: miért nem lehet alapértelmezett az, hogy nem enged ki a változó által lefoglalt területből a nyelv?
- A hozzászóláshoz be kell jelentkezni
> miért nem lehet alapértelmezett az, hogy nem enged ki a változó által lefoglalt területből a nyelv?
Mert nem nagyon van ilyesmi értelmezve. Egy `char *` vagy `void *` pointerből nem mondja meg senki, hogy aki ezt a pointert adta, az hány bájtra gondolt közben.
- A hozzászóláshoz be kell jelentkezni
Ha ez el tud dőlni fordítási időben, valamint kikapcsolható, akkor legyen. Ha csak egyetlen órajel vagy byte is kárát látja futásidőben, akkor ne legyen. Nem csak oprendszer fölött programozunk sok gigabyte RAM-ot felhasználva sok magon, sok gigahertzes CPU-n, ahol igazából pár MHz is elég lenne, hanem van, ahol minden órajel és minden byte számít.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Nem az lenne a megoldás, hogy nem pointer aritmetikával érdemes tömböt kezelni, ha nem csinálsz olyat, amihez tényleg szükséges, hogy tudd, hogy mi hol van a memóriában? Hanem pl. csinálsz egy struktúrát, és függvényeket az elérésére, sőt, C++-ban a boost::numeric::ublas::vector tudja is (ha nem kapcsolod direkt ki).
typedef class{
int *ptr;
size_t size;
} IntArray;
void IntAlloc(IntArray *a, size_t n){
a->ptr = (int *)malloc(n * sizeof(int));
if(!(a->ptr){
fprintf(stderr, "Allocation error.\n");
abort();
}
a->size = n;
}
void IntDealloc(IntArray a){
if(!a.ptr) return;
free(a.ptr);
}
int IntArrayGet(IntArray a, size_t n){
if(n > a->size){
fprintf(stderr, "Array read access violation.\n");
abort();
}
return a.ptr[n];
}
void IntArraySet(IntArray a, size_t n, int v){
if(n > a->size){
fprintf(stderr, "Array read access violation.\n");
abort();
}
a.ptr[n] = v;
}
Biztos van benne hiba, nem próbáltam ki. Az is biztos, hogy egy rutinos C programozó sokkal szebbet ír, meg az is, hogy kell még egy csomó minden (pl. egy csomó, egymás utáni elem írásakor nem akarsz minden elemnél ellenőrizni, és akkor még nem is elég intből intbe, hanem lehet, hogy konverzió és valami függvény alkalmazása is kell közben, ha mondjuk egy float vectort akarok int-ekből feltölteni, vagy egy float arrayt egy másik float array elemeinek a gyökével). De ilyesmire eleve érdemesebb C++-t használni. Olyasmire meg C-t és pointereket, ha pl. egy memory mapped device-hez írsz éppen drivert.
- A hozzászóláshoz be kell jelentkezni
Sírok. Mondom, hogy az sem fér bele sokszor a futásidőbe, hogy függvényt hívjunk, mert az CALL, RET, meg memória másolás. Lehet szépészkedni, meg akár shell scriptet írni, de nem akkor, amikor valós időben dolgozol fel hárommillió mérési eredményt, s az eredményt el is kell küldened.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
És nem is ez lenne a fő probléma, hanem az, hogy eredetileg nem is volt szó semmilyen tömbről, az csak valamilyen sokadfokú asszociációval került elő.
- A hozzászóláshoz be kell jelentkezni
Önmagában nem zavar az offolás, ha az a szakma keretein belül jut eszébe valakinek. De valóban, az eredeti felvetésem más volt. Való igaz, az && alacsony precedenciája miatt önállóan kiértékelhetővé teszi annak argumentumait, s mehetünk balról jobbra az első false-ig, ahol kilépünk. Ez jó, hogy így van, a problémám az volt, hogy ha a precedenciát nézzük, előbb módosulhat a pointer által mutatott változó, s akkor a balról jobbra kiértékelés is más eredményt ad. Mondjuk akkor a nyitóban írt kifejezés NULL pointer dereference is egyben.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Ez a rész szerintem már marhára nem a microcontroller-programozásról szólt, hanem arról, hogy C-ben is lehet olvasható és biztonságos kódot írni. Azt elismerem, hogy ára van. Ahol nem fér bele a futásidőbe, ott nyilván nincs más megoldás, mint hatékony kódot írni. Kevesebb tere marad a programozónak hibázni.
- A hozzászóláshoz be kell jelentkezni
Bizony, ez a rugózásról szól. ;) Meg arról, hogy C++, Java és Rust nyelven is osztják egyesek az észt. :-D
Az "áttekinthető kód" - ami locsemeg barátunknak áttekinthetetlen - az pont olyan, amit gondolkodás nélkül át lehet ültetni pl. PIC16 mikrokontrollerre. (Ami véletlenül locsemege barátunk kedvenc és csak asm-ben programozott autómárkája. :-D)
Ehhez a kódhoz két peremfeltétel kell:
- balról jobbra értékelünk
- "boolean rövidzár" szerint (Ami régen a túróspaszkálban csak opció volt.)
Lényegében egy jó C fordító megfelelő optimalizálással az idézett kódot fogja előállítani. Ha "visszafelé" elkészíted belőle a folyamatábrát, akkor nehezen kapod meg a topicnyitó if szerkezetet. Ez is azt bizonyítja, hogy nem jól van megfogalmazva.
A "Kevesebb tere marad a programozónak hibázni." annyiban jó megátás, - egyszerűbb cpu-t feltételezve - hogy pontosan azt kell leírni, amit csinálni szeretnél. És ettől kezdve egyszerűsödik a helyzet: Ha mást írsz le, akkor más fog történni. :-D
Most pedig már nem értem, miért molyol egy asm programozó a C side effect szintaxis elemzésével. A többieket inkább értem, mert már a C-t is elfelejtették, nemhogy az asm-et.
- A hozzászóláshoz be kell jelentkezni
Nekem a második. De lehet, hogy csinálnék egy függvényt rá, ami boolean isnull(const void *), és remélném, hogy a compiler inline-olja.
- A hozzászóláshoz be kell jelentkezni
Nem ez volt a kérdés, hanem a side effect, ami mögötte van. Ilyen vizsgálatnál én is inkább a másodikat választanám. Mondjuk valószínűleg kiírnám a feltételvizsgálatot (== NULL/== 0). Nem azért, mert a fordított kódban bármi változást okoz, hanem azért, hogy egyértelműen jelezzem más, a kódot olvasónak, hogy mi volt a szándékom.
- A hozzászóláshoz be kell jelentkezni
Elnézést, hogy belekotyogok ebbe a kissé akadémikus vitában. Szeritem itt a magányos farkasok és a nagy-multi_csapat tagjai közötti vitának lehetünk tanúi. Azt mindkét félnek tudomásul kell vennie, hogy más-más feladat más-más munka- és programozási stílust követel. Ha használsz egy nyelvet más a szókészleted a családban, egy tudományos előadáson és a kocsmában. Hát valami ilyesmiről van szó itt is...
A magamrészéről igyekszem - úgymond - didaktikusan programozni. Vagyis szeretném megérteni 4-5 év múlva is, hogy mit is akartam akkor, évekkel ezelőtt végrehajtatni a géppel. Ugyanakkor megértem azt, ha valaki küzd az órajelekért, mert ilyen dolgokkal is foglalkoztam. Alapvetően nagyon fontos lenne tudnia minden valamire C programozónak, hogy mire is fordul a kódja.
Akik team-ben dolgoznak ott nincs mese, van egy vállalati elvárás és ahhoz kell igazodni.
A magam részéről örülök, hogy olyan munkám van amilyen, nagyon kevés alklamazkodást igényel és a olyan programírási stílust alakítottam ki, amely számomra a legáttekinthetőbb. Utánam sem jön senki, hogy belejavítson, hibát keressen a kódban, egyszerűen az a megközelítés, amit használok (nem kizárólag a programírási stílusra gondolok) velem - pontosabban: a mi generációnkkal - kihal.
UFF, beszéltem.
> Sol omnibus lucet.
- A hozzászóláshoz be kell jelentkezni
Ugyanakkor megértem azt, ha valaki küzd az órajelekért, mert ilyen dolgokkal is foglalkoztam.
Alapvetően erre írtam pár napja, hogy legalább legyen felette egy sor megjegyzés, hogy mi a lófaszra gondolt a költő és milyen side effect van itt kihasználva és ezzel megbaszva az olvashatóság a teljesítmény oltárán... :)
- A hozzászóláshoz be kell jelentkezni
Már ne haragudj, de ez hülyeség. Egy villanyszerelő sem fog máshogy szerelni villanyt ha egy családi ház garázsába köti be, vagy ha egy irodaházat köt be. Vagy az UTP kábelt sem fogod máshogy bekötni.
Minden szakmának megvannak a maga szabványai, szabályai, best practice-ei. Ezekre az esetekre is vannak.
Ráadásul itt meg konkrét ASM kóddal is bizonyítva lett, hogy szimplán nem igaz az sem, hogy szarabb kód fordulna, ha side effect mentesen írta volna meg az if-et.
- A hozzászóláshoz be kell jelentkezni
Szerintem egy picit félreértetted, amit írtam...
> Sol omnibus lucet.
- A hozzászóláshoz be kell jelentkezni
(Most ahogy a topikot nézem, olyan, mintha igen kiváló víz- és gázszerelők akarnák megmondani a tutit villanyszerelés ügyben, 'közmű az közmű' alapon.)
- A hozzászóláshoz be kell jelentkezni
Vagy nem, mert vannak részek, amik nem változnak, függetlenül attól, hogy ilyen szoftver vagy olyan szoftver. Ráadásul ha már megmondani a tutit a téma: ha már előkerült a MISRA C is: Its aims are to facilitate code safety, security, portability and reliability in the context of embedded systems, specifically those systems programmed in ISO C / C90 / C99. Ez ugyan autóiparból jött, de találsz máshova is hasonló rendszereket. De elég csak felcsapni egy antipattern gyűjteményt.
- A hozzászóláshoz be kell jelentkezni
Off: Egy ilyet találtam, első pillantásra (mármint nem számítva azt az időt, amíg a bevezetést (vagyis az első öt fejezetet) áttekertem) nem találtam benne semmit, amivel ne értenék egyet.
- A hozzászóláshoz be kell jelentkezni
Mi használjuk ezt a szabályrendszert, de ettől még ha mondjuk játékból írok magamnak egy kis programocskát, akkor simán lehet, hogy nem tartom ezeket be. Eleve nem rakok össze statikus kódelemzőt sem a játék projektjeimhez, így nem tudom garantálni, hogy nem csúszik-e be valami apróság.
Szóval részemről a hasonlat nem teljesen áll meg.
- A hozzászóláshoz be kell jelentkezni
Nekem bejon az egysoros. Olyan tagokbol al, ami gyakran elofordul, az elso ket tag (pointet null? Ertek null?) konkretan unalmas. Az ember szeme vegigsiklik rajta, megnyugszik a lelke, hogy le van ellenorizve, majd ranez a harmadik tagra, hogy mi a lenyeg.
Ezt szerintem meg egy juniornak is ertenie kell. Ha nem erti, akkor meg nem junior, hanem csak tanulo. Ez egy jo programsor, lehet belole tanulni.
Locsemegenek pedig pirospont, hogy volt batorsaga a ketsegeit is megosztani, es nem azzal a kijelentessel jott ide, hogy csak az jo amit o mond.
A programozas absztrahalasi szintekrol szol. Ha valaki nem tudja az elmejeben elhelyezni a fenti egysorost, akkor ne alljon neki python lamda fuggveny mukodeset megerteni, - csak masolja be Ctrl-C, Ctrl-V a stackoverflowrol a gugli elso talalatat a probkemajara, es remenykedjen. Hiszen ez a junior, nem?
En leginkabb bash-ban irok dolgokat. Ott is lehet hasonlo szornyusegeket irni, harom soros csatakigyot benne grep, awk, sed, tr, es ami meg az eszembe jut, pl. bash beepitett string manipulacio.
Nem szeretem, mert fel ev mulva nehez seggberugni erte magamat, nem hajlik valami jol a terdem. Szet szoktam szedni fel soros reszekre, atmeneti valtozoba gyomoszolom a kimenet, es van, hogy meg ra is ellenorzok, mert miert ne. De nincs az azvisten, hogy minden elemi parancsot kulon sorba irjak, az mar a lo masik oldala lenne.
Szoval van egy egeszseges hatar, hogy kinek hol huzodik ez, azt embere valogatja. En Atari BASIC -en szocializalodtam, ergo lazan parametereztem a GOTO-t valtozoval, es sokaig nem ertettem, hogy miert dagad a programozas tanar feje, amikor panaszkodtam, hogy a Pascal szar, mert nem donthetem el futas idoben, hogy hanyas sorra ugorjon a vezerles, pedig az milyen elegans. Ehelyett az a sok IF... Tiszta ovoda. :-)
En a commentek hive vagyok. Ahol nem trivialis a kod, vagy nem eleg beszedesek a valtozok nevei, ott leirom, hogy mire gondolt a kolto. Es pesze megesik, hogy futas kozben ugy megyek tovabb, hogy bash kodbol a valtozok ertekei alapjan kigeneralok nehany tucat masik bash kodot, es rajuk adom a vezerlest, hogy na akkor fussatok bolondok, de a hatterben am, mert ettol leszunk gyorsabbak. Es gyonyoruen mukodik. Nem trivialis, ezt alairom, de ott a comment, hogy mit miert csinalok. Ha ezt mind szet kene bontanom, mert az onmodosito kod ordogtol valo, akkor morcos lennek.
Az eszkozoket szerintem nem kell felni hasznalni. Egy if -ben tobb ellenorzes es egy-ket valtozo egyszeru modositasa siman belefer.
Szerintem.
- A hozzászóláshoz be kell jelentkezni
Te felfogtad, hogy itt nem az egy sor vs több sor a probléma, hanem a side effects kód?
Lehetne ez is a köd:
if (cucc && *cucc && Kutya(--(*cucc)) && Cica(--(*cucc)) {...}
ez is tök jól olvasható épp csak tippelni lehet, hogy vajon mi fog itt történni, nem módosít-e valahol valami olyat, ami miatt már nem az fog történni, amire számít az ember.
- A hozzászóláshoz be kell jelentkezni
Ebben a sorban ket nezopont utkozik:
- 1, mit akar a kolto?
- 2, mit csinal belole a compiler?
A fenti szalban sokan az egyes ponton porogtek, hogy szegeny juniorok agya fel fog robbani ha ilyet latnak, tessek ezt szebben, csoportmunkasabban megirni. Reszben egyet is ertek veluk, de ezt a problemakort meg lehet oldani egy ertelmes kommenttel.
A masodik pontra pedig az ipar mar megszulte a megnyugtato valaszt: ugy hivjak, hogy teszteles. :-)
- A hozzászóláshoz be kell jelentkezni
megnyugtato valaszt: ugy hivjak, hogy teszteles.
A biztonságos programozáshoz szükséges ezen kívül a forráskód auditálás, ugyanis a tesztelés önmagában nem tud abszolút 100,0%-ot biztosítani.
Side effect, a fordítófüggő "most éppen jó" és a kód félreolvashatóság az mind kockázat növelő tényező, a biztonságos programozás elve ellen dolgozik.
- A hozzászóláshoz be kell jelentkezni
Ebben egyetertunk. En a linterek hasznalatat is a teszteleshez sorolom. Bash eseten a shellcheck peldaul nagyon hasznos, en nagyon szeretem ahogy ramutat arra, hogy eppen hogyan tettem taposoaknat egy teljesen artatlanul kinezo kifejezesbe.
- A hozzászóláshoz be kell jelentkezni
Az olyan kód, ahol egy feltétel vizsgálatban valami adatmódosítás van, szintén taposóakna. Szerinted újabb, modernebb nyelvekben miért nem lehet ezt ebben a formában leírni? Vagy amit még le lehet (pl. mikor módosító függvényt hívsz az if-ből), ott is miért vannak ajánlások, konvenciók, hogy ne tedd?
- A hozzászóláshoz be kell jelentkezni
Kimondani, hogy feltetelvizsgalatban SOHA ne legyen adatmodositas, szerintem mar a lo tuloldala.
while (i--) { ... } // jajistenem mindmeghalunk
- A hozzászóláshoz be kell jelentkezni
while (i--) { ... i--; ...} és már kész is a végtelen ciklus
- A hozzászóláshoz be kell jelentkezni
Ha induláskor pozitív és páros az i, akkor nem végtelen ciklus.
- A hozzászóláshoz be kell jelentkezni
No es? Ezen kivul meg ezer fele elrontasi lehetoseg van, ami mind tanulsagos. Nem kell ezert aggodni. Vagy esetleg vegyuk ki a parancskeszletbol a dontott vonalat is, mert ha nulla erteku valtozo kerul a jobb oldalara, akkor meghal egy tunder? A programozas mar csak ilyen veszelyes szakma. :-)
- A hozzászóláshoz be kell jelentkezni
Persze, csak aki jó programozó, az próbálja minimalizálni ezeket.
- A hozzászóláshoz be kell jelentkezni
+1, a kérdésben a --*ptr mint "side effect" ez a szint, és egyértelműen
nem módosít valahol valami olyat, ami miatt már nem az fog történni, amire számít az ember.
Egyet tudok érteni azzal, hogy beleírják egy szabványba hogy ők ilyet nem adnak ki a kezükből/nem fogadnak el a partnerektől, de ha az embert nem köti az adott szabvány, akkor el tudja dönteni hogy mi a jobb megoldás. A
if (ptr && *ptr && --*ptr == 0) timeout();
jobb, mint szétszedni zárójelekkel együtt 11 sorba, olvashatóbb, egyszerűbben látszik hogy jó, és sokkal kevesebb elírásra ad lehetőséget.
- A hozzászóláshoz be kell jelentkezni
Nem a juniorok agyáról van szó, hanem arról, hogy miért vegyítesz összehasonlítást végző kódot olyannal, ami adaton machinál.
oldani egy ertelmes kommenttel.
Komment oda kell, ahol valami smelly vagy nem triviális dolog van. Ha meg valamit meg lehet írni triviálisan is, akkor azt úgy kell megírni. Ilyenkor hol van a sokat hangoztatott Keep it simple, stupid elv?
az ipar mar megszulte a megnyugtato valaszt: ugy hivjak, hogy teszteles. :-)
Vagy inkább azt, hogy don't do it. Ld. MISRA C.
- A hozzászóláshoz be kell jelentkezni
A programozas lenyege, hogy vegyited a az osszehasonlitast vegzo kodot azzal, ami az adaton machinal. Ez by design ilyen.
A kerdes cdak az, hogy ezt egy zarojelen belul teszed, vagy kiveszel onnan reszeket?
De ha gondolod, akkor huzzuk karoba azt, aki nem atall egy zarojelen belul letrehozni egy valtozot, inicializalni, valtoztati, es feltetelesen ellenorizgetni? Itt egy pelda erre a felettebb trukkos, es ordogtol valo dologra:
for (register byte i=0; i<42; i++) {...}
Tenyleg az az ajanlas, hogy ez legyen harom sor? Szerintem eleg, ha csak az elborultabb esetek eseten vagjak nyakon a programozot, es a trivialis eseteket megengedik neki. Nehogymar antipattern legyen egy for ciklus, banyek.
- A hozzászóláshoz be kell jelentkezni
OT:
3 sorból amúgy se úszod meg, mert akkor nem lesz lokális változó: sztem minimum 5. (ha elhagyod a külső { } -eket, akkor maradt egy i változód)
{
register byte i = 0;
while ( i < 42 ) {
...
i++;
}
}
Ráadásul ezzel a formával vacakolni kell, mert máris nem lehet egy jó kis continue a ciklusmagban - ellentétben az eredeti for-ral. Szóval ez minden szempontból rosszabb, mint az átírás előtti forma.
/OT
De gyaníthatóan nem pont erre gondoltak.
- A hozzászóláshoz be kell jelentkezni
Rust-ból, mint C alternatívára törekvő modern nyelvből például száműzött ez a for() univerzalitás.
Így itt nem tudsz leírni ilyet: for (int i=5, j=3, k=4; a[i] && b[j++] == '\r'; i-=2*j, k+=2) { ... }
Ami lehetőséged van, az kb. ennyi:
for normál növekménnyel: https://rust.godbolt.org/z/zWzE7PjW4
for esetleg i+=3 módon: https://rust.godbolt.org/z/3o8af6q1f
for amolyan foreach jelleggel: https://rust.godbolt.org/z/qTxoManxf
erdekességképpen ugyanez iterátorral megvalósítva: https://rust.godbolt.org/z/Ecn9zrePh
- A hozzászóláshoz be kell jelentkezni
Szegenykeim. Nem irigylem oket. Szar lehet eletlen kessel dolgozni, de persze ertem, hogy magas nyelvi formakat akarnak hasznalni. :-)
En konkretan meg bash parancsorban sem szegyellem hasznalni a C tipusu for ciklust. Szerintem tokeletes kompozicio, benne van minden lenyeges, es semmi sallang. Ugyan mi egyeb kivanalmam lehetne meg ezen kivul?
- A hozzászóláshoz be kell jelentkezni
Ez mondjuk nagyon jó lenne, ha lenne C-ben: https://rust.godbolt.org/z/qTxoManxf
Sok programot igencsak tömörebbé, átláthatóbbá tesz.
- A hozzászóláshoz be kell jelentkezni
Nem vagyok benne biztos, hogy erre gondolsz, (szolj, ha mast kellett volna eszrevennem) de a foreach mar megjelent C++ 11 -ben, std::for_each neven. A foreach valoban kenyelmes, megsporolja a sizeof lekerest, es az index valtozoval sem kell torodni, igy tenyleg tomorebb a kod.
En amugy a yield -et hianyolom bash alol (C++ -ba mar beleheggesztettek) mert azt nagyon megszerettem C# alatt.
Van egy csomo jo cucc mindenfele nyelvekben, de szerintem nem veletlen, hogy ennyi nyelv van. Mind masban penge, es masban nyogvenyelos. A C/C++ eleg hardverkozeli, hogy ne kelljen ugy megvedeni a kezdoktol, hogy kiherelik a funkcionalitasat, a patternek tuleroltetesevel. Aki nem szeret sok parancsot latni egy sorban, az hasznaljon assemblert! :-)
- A hozzászóláshoz be kell jelentkezni
Mind masban penge, es masban nyogvenyelos.
Ez így van. Ez is egy komoly tervezési szempont, mikor melyik programozási nyelvekre építve kezdjünk egy projektet.
Gyakran egyazon projekten belül is több nyelvre támaszkodunk. Amelyik amire praktikusabb, na és persze képes benne programozni a csipet-csapat.
- A hozzászóláshoz be kell jelentkezni
Nem, a programozás lényege az, hogy megoldást nyújts a feladatra. Ezen kívül szokott elvárás lenni az, hogy a kód legyen lehetőség szerint minél inkább hibamentes, karbantartható, meg a többi.
És igen, a for is egy nagyon rossz konstrukció, pont azért, mert össze-vissza lehet kutyuni a cikkusváltozód. Nem véletlen, hogy más nyelvekből egyre inkább próbálják írtani.
- A hozzászóláshoz be kell jelentkezni
A C for ciklus egy borotvaeles kes. Veszelyes es hasznos, de szerintem hasznosabb mint amilyen veszelyes, szerinted pedig forditva. ;-)
A hibamentesseget en is sokra tartom. A topic nyito pelda sorban en latok egy hibat, amit eddig senki nem forszirozott. Mi van akkor, ha a ptr null?
Az elso valasz, hogy semmi baj nincs, hiszen az if elso tagja miatt nem cimzunk ra, hibas.
Ugyanis ez csak az ellen ved, hogy itt es most nem robbanunk fel, azonban egy ilyen kod eseteben mar elvarjuk, hogy a ptr mutasson valahova, marpedig, ha ez nem teljesul, akkor mar gondok vannak.
En ugy vagyok vele, hogy a fontos if -eknek ki kell irni az else agat. Ez a mentalitas szerintem tobb hibatol ov meg, mint az, hogy dontesi blokkba teszunk adatmodositast.
Ha szetszednem tobb sorba, akkor emiatt lenne, hogy kezeljem a pointer null erteket is, es ne soporjem a szonyeg ala. Mas miatt szerintem nem kell, ha itt a ptr uzemszeruen lehet null, mert mondjuk ezzel kezeljuk azt az esetet amikor a szamlalo ki van kapcsolva, vagyis nem kell vele foglalkozni, akkor teljesen jo ez igy.
- A hozzászóláshoz be kell jelentkezni
Akkor látom még mindig nem érted mi a baj a fenti iffel: az, hogy miért van összehasonlító műveletek mellett adatmódosítás? Miért van a kódban side-effect? Ha ezt megérted, akkor megérted, hogy miért rossz konstrukció a C-s if is.
- A hozzászóláshoz be kell jelentkezni
Már oda sikerült eljutnod, hogy maga a nyelv a rossz? Ja, mint egyesek szerint az is rossz, hogy az apa férfi, az anya nő. Szerintem meg ezek értékek, jók. Épp, mint a C-ben az if-en belüli adatmódosítás lehetősége, tudniillik ettől általános, s nem korlátozott a nyelv.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Nem nekem. hanem a szakmának, nem véletlen, hogy a modernebb nyelvekből egy csomó mindent kivágták a kukába, abból, amit a C tudott, bár azt elismerem, hogy ezeknél a vezérelv sok esetben az volt, hogy a C egy szar alkalmazásfejlesztő nyelv. Igaz, nem is annak találtak ki, hanem rendszerprogramozásra.
Ld. Tony Hoare is pl. "billion dollar mistake"-nek nevezte a null pointer megalkotását.
Az meg, hogy mit enged a nyelv: egy férfi segget is meg lehet dugni, főleg, ha tranyó és nőnek vallja magát és megengedi az illető. Egyesek szerint ez is mekkora érték mert éljen a diverzitás, de a nagy többség ezt még mindig csak szimplán buzulásnak hívja. (Szalmabábozhatsz még itt a woke szarsággal, de elég rossz ajtón kopogtatsz.)
- A hozzászóláshoz be kell jelentkezni
Arra utalok, hogy szerintem nagyon félre mentek a dolgok. Meg akarjuk védeni a programozót saját magától, mert feltételezzük, hogy hülye, ezért alkotunk vacak, nem általános nyelveket, de legalább oda a hatékonyság. Véletlenül se menjen a programozó assembly, vagy ANSI C közelébe, mert még baja esik szegénynek.
Ezzel szemben azt állítom, picivel jobban meg kellene tanulni programozni, s nem fércműveket alkotni, nem összeollózni innen-onnan dolgokat. Az internetről fogalmatlanul copy-paste-elés nem programozás.
A NULL pointer egy zseniális dolog. Tudod vele jelezni annak aktuális érvényességét, tudod ezt vizsgálni. Assembly-ben is bevett szokás, hogy kiveszel egy elemet a halmazból, s azt használod hibajelzésre, érvénytelenség jelzőnek, végejelzőnek. Mint pl. a C stringekben a '\0'.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Ja, annyira zseniális dolog, hogy a saját készítője mondja, hogy bocs, elkúrtam. Mindegy. Erősen úgy látom, hogy annyira oda vagy gyógyulva a mikrokontrollerekre, hogy el sem tudod hinni, hogy az absztrakció ellenére a generált kód még lehet hatékony. Vagy az, hogy a konkrét megvalósítás, vagy amit kifejezel a nyelvben szemantikailag az elváljon.
A meg lehet tanulni programozni dologra: ha jól emlékszem, pont te írtad, hogy sosem tanultad a C-t, hanem magadtól tanultad. Ennek tükrében teljesen vicces a "picivel jobban meg kellene tanulni programozni" kijelentésed, mert pont azok mondják, hogy ne írjunk ilyen kódot, akik picivel jobban megtanultak programozni. Esetenként részt is vettek egy-két programnyelv kifejlesztésében. Nem azért, mert hülyék lettek volna, vagy hülyékre akarták méretezni a dolgokat, hanem azért, mert rájöttek, hogy baromi sok problémát okozhat egy ilyen. 50 éve jelent meg a C, azóta rengeteget változott az informatika, egynéhány dologra már rájöttek az emberek.
Amit én látok az az, hogy juniorok általában örülnek, ha egyáltalán működik, amit csinálnak. Aztán jön a medior szint, ahol előkerülnek a mindenféle trükkös dolgok, nyelvi elemek, ahogy egyre jobban és magabiztosabban megismer egy nyelvet, technológiát. Aztán tapasztalataim szerint mire valaki elér a senior szinthez, elkezdi ritkítani a mindenféle "okos" kódot és a felesleges komplexitást. Mert szar karbantartani, és általában több a nyűg vele, mint a haszon. Nyilván mérlegel, hogy kell-e a plusz teljesítmény, vagy sem (már ha egyáltalán van, mert sokszor az okos kód valójában nagyon nem okos, csak a sok trükktől nem látszik, hogy egyébként szar). Architect szinten meg kb. tűzzel-vassal írtja. Kellemetlen dolog egy több millió eurót/dollárt kifizetni kártérítésekre vagy ne adj Isten, egy szoftverhibával elkövetett gondatlanságból elkövetett emberölés miatt rendőrségre járni. Sorolhatnék neked tonnaszám eseteket, szerintem egy jó részéről te is hallottál már. Persze mondogathatod tovább, hogy "jaj, oda a hatékonyság" meg "okosabb programozók kellenének".
Nem a dolgok mentek félre, csupán a szoftveripar fejlődik és jön rá dolgokra, hogy mi az ami nem jó. 50 évnyi tapasztalat meg nem kevés, főleg nem az informatikában, ami gyakorlatilag épp, hogy csak kialakult.
- A hozzászóláshoz be kell jelentkezni
Ó, persze, és ott tartunk, hogy olyan szintű az absztrahálás, hogy programozók nem értik, hogyan működik a számítógép, így aztán embedded környezetben meg vannak halva.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Aha, persze, mert ha valamit absztrahálsz, akkor hülye leszel tőle. Láttam én már CPU cache elérés optimalizálására illetve branching minimalizálására optimalizált kódot C#-ban. ¯\_(ツ)_/¯
- A hozzászóláshoz be kell jelentkezni
Nem leszel hülye tőle, csak elmúlik a hardware ismeretének igénye. Régen a villamosmérnök programozott, ma meg már boldog-boldogtalan, talán még az óvónéni is.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Ezzel szemben azt állítom, picivel jobban meg kellene tanulni programozni, s nem fércműveket alkotni, nem összeollózni innen-onnan dolgokat. Az internetről fogalmatlanul copy-paste-elés nem programozás.
Szerinted meg tud valósulni, hogy a programozó csapat SOHA ne hagyjon el, illetve SOHA ne implementáljon tévesen egy alapvető ellenőrzést?
Én még mindig a fordítottjában hiszek: alapból a rendszer ellenőriz és ahol az optimalizáló feleslegesnek látja, ott a rendszer elhagyja az ellenőrzést.
Nézd meg ezeket a példákat és a belőle generált assembly-t:
https://rust.godbolt.org/z/6KMzPoj1f
Ahol az 1000 helyből mégsem kezel le a fejlesztőcsapat 1-et, ott a rendszer biztosítja hogy ne férhessen hozzá a szoftver a változón kívüli memóriaterületre.
- A hozzászóláshoz be kell jelentkezni
Nyilván nem. Én is szúrtam már el, de semmi baj, kijavítom a hibát.
ott a rendszer biztosítja hogy ne férhessen hozzá a szoftver a változón kívüli memóriaterületre
Félek, hogy ez csak futásidőben ellenőrizhető, s akkor elrontottuk a sebességet meg a memóriafelhasználást is.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Én is szúrtam már el,
És itt válik el a történet, van aki megvonja a vállát és van, aki próbálja kerülni azt, hogy olyan kódot írjon, amit el lehet szúrni.
de semmi baj, kijavítom a hibát.
És az okozott károkat is? Könnyű egy szoftverhibát csak úgy, önmagában nézni és eltekinteni attól, hogy az a szoftver, valahol, valamilyen körülmények közt futni fog, emberek használják majd.
s akkor elrontottuk a sebességet meg a memóriafelhasználást is.
Ezen kívül létezik még jó pár más fontos tényező és elvárás egy szoftvertől is.
- A hozzászóláshoz be kell jelentkezni
És az okozott károkat is?
Miért ír az ember bootloadert? Unaloműzésként, vagy azért, hogy távoli gépen firmware-t lehessen cserélni?
Ezen kívül létezik még jó pár más fontos tényező és elvárás egy szoftvertől is.
Persze, csak ha másodpercenként hárommillió mérési eredményt kell valós időben előfeldolgozni valamiképpen, akkor hirtelen mégis ez válik a legfontosabbá.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Szerintem szinte mindegyik nyelvi elem veszélyes, de a használatuk elkerülhetetlen.
Az if, mert elfedhet egy hibát else ág nélkül. Mégis gyakran látom így.
A ciklus, mert végtelen lehet, akárhogy is írod. Mégis kell.
A rekurzivitás, mert a végtelenbe mehet, mégis használják, mert elegáns.
Az osztás is veszélyes, mert nullával felrobban, de mégis gyakran elosztunk minden marhaságot.
A goto mert mindenki üldözi, pedig az apple féle goto fail sem a goto hibája volt.
A longjump is ördögtől való, bár megkérdezném a magas nyelvek művelőit, hogy a kivételkezelést hogyan oldják meg nélküle?
A poiterek, referenciák is gyakran okoznak gondot. Adjunk át mindent érték szerint? Juj, de jó lenne!
Tudsz olyan utasítást, amivel nem lehet galibát okozni? Mert én nem. Még egy összeadás is okozhat túlcsordulást.
Igen, a C tipusú for egy veszélyes eszköz. Pont mint minden más parancs. Viszont a a funkciókészlete nagyon széles felhasználási kört enged meg. Nem kötelező bolond módra használni, azonban lehet vele elegáns tömör kódot írni, ami segít a csökkenteni a hibákat, mert az ember könnyebben azonosít egy gyakran használt rövid nyelvi elemet, mint ugyanazt, de sokkal több sorban leírva. Egyszerűen több figyelem juthat így a lényegi részre.
Igen, a C nyelv szintaxisa megengedi az adatmódosítást a feltételvizsgálaton belül, mert ez egy gyakran használt eszköz. Mint egy éles kés a fiókban. Én gyakran vágok kenyeret, húst, zöldséget, hadd ne kelljen mások félelme miatt ilyen elemi dolgok miatt elballagnom a boltba ahol van kenyér meg hússzeletelő masina.
Tényleg félünk egy " while (--i) " kifejezéstől? Tényleg félünk egy " for (i=200; i<=400; i++) " kifejezéstől? Ezeknél mi a side effect? Miért félnek tőle más programnyelvekben? Felnőtt már egy-két generáció, akik elég magabiztosan voltak képesek használni a C for ciklust a mindennapokban. Mikor vált veszélyforrássá? Nem lehet, hogy az éles késeket használni nem képes embereket kéne korlátozni, és nem a kés gyárakat gonosznak beállítani?
Nekem továbbra is az a véleményem, hogy a tévedésre, hibára lehetőséget adó nyelvre, vagy nyelvi elemre nem az a válasz, hogy "Tiltsuk be!", vagy hogy "Ne használjuk!", hanem a képzés, hogy hogyan használjuk, és a megfelelő tesztelés.
- A hozzászóláshoz be kell jelentkezni
Nekem továbbra is az a véleményem, hogy a tévedésre, hibára lehetőséget adó nyelvre, vagy nyelvi elemre nem az a válasz, hogy "Tiltsuk be!", vagy hogy "Ne használjuk!", hanem a képzés, hogy hogyan használjuk, és a megfelelő tesztelés.
A "ne használjuk" meg a "hogyan használjuk" az ugyanaz, csak más a tárgy. Ha valamire megmondod hogy hogyan kell használni, akkor az ekvivalens valami másnak a nem használatával, és fordítva: ha valaki megmondja hogy mit ne használj, az ekvivalens valami másnak a hogyan használatával.
Miért félnek tőle más programnyelvekben? Felnőtt már egy-két generáció, akik elég magabiztosan voltak képesek használni a C for ciklust a mindennapokban. Mikor vált veszélyforrássá?
A for ciklusnál tömörebb, olvashatóbb, hibabiztosabb és egyszerűbben összefűzhető a kód többi részével a map/filter/fold/foreach.
- A hozzászóláshoz be kell jelentkezni
Gyűlölöm azokat a nyelveket, amelyekben van foreach. Szerintem sokkal gondolkodósabb. Ilyen nyelvekben is inkább for-t vagy while-t használok. Egyszerűbb, átláthatóbb, jobban megmutatja, mi történik, közelebb van az assemblyhez.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Én kedvelem a foreach -et. Amikor nincs más igény, csak végiggyalogolni a tagokon, akkor kényelmes. Azon belül is van break, continue, szóval az esetek többségében teljesen megfelelő. De amikor másra van szükség, például egy tag olvasása után tudom, hogy a következő érdekes tag nem a következő, hanem mondjuk tízzel odébb lesz, akkor a for jobb, mert abban növelhetem a ciklusváltozót, és máris ott vagyok. De az ilyen ritka.
- A hozzászóláshoz be kell jelentkezni
Gyűlölöm azokat a nyelveket, amelyekben van foreach.
Es errol mit gondolsz?
struct whatever
{ struct whatever *prev,*next;
...
};
...
struct whatever *first,*something;
first=whatever_new();
something=whatever_new();
list_add(first,something);
...
struct whatever *curr;
for ( curr=first ; curr != NULL ; curr=curr->next )
{ whatever_function(curr);
...
}
Szoval ki-ki eldontheti hogy ez most mennyire foreach es mennyire for :)
- A hozzászóláshoz be kell jelentkezni
Azt nem mondtam, hogy nem lehet olyan konstrukciót csinálni, amely végigmegy egy pointerekkel láncolt listán az egyes elemeken, struktúrán. Sőt, akár valamilyen szűrés szerint lehet egy olyan láncolást is csinálni, amelyben csak azok az elemek vannak, amelyek megfelelnek a szűrés feltételeinek.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Kb ez (lehet) a gyakorlati kulonbseg: a for() az egy lista indexein halad vegig, a foreach() meg a lista elemein. Gyk. egy indirekcios szinttel lejjebb. Olyat ugye ritka(bba)n csinalunk, hogy:
for ( i=0; i<i_max; i++ )
{ function_something(whatever,i);
}
es inkabb ez a gyakoribb:
for ( i=0; i<i_max; i++ )
{ function(whatever,something[i]);
}
Es ezutobbi mar inkabb a foreach()-hez all kozelebb, mintsem a for()-hoz a gyakorlati alkalmazast illetoen.
- A hozzászóláshoz be kell jelentkezni
Igen, de technikailag nekem szimpatikusabb, ha az index a ciklusváltozó. Lehet ez a második konstrukció, legalább látszik, mi történik.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Ha az indexet nem használod semmire a ciklusban, pl. olyan a ciklus, hogy
for(int i =0; i < v.size(); i++){
do_something(v[i]);
}
akkor az index olyan, mint egy feleslegesen csupaszon hagyott vezeték, csak arra jó, hogy figyelni kelljen rá, hogy nehogy megfogd, pl. nehogy i-nek nevezz egy belső ciklusban egy változót. Nem sokkal szebb ennél, hogy
for(auto vElement = v.begin(), vElement != v.end(); vElement++){
do_something(*vElement);
}
amiben a ciklusban benne csak az a változó létezik, amit használsz is, ráadásul, ha később rájössz, hogy nem array az optimális konténer, hanem láncolt lista, akkor csak az elején kell átírni. Tudom, hogy ez C++, de simán emulálható C-ben, és még overheadje sem lesz, ha nem egy kalap szar a compiler elintézi.
Nyilván nem azt akarom mondani, hogy ne legyen a nyelvben normális for ciklus, van amire az az optimális, van amire ez. Az a jó, ha van választék.
Azt sem mondom, hogy embedded rendszerre így kell megírni, vagy C++-ban kell megírni, ahol egy függvényhívás is megengedhetetlen overhead, az más speciális eset. De van a másik véglet is, amikor nyugodtan feláldozható pár százalék CPU-idő azért, hogy csökkentsük a hibázás valószínűségét, vagy hogy olcsóbb legyen majd 15 év múlva továbbíratni a programot valaki mással.
- A hozzászóláshoz be kell jelentkezni
Épp az szokott zavarni, hogy eltüntetjük az indexet. Akkor előbb-utóbb kell az elejére egy i = 0; a belsejébe egy i++; mert szinte biztos, hogy számolnom kell az elemeket, mert hivatkozik valami másra, ami az iterációs indextől függ. Akkor nem nyertünk semmit, csak duplikálva lesz. Előlem elfedődik a ciklusvátozó, ami van, csak nem látszik, majd én létrehozom láthatóan még egy példányban. Pazaroljuk a RAM-ot, futásidőt, ráadásul utálom, ha valami kettős módon van könyvelve, rossz a tudat, hogy ezek szétcsúszhatnak. Például egyszerre pointerrel és indexszel.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Mondanék egy egyszerű példát: az a feladat, hogy keressük meg a tömb maximumát. A naív megoldás (a példa kedvéért PHP, mivel ott kétféle foreach van):
$max= false;
foreach ($arr as $ai) {
if ($max===false || $ai>$max) $max= $ai;
}
A nem naív megoldás az, ha rögtön visszakérdezünk, hogy 'és ha egynél több helyen veszi fel a maximumot, az oké?' ugyanis jó eséllyel az lesz a válasz, hogy 'hát... hát-hát... no igen, akkor inkább legyen az eredmény egy újabb tömb, ami a maximális elem(ek) indexeit tartalmazza'. Na ekkor kezdünk el kódolni, hogy pl.:
$maxp= [];
foreach ($arr as $i => $ai) {
if ($maxp==[] || $ai>$arr[$maxp[0]]) $maxp= [$i];
else if ($ai==$arr[$maxp[0]]) $maxp[]= $i;
}
Persze nem mindig derül ki ez elsőre, ilyenkor csak az ösztönünkre hallgatva kezdjük rögtön egy olyan változattal, amit kevesebb átalakítással lehet fejleszteni, pl.:
$maxp= false;
foreach ($arr as $i => $ai) {
if ($maxp===false || $ai>$arr[$maxp]) $maxp= $i;
}
$max= $maxp===false? false: $arr[$maxp];
- A hozzászóláshoz be kell jelentkezni
Ha szakítunk a C szintaktika maszirozásaival és for ciklus helyett például Rust iterátor szintaktikával esünk neki, az így néz ki:
Maxérték:
https://play.rust-lang.org/?version=stable&mode=release&edition=2021&gi…
Ez nem tűnik erőforrás pazarlónak a for ciklusos iránnyal összevetve.
https://rust.godbolt.org/z/G4cEq13cv - méretre optimalizálva
https://rust.godbolt.org/z/a73sf34av - sebességre optimalizálva
A folytatás, ahol az egy vagy több maxérték pozícióját kell meghatároznunk, iterátorral is bonyolódik:
https://play.rust-lang.org/?version=stable&mode=release&edition=2021&gi…
- A hozzászóláshoz be kell jelentkezni
De ez igazából párhuzamosítható feladat, mint az összeg, maximum kiválasztás, rendezés stb: két részre osztom, megoldom a problémát mindkét részen, kapok (maxval_1,[keys]_1) (maxval_2,[keys]_2) és innen már értelemszerű. Vagy n részre osztom. Vagy ahogy a legkezelhetőbb algebrailag.
Keresgéltem hogy minek kell teljesülnie egy rekurzív, párhuzamosítható függvényre hogy valami képes legyen magától párhuzamosítani, de nem találtam semmit, talán még nem tart itt a tudomány.
(És akkor a probléma megoldása is csak ennyi: a két/n részre osztásos rekurzió több algoritmusnál előjön, hasznos, biztosan valaki megírta már, és akkor csak azt a részt kell megírnunk nekünk hogy mivel térjen vissza f ( (maxval_1,[keys]_1) , (maxval_2,[keys]_2) ) . )
- A hozzászóláshoz be kell jelentkezni
És itt jön be a másik kérdés: a maximumkeresés vajon mennyire gyorsítható a gyakorlatban?
Elvileg van 8 magod, 8-szorosára gyorsul kockáspapír-ceruza szerint.
Gyakorlat viszont keserűbb. Az ilyen egyszerű, egyetlen összehasonlítás és lépünk a következőre mert nem nagyobb, szóval itt a CPU mag helyett gyakran a RAM tempó lesz a valódi korlát.
Valójában nehéz dolog néha optimalizálni. Jelfeldolgozásnál ARM NEON esetén és gyakran az x86 AVX használatakor a RAM tempója fogja vissza az algoritmusaimat, a CPU-ban lenne még kraft.
- A hozzászóláshoz be kell jelentkezni
for(auto vElement = v.begin(), vElement != v.end(), vElement++){
do_something(*vElement, vElement - v.begin());
}
Nyilván úgy néz ki, mintha most elpazarolnánk egy kivonást, de a compiler feltehetőleg nem hülye, ha attól tényleg lassabb, majd elintézi, és a csupasz vezeték sincs ott.
- A hozzászóláshoz be kell jelentkezni
Engem meg épp az szokott zavarni, hogy minek van indexem, hogy ha egyszer nincs mit indexelni? Főleg, ha mondjuk nem egy tömböm van, hanem láncolt listán? Nem indexet akarok kezelni, hanem a gyűjtemény elemeivel dolgozni.
Egyébként a pazarlásról: anno olvastam egy nagyon kiváló cikket "mire optimalizáljunk?" címen. Ha jól emlékszem, a feladat egy tömb elemeinek szummázása volt, talán némi csavarral, csóka megírta többféleképp is, kezdve C-s fortól, C# foreach, linq, sum, stb.-ig, megnézve a generált ASM kódot is. Legmókásabb az volt, hogy a for-ra sikerült messze ráverni valamelyik felállásban, szimplán azért, mert volt más megoldás, ami a prefetchinget figyelembe véve gyorsabb kódot generált. Vagy egyes esetekben, mikor megpróbált optimalizálni a kódon, nem hogy javult, de romlott a teljesítmény. Mégpedig azért, mert a "frissen végzett egyetemista" kódot felismerte a fordító, arra ki volt optimalizálva, az "okos" kódra viszont nem, emiatt butább asm kódot generált.
Szóval ennyit ér az, hogy ha magad akarsz kezelni mindent: false sense of control.
Egyébként ha már ennyire szóba hozod a pazarlást: az egyetlen létező erőforrás, amit te eddig említettél az a hardver. Igen, megértem, hogy mikrokontrollernél ez fontos. Megjegyzem, nem csak ott, csak más súllyal. Mi pl. logisztikai optimalizáción dolgozunk. Nálunk kb. a legszűkebb erőforrás a fejlesztők száma és azok ideje. Munka van dögivel, fejlesztendő featurek sorban állnak. Viszont ezek a featurek a vevőinknek éves szinten több millió euróban mérhető üzemanyag-költség megtakarítást jelentenek vagy azt, hogy 5-10-15%-al kisebb flotta is elég a fuvarok elszállításához a jobb kihasználtság miatt. Tedd kérlek mérlegre azzal, hogy spórolnánk mondjuk éves szinten 1-2 ezer eurót valamelyik cloud providernél. Különösen ajánlom végiggondolásra, hogy a szállítmányozás mekkora részt foglal el a gazdaságban és milyen hatása van a környezetre is.
Néha egy kicsit érdemes kihúzni az ember fejét az assembly kódból is kicsit a kontextust is nézni, hogy hol, mikor milyen környezetben van használva az ember munkája.
(Btw. ha valakinek megvan a link a cikkre, akár internet archiveből, légyszi linkelje, évek óta keresem, de szerintem konkrétan a domain is megszűnt már. Valamilyen Zoltán volt azt hiszem a szerző, alapvetően C#-os témákkal foglalkozott.)
- A hozzászóláshoz be kell jelentkezni
Néha egy kicsit érdemes kihúzni az ember fejét az assembly kódból is kicsit a kontextust is nézni, hogy hol, mikor milyen környezetben van használva az ember munkája.
Igen, de ez fordítva is igaz, mert itt talán többen is olyan szemszögből kommunikáltok, mintha csak szerverre meg PC-re objektumorientált kód írásáról lenne szó, ahol CPU és RAM van rogyásig, ráadásul mindegy is, mennyi idő alatt készül el, mert aszinkron, s van alatta oprendszer. Én ezzel szemben oprendszer nélkül, MCU-ra, valós idejű feldolgozásról, natív C-ben vagy assembly-ben programozva, kevés programtárral, kevés RAM-mal, kevés gépi ciklussal megoldva beszéltem a feladatról, s embedded környezetben meg ez az igény. Az én munkám ilyen.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Miből gondolod, hogy nekünk időnként nem kell ugyanúgy mikro-optimalizálni dolgokat? Miből gondolod, hogy nekünk nincs ugyanúgy időkritikus feladat?
- A hozzászóláshoz be kell jelentkezni
Nem gondolom, mert nem tudom. :)
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
"A "ne használjuk" meg a "hogyan használjuk" az ugyanaz, csak más a tárgy."
Ezzel nem értek egyet, mert például itt: https://hup.hu/comment/2748983#comment-2748983 elhangzott, hogy a Rust nyelvben, mint C/C++ alternatíva nyelvből száműzték. Ha valamit nem tudok használni, akkor ott már nincs szó arról, hogy hogyan használjam.
- A hozzászóláshoz be kell jelentkezni
Nem az a baj, ha a kés éles, hanem ha a nyele is. (Amúgy szerintem az vele a helyzet, mint a GOTO-val: akkor kell használni, ha szükség van rá., Egy modern compilerrel nem egyáltalán nem biztos, hogy gyorsabb lesz valami attól, ha trükkösen írok meg valamit, mert elég jól optimalizálnak, úgyhogy olvashatatlan trükkös kódot elég akkor írni, ha egy profiling során kiderült, hogy ott lassú, ott van egy bottleneck. )
- A hozzászóláshoz be kell jelentkezni
Nem lehet, mert nincs olyan fogalom C-ben, hogy ciklusváltozó. Akinek nehéz a C, annak ott a C++, abban minden verziójában új eszköz van egy tömb/intervallum elemenkénti feldolgozására.
- A hozzászóláshoz be kell jelentkezni
És képzeld, vannak olyan elvetemültek, akik for-ban használnak vessző operátort, meg feltételvizsgálat több feltételre történik. Szörnyű! Mivé lesz így a világ?
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Aztán jöhet egy ilyen kódolási stílus, mert megengedi:
for (
előkészítés,
előkészítés,
előkészítés,
előkészítés,
;
feltétel &&
(feltétel || feltétel) &&
függvény1() &&
feltétel
;
ciklusvégi utasítás,
ciklusvégi utasítás,
ciklusvégi utasítás,
ciklusvégi utasítás,
függvény2(),
ciklusvégi utasítás,
ciklusvégi utasítás,
);
Végülis nem is kell a { ... } rész, ha nincs break és continue.
- A hozzászóláshoz be kell jelentkezni
Igen, ezt lehet csinálni. Nem mondom, hogy célszerű, meg azt sem, hogy átlátható, de miért ne, ha valakinek ilyen fura ízlése van?
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Rustban is tetszőlegesen sok kódot beírhatsz akármelyik kifejezés helyére. Ha van egy programod, meg van benne valahol egy kifejezés, akkor berakhatod az egész programkódot a kifejezésbe. Nem látom, hogy ez miért érdekes?
szerk: na jó, elgondolkodtam. Az járt a fejemben, hogy a for ciklusban az 1..length-et kicserélheted akármire, ami egy tömböt ad vissza, és közben olyan side effecteket hajt végre, amilyeneket nem szégyell. De ez talán pont rustban pont nincs így, például változók értékeit nem tudja megváltoztatni (?)
Mindegy, minden más nyelv megengedi, és a rust is megengedi, hogy a kifejezések helyére, ahova alapesetben 1, néha 2 dolgot írsz, ha akarod tetszőlegesen sokat írhass.
- A hozzászóláshoz be kell jelentkezni
változók értékeit nem tudja megváltoztatni
Igen, amit a C-hez képest megfordítottak:
- C-nél "pluszmunka" kitenni az a "const" kulcsszót. Alapból minden írható. Gyakran nem "pluszmunkázunk", jó az írható pointerként.
- Rust-nál "pluszmunka" kitenni a "mut" kulcsszót. Alapból semmi nem írható. Ahol kell, ott "pluszmunkázunk".
Egyúttal a Rust odafigyel arra, hogy kétszer nem tudod egyazon változót betenni a függvénybe, ha egyike írható és előrébb van. Sem a róla származó referenciát, stb.
Lásd: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist…
- A hozzászóláshoz be kell jelentkezni
Itt mindenki PC-re programozik? A const MCU-n egyúttal azt is jelenti, hogy flash memóriába, programtárba van allokálva az a valami, nem pedig RAM-ba. A RAM drága, mert kevés van belőle, ezen felül oda be kell másolni programtárból előbb, tehát futásidőben is rossz lenne, ha RAM-ban tárolódna. Sőt, ugyanúgy elfoglalná a helyet a programtárban, mert onnan lehetne RAM-ba inicializálni a konstanst. Szóval a const egészen más ahhoz képest, mintha nem írom oda.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Na egy újabb szépség: const ... és nem ugyanaz a const, amiről mi beszélünk. Mikrovezérlőn ugyanígy.
Van egy adathalmazod, amit egymás után több kiértékelőnek akarsz átadni. A kiértékelőket a csapat más tagjai írják, te csak headerfájlt kapsz.
uint8_t kiertekelo1(uint8_t *adat, uint8_t len);
uint8_t kiertekelo2(const uint8_t *adat, uint8_t len);
Melyik esetben lehetsz nyugodt, hogy a kiértékelőnek átadott 100 byte-os adathalmazod változatlan marad a függvényhívás után is?
(Implementáld ... változtasd meg mondjuk az adat[1] értékét és próbáld lefordítani.)
Rust esetén ugyanez egyébként így fest:
fn kiertekelo1(adat: &mut[u8]) -> u8 // ha nem módosítanád, warning-ol a fordító az indokolatlan "mut"-ért.
fn kiertekelo2(adat: &[u8]) -> u8 // "const", ha nem veszed a fáradságot a "mut" kiírására.
- A hozzászóláshoz be kell jelentkezni
Melyik esetben lehetsz nyugodt
Természetesen a második.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Önmagában ez a függvény-deklaráció nem biztosít semmit, a függvény belsőleg typecast-olhatja a pointert nem-const-ra. (Const ügyben érdekes lehet a C++ FQA 18. pontja is (A fejlődésnek hála újabban mobilon nem tudok URL-t bepasztázni. Ez bizonyára valamiféle biztonsági intézkedés. Vagy csak Nyomasek Bobó újabb sikere.))
- A hozzászóláshoz be kell jelentkezni
Végülis átcastolhatod (C-ben csináltam próbából pár éve én is ilyet) vagy akár inline assembly-ben is tutira írhatod a memóriát.
De ezek nem azok a műveletek, amit az adott függvényre végzett kód auditon olyan könnyen kimagyarázol.
Rust esetén szintén hasonló a helyzet, csak ott akad egy figyelemfelhívó unsafe kulcsszó is az alábbi mindkét esetben.
- inline assembly - ezzel tutira megírhatod, ahogy C-ben is.
- szintén cast-olhatsz: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist…
Már látom szemeim előtt a kód auditon a kérdést: "Miért nyúltál a "const uint8_t *data" / immutable &u32 alá?
Mi volt a célod, hogy mégis más értéket adj vissza, amikor megígérted hogy nem bántod?
Eleve miért nyúltál "unsafe" kulcsszóhoz? Unsafe kulcsszó nélkül tényleg megoldhatatlan a rád bízott feladat?
- A hozzászóláshoz be kell jelentkezni
Sőt, a módosíthatóságot cast-tal ráadni Rustban még a szokásos unsafe-es „vigyázz a saját algoritmusodra” figyelmeztetésnél is erősebb probléma. A fordító annyit optimál, hogy nem megbízható akkor sem, ha ez az egyszerű példa most épp működik. (mondjuk most nem sikerült 10 perc alatt hibás kódot prezentálnom, de korábbról emlékszem ilyenre).
Az UnsafeCell pont jó ide, arról tudja, hogy hogyan (ne) optimáljon.
https://doc.rust-lang.org/nomicon/transmutes.html
„Transmuting an & to &mut
is UB.”
https://stackoverflow.com/questions/54237610/is-there-a-way-to-make-an-…
C++ (17-től?...) is csinál olyanokat, amiről érdemes tudni alacsonyszintű programozáskor, pl. struktúrát konstans mező esetén optimálhat - ha a memóriában ábrázolt képe érdekel (rákasztolod valahova), akkor std::launder-t kell használni.
- A hozzászóláshoz be kell jelentkezni
Itt a HUP-on, vagy egyébként sem? Amúgy https://hup.hu/forumok/hup8bug2 itt elmondják, hogy nem támogatják a böngésződ, használj mást.
- A hozzászóláshoz be kell jelentkezni
Ez jogos, kiveve ha azert nem akarod a flash-ben tarolni mert annak az elerese lassabb... es kb architekturatol fuggetlenul megszakad akkor az instruction pipeline. Ezert emiatt jobb lehet ha pl #define-olod, vagy ilyesmi, mert akkor a fordito kioptimalizalja attol fuggoen hogy mire (sebesseg vs kodmeret) akarsz optimalizalni. Vagy nem minden esetben, mert pl pc-relativ ldr-re forditja... ha meg inline tolti be a konstanst akkor meg stall lehet a pipeline-ban... szoval ez azert egy nehez szakma :)
- A hozzászóláshoz be kell jelentkezni
Még ettől is rosszabb lehet a helyzet. ;)
Bár nem hangzott el konkrétan, de talán 8 bites PIC-ről lehet szól. Abban nincs pipeline, de van egy 24 bites (hardver) pointer, amivel a flash olvasható, de nem közvetlenül, hanem egy átmeneti regiszterbe. A pointer beállításával (általában 1-2 bájt) egy utasítással felolvashatod a a bájtot és dolgozhatsz vele. Az olvasó utasítás lehet pl. auto increment, tehát egy string elemeinek beolvasása is csak egy-egy utasítás. Ha a pointer interrupt alatt is használatban van, akkor törődni kell a mentés-visszaállítással még az átmeneti regiszter esetén is.
Szóval nem árt tisztába lenni azzal is, hogyan műkődik a szerkezet. Ezért van az, hogy aki több ezer vagy tízezer class-t használ, magassszintű programnyelven dolgozik - aminek szintén kismillió szabálya és side effectje létezik -, és mégsem boldogul a 30..80 assembly utasítással. Mert ott pontosan érteni kell mit csinál.
Vagyis a magasszintű nyelv jó, mert elfedi előtted a tudnivalókat. Ezért viszont olyan súlyos árat fizethetsz, amihez képest a "kiveve ha azert nem akarod a flash-ben tarolni mert annak az elerese lassabb" kismiska. Az mcu-ra készült C fordítók - kellő optimailzálás mellett is - 3-5x nagyobb és lassabb kódot generálnak. Ez sok esetben elfogadható, de még ennél is súlyosabb a helyzet! A C alapvetően nem ilyen architektúrára készült.
A C-hez itt hiányzik a sok regiszter , a szoftver stack, a lineárisan címezhető (és elegendő) memória, de még a vektoros interrupt is. Sok esetben érdemes a programot "vezérlés" és "szoftver" részre bontani. Az előbbi készüljön assemblerben, az utóbbi ne legyen érzékeny a sebességre.
- A hozzászóláshoz be kell jelentkezni
PIC32MZ, ez talán valami ARM core.
Valaki írta az optimalizálást. -O1 még free, efölött viszont már pénzes a licensz.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Bizony, annak csak a neve PIC. :(
Viszont nem kell két pécét kötnöd a műszerhez! :-D
Ezzel a kategóriával valószínűleg nem fogok dolgozni. A pic18-ig megy a -O3, ami már szinte olvasható kódot fordít. ;)
A fordítóknak igen jó az ára. Ha belegondolok, 1995-ben vettem C fordítót AIX-re 160 eFt-ért. Ahoz képest a $2900 az XC32-ért soknak tűnik.
- A hozzászóláshoz be kell jelentkezni
PIC32MZ, ez talán valami ARM core.
Pontosabban a PIC32 az MIPS architektúra. MIPS cég ugyanúgy licenceli a dolgait, mint az ARM, csak kedvezőbb áron. Az ARM-hoz hasonlóan a mikrovezérlős ág mellett szintén van alkalmazásprocesszor ága is. Ez utóbbival találkozol rengeteg SOHO hálózati eszközben. Olyanokban, amikre előszeretettel rántunk OpenWRT-t.
root@OpenWrt:~# cat /proc/cpuinfo
system type : MediaTek MT7621 ver:1 eco:3
machine : Ubiquiti UniFi nanoHD
processor : 0
cpu model : MIPS 1004Kc V2.15
BogoMIPS : 581.63
...
...
isa : mips1 mips2 mips32r1 mips32r2
...
Kár egyébként a MIPS-ért. Kb. 2 évtizede megrekedt a fejlődésük. Kb. azidőtájt, amikor ARM a modern ARM Cortex mikrovezérlőivel és alkalmazásprocesszoraival előrukkolt.
És hogy miért találod a MIPS-et az eredeti Microchip termékágban és miért a hálózati eszközökben? Ahogy imént írtam, a licencelése kedvezőbb az ARM tarifánál.
A harmadik versenyző még a pályafutása elején áll, ez pedig a RISC-V architektúra. Ez még leginkább csak bontogatja a szárnyait, de ígéretes versenyző.
Szerencsére a GCC és CLANG egyaránt fordít ARM, MIPS, RISC-V (mikrovezérlő, alkalmazásprocesszor 32/64 bit) magokra egyaránt. Benne van az Ubuntu disztróban mindhárom architektúrára a fordító.
Sőt a Rust is fordít rájuk. Alábbi parancs mutatja a listát mindháromra. Benne van mindhárom architektúrára való fordítás.
$ rustc --print target-list | grep -E 'arm|mips|riscv'
További hasznos eszköz amikor a fejlesztői lapka nincs nálad:
$ apt-cache search qemu-system | grep -E 'arm|mips|riscv'
qemu-system-arm - QEMU full system emulation binaries (arm)
qemu-system-mips - QEMU full system emulation binaries (mips)
RISC-V emuláció úgy néz ki, az új QEMU-ban már van: https://wiki.qemu.org/Documentation/Platforms/RISCV
- A hozzászóláshoz be kell jelentkezni
Még ettől is rosszabb lehet a helyzet. ;)
Ez ketsegtelen :) Mindig van lejjebb!
Bár nem hangzott el konkrétan, de talán 8 bites PIC-ről lehet szól
Konkretan AVR es ARM-nal is igy van, de barhonnan is nezzuk, ez sztem architekturatol fuggetlen allitas. Talan nativ CISC-nel lehetnek olyan implementaciok ahol kevesse szamit (regi klasszikus x86, 386, 486). Pelda: ARMv6-M-nel (lasd: Cortex-M0) 3 felekepp is betoltodhet egy konstans, az erteketol fuggoen: https://godbolt.org/z/rYn9zzv3b. A masodik es harmadik kozotti kulonbsegbol latszik hogy egy 8-bites konstans betoltese + egy kis matek meg mindig gyorsabb mint egy 32-bites konstans flash-bol valo betoltese. Raadasul az lr vermelesnek kesleltesvel/felcserelesevel a meg a konstans betoltes + matek kozotti stall-t is probalja minimalizalni a fordito ;) Viszont 3 darab 8-bites szam matekjabol mar lassabb oszetenni a 17+ bites konstanst mintha (varhatoan) flash-bol toltenenk be.
A C-hez itt hiányzik a sok regiszter , a szoftver stack, a lineárisan címezhető (és elegendő) memória, de még a vektoros interrupt is. Sok esetben érdemes a programot "vezérlés" és "szoftver" részre bontani. Az előbbi készüljön assemblerben, az utóbbi ne legyen érzékeny a sebességre.
Ez is azert erosen architekura-fuggo allitas. Peldaul ARM-ben siman meg tudod irni a _start-ot is C-ben, pont azert mert a Cortex-Mx garantalja hogy egy system reset utan elobb tolti be az xSP regisztert a vektor tablabol mint ahogy a futtatast elinditja. Es ennek az architekturalis felepitesnek pont ez is volt a celja hogy mar bare metal programokat is tudjunk tisztan C-ben irni ;) Hasonloan, a C ABI is illeszkedik a hardveres interrupt entry/leave felepitesehez: azaz az ISR-ek is ugyanolyan C fuggvenyek mint barmi mas, azt leszamitva hogy persze void (*)(void); tipusuak lehetnek csak. Mindezt kizarolag annak koszonhetoen hogy az interrupt entry lementi az {r0-r3,r12,lr,pc,xPSR} regisztereket, a C ABI meg pont az {r4-r11} elmenteserol kell gondoskodjon, barmilyen fuggvenyt is irunk.
Mas architekturaknal (pl AVR-nel) mar valoban figyelned kell arra hogy a _start az tartalmazza az SP inicializalasat, illetve az ISR-ek nem csak ugy siman fuggvenyek hanem specialis fuggvenyek (ld. ret vs. reti, naked attributumok jelentosege, stb). Itt mar nyilvan nem usszuk meg az asm-et, akkor sem ha akarnak, foleg a _start eseten - es ez valoban igy van jol. Es vsz PIC-nel is igy van, de azt nem ismerem sajnos egyatalan.
- A hozzászóláshoz be kell jelentkezni
No jó, locsemege bevallotta, hogy a mikrokontroller == MIPS32M, 252MHz, 5 stage pipeline, 32 bit CPU+DSP+FPU. Ez olyan, mint két pécé, 10 speciális kártyával, szupergyors összekötésekkel. Kiválóan alkalmas "szoftverrel történő hajtásra".
Amiről én írtam - ezt nem tudván - jóval kisebb szerkentyűk: PIC10..18, PIC24 és dsPIC - az utóbbiak 16 bites illetve DSP képességekkel felruházott mcu-k. Ezekben az a jó naaagy szoftver képességek előtt, hogy igen nagy választékban gyártják speciális, uniformizált perifériákkal. Ugyanezeket a perifériákat rakják a vásárolt MIPS core köré is - ennyi az átjárhatóság.
A 32 bites nagyágyúk másra valók. Ha olyan feladatod van, amit hardverrel kell megvalósítani, arra a PIC16/18 (14 és 16 bitess utasítás szélesség) eszközök kiválóan alkalmasak. A rengeteg beépítet perifériát "összekötözgeted" - persze programból - és szinte tetszőleges hardvert kialakíthatsz. A kialakított szerkezetet elindítod és teszi a dolgát. Utána alláthatod szoftverrel, kommunikációval is.
Csak egy példa a seciális perifériára a PIC16F1615-ben a Math Accelerator, ami nem más, mint egy 35 bites PID DSP. Felprogramozod és kész is van mondjuk egy precíziós motorvezérlés - amihez az összes egyéb elemet is megtalálod a tokban.
- A hozzászóláshoz be kell jelentkezni
Írtam, ömlenek be mérési eredmények - illetve ez az MCU mér, DMA-zik egyúttal -, majd valós idejű feldolgozás kell, de azért menjenek még soros buszok, USB, timer-ek, toronyóra lánccal.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Akkor azt is áruld el, hogy hány csatornán milyen sebességgel kell mérni!
- A hozzászóláshoz be kell jelentkezni
Túl IRL, többet nem mondok róla.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
hogy a mikrokontroller == MIPS32M, 252MHz, 5 stage pipeline, 32 bit CPU+DSP+FPU.
És ez még közel sem durva cucc. Nekem az STM32H757 a kemény mikrovezérlő. Ez a termék tartalmaz egy 480 MHz-es ARM Cortex M7 mikrovezérlőt + egy 240 MHz-es ARM Cortex M4 mikrovezérlőt a tokjában.
Hardver float már ARM Cortex-M4 tartozék, de a benne levő ARM Cortex M7 mikrovezérlő miatt már hardver double is van.
De ennél még komolyabb ARM mikrovezérlő fog jönni, amibe beleteszik a Heliumot is, ezáltal mikrovezérlővel még összetettebb valósidejű számítást (még több adattal futó jelfeldolgozás, szabályozókörök) el tudunk végezni. A Helium az ARM Cortex M55 alapokon gyártott mikrovezérlőknek lehet opcionális része, itt látható többféle kombinációban.
- A hozzászóláshoz be kell jelentkezni
Szerintem ez rovid es tomor, de osszessegeben nagyon rosszul kovetheto/olvashato. Tul sok a hibalehetoseg refactor eseten, es nem szembeotlo, hogy modositas tortent.
Mondjuk nem hasznalok c/c++-t munkam soran, de egy ilyen MR-t a jelenlegi tudasommal visszadobnek.
- A hozzászóláshoz be kell jelentkezni