C Kezdő; láncolt lista, bináris fájlműveletek, SIGSEGV hiba

Fórumok

Sziasztok!

A segítségeteket szeretném kérni az alábbiakban, nem tudom debuggolni ezt programot( még, bízok benne, hogy segít valaki :) ) :
A feladat adott, nem túl bonyolult:
https://pastebin.com/sGw7mcae

A debugger ezeket dobja:
https://imgur.com/a/AkmF6

És végül maga a kód:
https://pastebin.com/RkTdjtBL

Ami érdekessé teszi a dolgokat, hogy egyszer működik a program, egyszer nem, látszólag a második adattal nem bírkózik meg, de az sem mindig, 8GB rammal lefut, 4GB rammaé nem akar.

Örök hála, ha valaki veszi a fáradtságot, hogy rápillant. :)

Hozzászólások

foglalkoztam vele 3 percet.
Ez alatt próbáltam lefordítani, nem sikerült. Okok:
- c kód szerű dolog, c++ fordítóval lefordulna ha tippelem (struct jelölések elhagyása szerintem csak ott működik, c11< -nél nem)
- A "ct_rec -> id" -ért kb sikítófrászt kapott mind a fordító, mind az IDE (mind én)
- vegyes indentálás, amitől egy spagettivé változott az egész

Amire nekünk és neked is szükséged van: elkezded darabokra szedni a kódot és részenként megnézni hogy helyesen működik-e, mert nekünk ebben a formában lehetetlen, te mivel benne vagy még átlátod. Valószínűleg valami banális dolog lesz, de ebben nem fogjuk kiszúrni így

// Happy debugging, suckers
#define true (rand() > 10)

mert act_rec -> id helyett inkább act_rec->id kellene.
Ugyan a nyelv megengedi hogy az operátorokhoz szóközt tegyél, de ez attól még nem lesz helyes (mint ahogyan az sem a szabvány része hogy új sort kell kezdened és indentálnod, írhatsz mindent egyetlen sorba is, de ugyan úgy nem lesz helyes).
Az act_rec->id valójában egy (*act_rec).id szebb formában, és ha már a szépség okán ad a nyelv egy operátort a kezedbe, akkor célszerű arra használni :)
A logikailag összefüggő dolgokat pont ezért írjuk össze, dereferálhatsz így is: * act_rec, vagy így: *act_rec
Az előbbinél miközben szemmel parse-olod a kódot, az act_rec -et szorzod valamivel, a másodiknál pedig dereferálod (hiába a kód szerint mindkettő dereferálás lesz)

// Happy debugging, suckers
#define true (rand() > 10)

> Ugyan a nyelv megengedi hogy az operátorokhoz szóközt tegyél, de ez attól még nem lesz helyes

???

De, helyes lesz. Attól, hogy szerinted a szellősebb kód helytelen, másoknak nem feltétlenül ez a véleménye. Ha egy '->' helyett '- >' van írva, az helytelen.

Amit mondasz, az véleményem szerint analóg azzal, hogy mivel a szorzás magasabb precedenciájú, mint az összeadás, ezért ha ( a * b ) + ( c * d ) van leírva, az helytelen, mert a helyes az az lenne, hogy a * b + c * d. Illetve igazából még ez is helytelen, mert az a*b+c*d lenne a helyes. Pedig nem.

Bár jobban belegondolva, lehet hogy igazad van. Végül is nem mondtál semmit arról, hogy milyen szabályok alapján mondasz valamit helyesnek vagy helytelennek. Ha az a szabály, hogy a fölösleges szóközök létezése definiál valamit helytelennek, akkor akár igazad is lehet. Persze akkor meg miért mondod, hogy a szintén fölösleges soremelések nem létezése is helytelenné változtat valamit. Főleg ha hozzávesszük, hogy szerinted az indentálás hiánya is helytelen.

Asszem értem: amit írtál, a szerint a szabályrendszer szerint a szóköz operátornál fölösleges, indentálásnál kötelező. (Már csak az nem hagy nyugodni, hogy mi a helyzet a tabulátorral. Meg hogy véleményed szerint a vessző operátor használata helyes, vagy helytelen. Pl egy ilyen: for ( i=1, j=0; i > j ; i++, j++ ) { itt-pedig-csinálok valamit-i-vel-és-j-vel } )

=====
tl;dr
Egy-két mondatban leírnátok, hogy lehet ellopni egy bitcoin-t?

C style guide kifejezésre keresve, a találatok közt nem láttam olyat, ami a fenti esetben elfogadná a szóközöket a -> körül.
Szóval én elfogadom, hogy igaza van és ez így nem igazán jó.
Más kérdés, hogy az Ubuntu 16.04 saját C fordítója (gcc) ezt nem fordítja le C kódként, teli lesz hibával.
g++ már viszi, bár warningokkal van tele.

Nekem a clang4 is meg a gcc6 is szó nélkül lefordított egy pár soros tesztkódot, amiben merészeltem 'a -> b' formában írni egy ilyen hivatkozást. -Wall, -pedantic, -Wpedantic opciókkal játszottam, de szívesen veszek tanítást, hogy mivel lehet még hisztisebbé tenni a fordítókat.
És erősen IMHO, de egy style guide szerint nem javasolt valami az baromira nem ugyanaz, mint hogy a kód ilyetén írása helyes vagy helytelen. Legalábbis nekem az előző az kinézetbeli csúnyaságot, a második pedig működésbeli hibát sugall. Speciel ez szerintem értelmezési kérdés, de ha a szálkezdő valami ilyesmit írt volna: "nem javasolt így írni, mert", "az olvashatóságot / megértést nagyban nehezíti", stb., akkor nem kötöttem volna bele, de a "nem helyes" az legalább szintaktikai, ha nem szemantikai hibára utal. Megintcsak, szerintem.

Anno, a K&R C korszak kezdetén ez a ma létező konstrukció: a += / -= / "operátor-egyenlő" fordított sorrendben volt írandó: =+ / =- / "egyenlő-operátor". E miatt le lehetett írni ezt:

a=-1;

Sajnos ezt egyes fordítók így értették:

a = -1;

mások pedig így:

a =- 1;

ami pedig fentiek szerint egyenlő volt ezzel (mai írásmód) :

a -= 1;

És igenis az volt a mondás, hogy rakd oda a nyomorult (nemkötelező) szóközt, hogy egyértelműsítsd a fordítónak, hogy mi a nyavalyát is akarsz. Aztán eltelt némi idő, és rájöttek a nyelv megalkotói, hogy e helyett a marhaság helyett inkább dobjuk el a nem jól kigondolt formátumot, és az "egyenlő-operátor" helyett használjuk a nyelvben az "operátor-egyenlő" írásmódot. Ennek eredményeként aztán megmaradt a szóköz, tabulátor, soremelés, lapdobás, stb. (gy. k.: blank) használati szabálynak: ha akarod odarakod, ha nem, nem.

Vajon ebben a korban mi a fenét kezdtünk volna ezzel az odadobott "nem helyes így írni" dumával?

(Röviden: lehet szidni más kódolási kovencióit, de ne döngöljük már földbe azért, mert más, mint a mi konvenciónk. Ha meg még tanul, azaz nincs konvenció, akkor főleg ne. Főleg, hogy itt is elhangzott, hogy millió eszköz létezik arra, hogy valamit egységes formára hozzunk. Anno ősi Jujnikszokon volt "cb" parancs (C beautifier). Az teljesen mást állított elő, mint amit a később megjelent "indent" csinált alapból. Aztán jöttek az IDE-k, és amelyik nem külső eszközzel oldotta meg, az megint nagy eséllyel más formát állított elő.)

=====
tl;dr
Egy-két mondatban leírnátok, hogy lehet ellopni egy bitcoin-t?

Ne keverjük a kettőt! A style guide nem kötelező.
Fordítani meg a srác saját kódját próbáltam, az nem fodult, csak C++-ként. Update: elolvastam, előzőleg mit írtam. Bocs, tényleg félreérthető volt.
Szóval a fordítási hiba a pastebin-ről letöltött kódra vonatkozott.

És ezt miért nekem mondod? Vagy szerinted ha egy szintaktikailag helyes valamire rámondjuk, hogy az rossz, és adunk egy szintaktikailag ugyanúgy helyes (szemantikailag pedig vele teljesen megegyező) "javítást", az bátorítás? Mert szerintem nem az, és erre próbáltam felhívni a figyelmet. De mindegy.

=====
tl;dr
Egy-két mondatban leírnátok, hogy lehet ellopni egy bitcoin-t?

Volt egy feladat, volt néhány eszköz a kezében és azokkal megpróbálta szemmel láthatólag egyedül megoldani a feladatot. Nem abban kért segítséget, hogy hogyan lehetne szebb-olvashatóbb kódot írni, hanem a kód MŰKÖDÉSÉVEL kapcsolatban várt jótanácsokat.

Szemmel láthatólag semmi tapasztalata nincs, a kód ilyenformán nehezen debugolható, van egy cpp kiterjesztés egy vegytiszta C kódhoz rendelve, van néhány botorság is benne, erre nekiesnek azért, hogy itt vagy ott kell-e szóköz két karakter közé vagy nem, miközben a kifogásolt elem SEMMIFÉLE működésbeli anomáliát nem okoz. Ezt a hozzáállást én fölényeskedésként azonosítom [bocsánat].

> Sol omnibus lucet.

https://hup.hu/node/158771#comment-2217937 :)
Fene tudja. Szakközép második évében már tanultunk programozni is valamennyire. Assembly-t tanítottak. Utána, valami könyvtárban talált, rongyos C könyv alapján ilyesmit én is össze tudtam hozni. De valahogy sose éreztem úgy, hogy lenne hozzá tehetségem is.
(Röviden: szerintem az a vénás kijelentésed vagy elhamarkodott vagy tragikus a most munkába álló programozóktól elvárt színvonal)

Hajdan a kezdő szinten ott látszott, hogy ki fog még a vizsga után is kódot írni és ki lesz az, aki legfeljebb leveleket, hogy az utóbbi csoport a szubrutinokra bontást érthetetlen önszivatásként élte meg, és ha nem kapott rá külön tanári parancsot (illetve azzal egyenértékű jegyet), akkor úgy írta a kötelezőket, mintha méterre fizetnék.

Nem egy abszolút szűrő, de használható - és ezen átmegy az OP, még ha 2 év múlva nem is fogja érteni, hogy eztmegazt miért is csinálta úgy, ahogy.

Ha nagyon rövid részfeladatokra bontod a teendőket, nem lesz túl nagy az overhead? Úgy értem, sok call/ret páros, stack-re másolás, adatmozgatás, mondanám, sok hűhó semmiért, sok CPU idő, órajel ciklus, hőenergia, villanyszámla, rengeteg feleslegesen átbillent flip-flop, mocorgó elektron.

Persze értem, hogy áttekinthetetlen, ha valami túl nagy, de valahol a két szélsőség között van szerintem az optimum.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Régi fordítóknál valóban ez volt a helyzet. A modernek úgy tudom, képesek optimalizálni a kódot annyira, hogy akár a felesleges függvényhívásokat kiszűrje és befordítsa a megfelelő helyre, mintha fv. helyett makrót használnál.

De szóljatok, ha rosszul tudom!

Persze, akad aki a printnek is ír egy függvényt/metódust/...
Ez a látszattevékenység, egy másik véglet.

Extremitások nélkül csak annyit mondtam volna, hogy aki kezdőként felfogja vagy felismeri a részekre bontás értelmét, az SZERINTEM akkor is affinabb, ha üvöltő hibákat vét, mint az, aki megírja az adott feladatot tökéletesen elvégző kódot egy monolit rutinban.

A sebesség hajszolása közben írtam számtalan olyan függvényt, amit csak "elméletileg" hívtam meg.
Erre való az inline és az isolated_call direktíva, hogy az említett látszattevékenységtől megóvja az amúgy is izzadt processzort. ;) Külön érdekesség, ha rekurzió is van ...
A függvény néha csak a paraméter átadás tisztázását segíti, ami inline esetén nem is másolódik sehova a regisztereken kívül. Ilyenkor meg aztán jól jöhetnek az ebben a topicban is köpködött globális változók is.

Evolúciós lépések.
Valaki valahol leírta, hogy a GOTO-val vigyázni kell. Pár olvasták - írtak belőle - mások olvasták -előadták - mások félig megtanulták - továbbadták - ... ciklus után kijött a végeredmény: GOTO-t használni TILOS. Mindig, mindenhol.

Valahol valaki leírta, hogy a globális változókkal vigyázni kell.... TILOS, mindig, mindenhol.

A ponterekről is leírták, hogy vigyázni kell velük...

Szerintem meg egyáltalán nem elhanyagolható a mai programok overheadje. Éppen azért, mert néhány soros egyszerű marhaságokat is függvénybe tesznek, s a paraméterek átadása, a visszatérési cím tárolása, a visszatérés, s a vélhetően ebből adódó pipeline megszakadása máris összemérhető lesz a hasznos cselekménnyel.

Ami a karbantarthatóságot illeti, szerintem az is rossz, ha az adott programnyelv nyelvi elemei helyett a félméter hosszú függvénynevekre és paramétereire kell emlékezni. Legyen önálló függvény, ha az egy jól megfogalmazható funkciót valósít meg, s többször hivatkoznak rá, de feleslegesen inkább ne.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Legjobb tudomásom szerint ezeket a mikro-optimalizációkat egy fordító sokkal jobban megcsinálja, mint az ember kézzel.

Érdemes az egész előadást megnézni, de deeplink van a függvényhívás overheadje részhez.

Ugyanebben a témakörben (tágan) Czirkos Zolinak is van egy nagyon jó írása itt: https://infoc.eet.bme.hu/xor_csere/

Viszont assembly-ben mikrokontrollerre bizony van létjogosultsága a xor cserének. A másik kedvencem, amikor egy regiszter maszkkal meghatározott bitjeit egy másik regiszterből felül akarjuk írni, a többit érintetlenül hagyjuk. Ez egy xor, and, xor hármas lesz.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

C Kezdő; láncolt lista, bináris fájlműveletek, SIGSEGV hiba

El tudom fogadni, hogy vannak speciális esetek, ahol nem árt ezekkel tisztába lenni.
Ettől függetlenül az alábbi két mondással szerintem semmi baj nem volt, sőt:
- Optimalizálni "kész programot" lehet a mért eredmények illetve a felmerülő problémák függvényében.
- A mai világban sokkal nagyobb kihívás a kód karbantarthatósága, mint pár call/ret okozta lassulás.

Nagyon speciális eset az, ahol előre megmondott, órajelre kiszámolt futásidő kell. Ezek nem valószínű, hogy a C-kezdő topikban kerülnek elő.

Magam is inkább a túlzásoktól óvnék. Ezen kívül vannak beágyazott környezetek, mikrokontrollerre is írnak C-ben programokat - rögtön egy kollégám mellettem :) -, s észnél kell lenni, mert mondjuk összesen 16 mélységű dedikált stack-ed van, aztán bele kell férnie az alapszintű egymásba ágyazott hívásoknak, a megszakításnak, a megszakításból hívott függvényeknek. Ilyen környezetben, ha valaki írja a kódot, mondván, a billentyűzet és az editor mindent kibír, abból hamar lesz baj.

De mondom, jelentősen nem tér el a véleményünk, csak próbálok utalni arra, hogy nem csak olyan helyek vannak, ahol a RAM-ot gigabyte-ban, az órajel frekvenciát gigahertzben mérik, hanem van, ahol a RAM néhány száz byte, a programtár néhány kilobyte, az órajel néhány tíz megahertz, s így kell nagyon gyorsnak és hatékonynak lenni. Én assembly-ben piszkálok ilyesmit, van, amikor a C célszerűbb, de ahhoz nem nyúlok. :) Vagy csak alig.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

"Szerintem meg egyáltalán nem elhanyagolható a mai programok overheadje."
Nem azt mondtam, hogy nem kell optimalizálni, hanem azt, hogy az a "végső" fázisa a fejlesztésnek. Az, hogy ezt manapság nincs idő/szándék/tudás megcsinálni, az más tészta. Ettől még "tervezési fázisban" az architektúra a fontos, és nem az, hogy hány fölös függvény hívásod van.

A harvard architektúrájú "kicsi" mikrokontrollereket nem keverném ide (8-bit PIC, 8-bit AVR RISC), mert ezeket ugyan lehet C -ben programozni, de rendesen meg kell erőszakolni azt a C programot ahhoz, hogy optimális legyen a végeredmény. Ezekre a kütyükre a C inkább csak amolyan kényelmes assembly, mind gondolkodás módban mind nyelvi elemek tekintetében speciális megoldásokat igényel.
Minél feljebb megyünk teljesítményben annál kevésbé igaz ez. Pl. egy Cortex-M kaliberű MCU -ra, szépen lehet absztrahálni és strukturáltan programozni.

"pipeline megszakadása"
Milyen pipeline? AVR -ben vagy PIC12 -ben? vagy Cortex-M -en vagy Cortex -A -n? Cache méretre is optimalizáljunk? Ha igen, mekkorára? Ezekkel a kérdésekkel csak akkor tudsz foglalkozni, ha már tudod min fogsz futni és azt is hogy hogyan.

Igazad van, hogy nincs általános igazság, azaz nem lehet vakon, a jól megszokott beidegződésék mentén programozni. Optimalizálni kell, egy flexibilis, jól strukturált kódot pedig könnyebb refaktorálni. Érdemes olyan metodológiát használni kezdetben, ami ezt a flexibilitást segíti.

Egy kicsit hátrább az agarakkal azért. Mint írtam, szintaktikailag megfelelő, olvashatóság miatt nem teljesen. Helyes/helytelen Kötelező/Nem kötelező/Engedékeny pedig különböző szavak és nem rokonértelműek egymással. Helytelen azért, mert 15 év alatt szerintem ő volt az első akinél ilyen code style-t láttam (pedig magam is írtam már több 100k soros, c-ben írt szoftvereket, sőt, napi szinten túrok nem egy millió soron felüli kódot).

Az, hogy a nyelv nem rendelkezik róla és egy sorba össze lehet b*szni mindent, még nem lesz helyes, általános code style guide létezik, bár ugyan nem rendelkezik külön arról hogy 'var->tp' vagy 'var -> tp', viszont következetesen mindenütt az előbbit látod ezekben, nem az utóbbit. Pont ezen oknál fogva a legtöbb IDE nem is színezte helyesen (és szerintem találunk olyan fordítót is, ami csak az előbbi formát fogja elfogadni).

Az összeadás és a szorzással nem tudom mit akartál mondani, én dereferálást írtam (mert szemmel olvasva a kódot meg kell nézned az egész sort miatta és nem elég csak a változót: *ptr vagy * ptr)

Ez egy nem definiált szabály, ahogyan az sem hogy int* var; vagy int *var;

Előbbit c++ kódokban fogod látni, utóbbit c kódokban főként.
Én szintén az utóbbit preferálom mert az int* var; egy felsorolás esetében már félrevezető is lehetne:
int* a, b;

Ha következetesen nézzük, akkor az int* a típus, az a és b pedig változók, így elviekben egyformák, pedig nem. int *a, b; esetében pedig pontosan látod hogy mi-micsoda.

Szóval igen, az hogy valamiről a nyelv nem rendelkezik, vagy egy 30 éves code style guid nem írja le explicit (de minden példában következetesen ugyan úgy van használva) és több millió sor olvasása alatt egyetlen egy kódban láttam csak így (ami kód egyébként kb másik ezer sebből vérzik és ez a legapróbb problémája), szerintem nyugodtan lehet egy általánosan használt és elfogadott minta alapján egy kódot helyesnek vagy helytelennek jelölni.

Az meg hogy ilyen (már ne haragudj) bullshitekkel idetrollkodsz, az csak a te jellemrajzodat bővíti.

// Happy debugging, suckers
#define true (rand() > 10)

> Mint írtam, szintaktikailag megfelelő, olvashatóság miatt nem teljesen.

Újraolvastam, nem írtad.

> Helyes/helytelen .. különböző szavak és nem rokonértelműek egymással.

Kérlek adj a "helyes" szóra rokonértelmű szót. Én a "jó"-t inkább annak tekintem, mint a "szép"-et; te viszont úgy dobálózol vele, mintha szerinted a "szép" lenne a szinonimája. Vagy másként: nekem a "helyes" inkább jelenti azt, hogy "elfogadható", nem pedig azt, hogy "elfogadott". Szóval akkor szerinted melyik?

> Az, hogy a nyelv nem rendelkezik róla és egy sorba össze lehet b*szni mindent, még nem lesz helyes

Én még úgy tanultam, hogy rendelkezik róla, kb a következő formában: "az utasításokat pontosvesszővel kell lezárni; egy sorba írható több utasítás." Ez viszont azt jelenti, hogy nem kell, de nem is tilos sem egy sorba írni mindent, sem minden utasítást külön sorba. Következésképpen: Akár ez, akár az a használt forma, a nyelv szempontjából "helyes".

Most véletlenszerűen megnyitottam egy coding style leírást ( https://www.doc.ic.ac.uk/lab/cplus/cstyle.html ) Rákerestem a whitespace-re, és nézelődtem. Konkrétan nem találtam leírást a -> operátorról, de az tény, hogy abban a néhány példában ami ott (a whitespace rész környékén) szerepelt, egyik helyen sem volt körülötte szóköz. Ellenben mivel rögtön az whitespace szakasz első két példájában ("rosszabb" if, majd "jobb" if) azt látom, hogy miközben a hosszú kifejezések több sorra tördelését ragozza (mint pozitívum), laza mozdulattal beszúr szóközt a kisebb-mint-egyenlő köré - ebből viszont én azt a következtetést vonom le, hogy implicit módon azt sugallja, hogy a pluszban elhelyezett szóközök még olvashatóbbá teszik a kódot. (Szóval ennyit a coding style-ról.) Továbbá *szerintem* olvashatóbb az if ( feltétel ) forma, mint az if(feltétel) - és ehhez képest fenti doksiban ezt látom: if (feltétel) .

> és szerintem találunk olyan fordítót is, ami csak az előbbi formát fogja elfogadni

(Ezt ugye megint az 'a->b' vs 'a -> b' formára írod.) Kérlek mutass olyan fordítót, ami az utóbbi formát elutasítja, és akkor majd a fordító gyártójával le lehet levelezni, hogy ugyan melyik szabvány melyik pontjából vezette le azt, hogy az a forma szintaktikai (szemantikai? másmilyen? milyen?) hiba. (Nem az, hanem fordítóhiba.)

Mondom másként: az általad emlegetett coding style-ok ajánlások, amelyek mondhatják azt, hogy nem így kéne hanem amúgy. De amígy a nyelv szabványa megengedi, addig az nem hibás.

> Az összeadás és a szorzással nem tudom mit akartál mondani

majd pedig hozol ugyanúgy egy példát, amivel alá szeretnéd támasztani a véleményedet. Képzeld, én is ezt tettem az összeadás / szorzás segítségével - próbáltam alátámasztani példával az én vélemányemet. Ezek szerint nem sikerült.

Ami az int* a, b; példádat (ami szerinted helytelen, mert félreérthető) illeti, az a jó benne, hogy attól mert figyelmetlen olvasó (vagy a nyelvet kevéssé vagy épp nem jól ismerő) féleér(he)ti, attól még a fordítóprogramok pontosan ugyanazt fogják belőle fordítani, mint az álalad helyesnek titulált int *a, b; formából. Azaz mind a két forma "jó".

> Szóval igen, az hogy valamiről a nyelv nem rendelkezik

Itt a hiba. Te abból indulsz ki, hogy a nyelv nem rendelkezik a szóköz, tabulátor, stb (whitespace) használatáról. Pedig de. OK, kicsit nagyvonalúan, mert kb. azt mondja, hogy annyit raksz és oda, ahova akarod. (Kivételek - azaz ahova nem rakhatod büntetlenül - a teljesség igénye nélkül: numerikus és karakterlánc konstansok; kulcsszavak, változók és függvények neve; több karakterből álló operátorok belseje (++ OK, de + + már nem, vagy épp -> OK, de - > már nem).

Végül persze, a hozzászólásod végére csak kiderült a lényeg. "szerintem nyugodtan lehet" . Valóban. Szerinted.

Amúgy végtelenül boldog vagyok, hogy a jellemrajzomat sikerült bővítenem, de annyit árulj már el, kivételesen személyeskedés nélkül: mi volt a bullshit?

=====
tl;dr
Egy-két mondatban leírnátok, hogy lehet ellopni egy bitcoin-t?

Fussunk neki újra,

"Újraolvastam, nem írtad."
Te is fuss neki újra

"Szóval akkor szerinted melyik?"
Játszhatunk itt a szavakkal, de a tények makacs dolgok. Az hogy szerinted a helyes az "elfogadható" -t jelenti, legyen :)

"Mondom másként: az általad emlegetett coding style-ok ajánlások, amelyek mondhatják azt, hogy nem így kéne hanem amúgy. De amígy a nyelv szabványa megengedi, addig az nem hibás."

A nyelv nettó szintaktikát fogalmaz meg, amivel a fordítót tudod etetni. Ha csak ilyen szemmel nézzük a dolgokat, akkor tény, dobjuk ki az összes coding style -al, design pattern-el és minden más egyéb dologgal foglalkozó tömérdek irodalmat, mert hát a fordító megeszi így is, úgy is, nemde? De akkor már legyünk optimálisak és valóban vegyük ki az entereket a sorvégekről is, mert hát a fordítót úgy sem érdekli. A "programozás" úgy is csak a fordító etetéséről szól...

"Én még úgy tanultam, hogy rendelkezik róla"
Vagyis explicit nem tiltja meg, a te analógiád mentén haladva ez pedig a szabad kategória, sőt, érvelsz mellette hogy ez így jó... A többiek akik külön sorba írnak, vagy egybeírják a változókkal a '->' operátort azok pedig valami másik faj szülöttjei, esetleg ha megjegyzik hogy egy sorba írni a kódot hülyeség, vagy hogy az általánosan elfogadott az egybeírás (mert most próbáltam keresni, de keresve sem találtam olyan kódot ahogy így lenne írva) azok meg idióták, mert csak kötözködnek :)

"Itt a hiba. Te abból indulsz ki, hogy a nyelv nem rendelkezik a szóköz, tabulátor, stb (whitespace) használatáról."
De, ezekről rendelkezik, arról viszont hogy a '->' operátor köré kell-e, helyes-e, nincs pontos meghatározás, vagyis marad a coding style és az általánosan elfogadott forma (ahogy a több sorról is, mert arról sem nyilatkozik)

"majd pedig hozol ugyanúgy egy példát, amivel alá szeretnéd támasztani a véleményedet"
Nem, arra próbáltam példát hozni, hogy amiről a nyelv standard nem nyilatkozik, az nem azt jelenti hogy akkor úgy csinálod ahogy neked tetszik. Értem, a fordító megeszi, a kedves programozótársak nem fogják. De akkor az összeadós szorzós példádon elindulva, ez is helyes:
a
->
b
;

Megeszi a fordító? Meg. Nyilatkozik róla bármilyen standard hogy nem szabad? Nem. Akkor ez így jó? A választ te is tudod.

""szerintem nyugodtan lehet" . Valóban. Szerinted."
Öööö, omg, rendben, mellékeltem neked ide a fenti kód első pár sorát olyan formában, ami szerinted teljes mértékben elfogadható (fordító megeszi, szabvány nem nyilatkozik róla explicit, a coding style könyvet pedig már ki is dobtam): https://pastebin.com/SaUmFwan

Vagyis szerinted ez így ebben a formában: tökéletesen megfelelő

"mi volt a bullshit?"
Nézd meg a mellékelt kódot, olvasd el hogy mi mellett érvelsz, aztán talán leesik, vagy nem, igazából nem érdekel, a szálat magára hagyom

// Happy debugging, suckers
#define true (rand() > 10)

Megnéztem, sehol nem írtad - legalábbis én ezt sehol nem látom. De mivel konstruktív szeretnék lenni - bár sokadszor nem sikerül ezt megértened: kérlek explicit módon mutass rá arra a hozzászólásodra, és azon belül arra a részre, ahol szerinted ezt írtad.

Továbbmegyek: leírtad azt, hogy a fordító, az IDE (és te) sikítófrászt kaptak az 'act_rec -> id' formától. majd kifejted hogy azért, mert bekerült a nyíl operátor köré 2 szóköz. Újfent kipróbáltam (ezúttal nem saját kóddal, hanem a topicnyitóban belinkelt kóddal):
clang : FreeBSD clang version 4.0.0 (tags/RELEASE_400/final 297347) (based on LLVM 4.0.0)
gcc: gcc6 (FreeBSD Ports Collection) 6.4.0

Ebből a clang a struct-ot hiányolja, és nem aggódik a nyíl operátor körüli szóközök miatt, a gcc pedig sipítozikk azokra a sorokra, ahol ilyen act_rec -> id van. Kár, hogy ha a hibaüzenetet is megnézzük, rögtön látszik, hogy nem a szóközök miatt, hanem a miatt, mert nem ismeri fel, hogy az act_rec struktúra pointer lenne:


bencehelp.c:37:24: error: request for member 'id' in something not a structure or union
     if (_id == act_rec -> id)
                        ^~

Rögtön a következő hibaüzenet mutatja, hogy valóban nem a szóközökkel van a baja, ugyanis pont ugyanazt a hibát adja arra a sorra, ahol az általad elfogadott coding-style zerinti az írásmód, azaz nincs szóköz a nyíl operátor körül:


bencehelp.c:39:22: error: request for member 'next' in something not a structure or union
     act_rec = act_rec->next;
                      ^~

Mindenesetre annyit elmondhatunk: az általad ismert, használt kódolási konvencióknak ellentmond a nyíl-operátor körüli szóközök használata, ezért helytelennek itéled a kódot. Én meg azt mondom, hogy a nyelv szintaxisa explicit engedi, azaz nyelvi szempontból helyes. Viszont a kódon jól látszik, hogy az írója még tanulja, hogy hogyan is kell szép, áttekinthető (ezáltal olvasva is érthető) kódot írnia (lám, van ahol van szóköz, van ahol nincs) - e miatt erre is fel kell hívni a figyelmet, de semmiképp nem azzal, hogy egy a szintaxis szerint megengedett formára azt mondjuk, hogy az nem jó.

De mivel te ezt az egészet elengedted, ez már Bencének szól.

=====
tl;dr
Egy-két mondatban leírnátok, hogy lehet ellopni egy bitcoin-t?

Nehéz szó nélkül hagyni ezt a dolgot, mert a lényeget kihagytad és ismételten sikerült leírni azt, hogy ez a forráskód így ahogy van tökéletes, mert egyetlen hibaüzenetet sem dob a fordító erre: https://pastebin.com/SaUmFwan

Jól értem?

// Happy debugging, suckers
#define true (rand() > 10)

Te az első hozzászólásodban közölted, hogy az 'a -> b' nem helyes. Én pedig az én második hozzászólásomban ezt írtam:

>> ha a szálkezdő valami ilyesmit írt volna: "nem javasolt így írni, mert", "az olvashatóságot / megértést nagyban nehezíti", stb., akkor nem kötöttem volna bele, de a "nem helyes" az legalább szintaktikai, ha nem szemantikai hibára utal. Megintcsak, szerintem.

Nem védtem a topiknyitó kódját, egyes egyedül erre a - szerintem - baromságra reagáltam (szóköz a nyíl körül) - és ezt még le is írtam. (Azt is, hogy direkt saját példakódon néztem ezt az egyetlen "hibát".)

Te meg rugózól rajta, de legalább eléggé szinvonaltalanul. Személyeskedsz, olyasmiket tulajdonítasz nekem, amiket nem írtam, de cserébe egyszerű kérdésekre sem válaszolsz (lehet, mert akkor be kéne látnod, hogy nincs igazad).

Idézet tőled:

"a lényeget kihagytad és ismételten sikerült leírni azt, hogy ez a forráskód így ahogy van tökéletes"

aki a lényeget kihagyta az te voltál; és nem, egyetlen egyszer sem írtam le azt, hogy topiknyitó kódja tökéletes. Mint ahogy erről az újabb kódról sem írtam semmit.

Sokadszor írom le: én ebben a topicban kizárólag a "nyíl-operátor + szóköz"-ről beszéltem, illetve azt az isteni kinyilatkozásodat próbáltam cáfolni, hogy a C-nyelvben nincs meghatározva a szóközök használatának módja, ellenben ha valamilyen Style-guide-dal ellentétes módon ír valaki kódot, akkor az hibás.

De azért próbálkozzál még. Mondjuk megérteni azt, amit leírtam. Ne pedig azt, amit nem írtam le.

=====
tl;dr
Egy-két mondatban leírnátok, hogy lehet ellopni egy bitcoin-t?

Ha egyszer lefut egyszer nem, akkor gyanus, hogy inicializalatlan valtozo van valahol.

Néhány WTF:


  if(fp==NULL) {
    fclose(fp);

Miért akarod lezárni a NULL-t?


record * fst_rec = NULL, * act_rec = NULL, * new_rec = NULL, * prev_rec = NULL;

Miért jó, hogy vannak globális változóid? Szüntesd meg őket és tedd őket paraméterré/visszatérési értékké.


    tmp.id = act_rec -> id;
    strcpy(tmp.name, act_rec -> name);
    tmp.goals = act_rec -> goals;
    fwrite(&tmp, sizeof(temp), 1, fp);
  }

Minek másolod ki a tmp struktúrába? Így gyakorlatilag ugyanazt a memóriatartalmat írod ki (+/- a next, ami valami konstans állandó szemét lesz). [egyébként itt lehet a gond is, utána ugyanis szépen ezt olvasod vissza és nem inicializálod a visszaolvasáskor a next értéket, úgyhogy szépen kimutathatsz belőle a nagy világba]

Itt adtam fel...

Ha rám hallgatsz: oszd további problémákra, pl. a beolvasást írd át úgy, hogy legyen egy külön függvényed, ami pontosan egy rekordot beolvas és visszaadja a rá mutató pointert (inkább sikeres/fájl vége/hiba visszatérési értéknek, a pointerre mutató pointert meg kapja meg paraméterként), akkor a fájl beolvasós függvényed lényegesen egyszerűbb, mint a mostani globális state-el való játék (pszeudókód):


last_rec = first_rec = read_one_record();
while(!fp) {
   new_rec = read_one_record();
   last_rec->next = new_rec;
}
return first_rec;

A NULL helyett érdemes lehet egy jól definiált (az lehet globális konstans változó) értéket használni listavégnek, pl. ha biztosan nincs 0 értéked, használd a "0 id-jű rekord a vége" megoldást [aminek a next-je önmagára mutat]: így ha a többi rekord nem mutat ki a memóriából, akkor az egész láncod a saját memóriarészedben lesz, és ha bárhol hibázol, nem elszáll SIGSEG-gel, hanem végtelen ciklusba megy :)

BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)

if(fp==NULL) {
fclose(fp);

Ez konkrétan segfault lesz, mert az fclose nem szereti a NULL-t.

"A NULL helyett érdemes lehet egy jól definiált (az lehet globális konstans változó) értéket használni listavégnek, pl. ha biztosan nincs 0 értéked, használd a "0 id-jű rekord a vége" megoldást [aminek a next-je önmagára mutat]: így ha a többi rekord nem mutat ki a memóriából, akkor az egész láncod a saját memóriarészedben lesz, és ha bárhol hibázol, nem elszáll SIGSEG-gel, hanem végtelen ciklusba megy :)"

Ez nem tudom vicc-e, de nem. Inkább szálljon el segfaulttal mint fusson végtelen ciklusra és a lista vége legyen NULL mert az jól definiált, hogy nem mutat sehova. Egy kezdőnek nem kéne marhaságokat tanácsolni, mindenki NULL-t használ C-ben.

Nem kell, de érdemes lehet elgondolkodni rajta :) Hál istennek szinte soha nem kell C-t írnom, de akkor szeretem úgy intézni a dolgokat, hogy legalább ránézésre megmaradjanak az előnyök az OOP-ből (pl. struktúrák belsejében nem nyúlkálok, csak dedikált függvényen keresztül; ezzel minimálisan megkapom az encapsulationt, kapnak dedikált "constructort" és "destructort" [általában nem sokkal több, mint egy malloc/free], copy constructort (az OP-ban levő kód hány példányban szerepel a másoljuk a mezőket A struktúrából B struktúrába kisebb nagyobb különbségekkel/hibákkal?) stb.).

A fentinél maradva: NULL pointer vs. null object témakörben ilyen szabályok mellett nagyjából mindegy lesz, hogy a has_next(struct record current) és get_next(struct record current) if(current == NULL) vagy if(current->id == 0) vagy if(current->next == current) lesz [Null object pattern-el akár a második kettő közül választhatsz is egy rand() hívás után :) ], de egy debug sor egyszerűbb még a "szabályok" gyorsan felrúgva is:

printf("current: %d next: %d", current->id, current->next->id);

Szemben a NULL-ossal, ahol ahhoz, hogy a debug üzid miatt ne crashelj, kell plusz egy feltételvizsgálat.

Szerk.: Egyébként az OP-ban levő screenshoton levő .cpp kiterjesztés alapján C++ :)

BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)

A C nem OOP nyelv, a NULL ott nem valami absztrakt objektum, hanem egy memória cím. Ha valami konstansnak, vagy létető címnek definiálod akkor ráfutva oda akár bele is írhatsz, ha a saját területeden van a cím, ha nem akkor ugyan úgy segfault lesz mint a 0 címnél. Ha véletlenül a kernel vagy akkor meg random helyre belekontárkodhatsz írásnál adatba. Oda NULL kell, ha a programod szar és hivatkozni akarja akkor haljon meg, legalább ott lesz valami infó, hogy hibás.

Mi ez a pusztito igenytelenseg? Aztakurva.

Régen ilyen feladatok voltak Prog1 beugróra :D
Ha nagyon elakadsz, előbb rajzold le, mit is kellene csinálni és milyen eljárásokra-függvényekre lenne szükség, aztán programozz.
Most ha kel is ilyesmit írnom, szerencsére elég Javában is :D , ha nagyon nyöszörögnek (pl a gyakszisaim a nyári gyakorlaton, rendszergizda képzés, kisgizda fokozat) akkor C# (közös feladat, Javát nekik nem oktattak).

én a globális változókat biztosan eltüntetném.
sanszos hogy az "egyszer lefut egyszer nem" az azért van, mert az előzményektől függően van értéke ezeknek a pointereknek

szerintem egy statikus kódelemző hanyattesik annyi critical error lesz a kódban :)
viszont ha sikerül rátuszkolni akkor megmondja jó eséllyel hogy mitől van a segfault

--
Gábriel Ákos

Indentálásra, kód stílusra ajánlom a kernelest, az kb. a de facto standard C esetén. link

A Create és az update legyen két függvény. Meg kéne oda még túl hosszúak.

Struct-nál vagy typedef kell, vagy struct akarmi módon kell definiálni.

A feladat binárisfáért kiállt...

A végén nincs free...

Am ez prog1 házi?

"Indentálásra, kód stílusra ajánlom a kernelest, az kb. a de facto standard C esetén."
Sokféle stílusban lehet dolgozni. A lényeg, hogy ilyesmit az ember nem csinál kézzel. Rengeteg kód formázó progi van, sőt a legtöbb editor is tud már ilyesmit (pl. Eclipse -ben CTRL+SHIFT+F). Arra kell rászokni, hogy automatikusan futtasd ezeket. (http://universalindent.sourceforge.net/)

Pár ötlet, hogy működjön a programod:
- Ha van Windows-od, akkor javaslom, hogy próbáld ki a Visual Studio Express-t (https://www.visualstudio.com/vs/express/). Szerintem kezdőknek sokat segít.
- A C nyelv szintaktikája sokmindent megenged, de ez nem azt jelenti, hogy ezek közül mindet ki is kell használni. Javaslom, hogy a struct-os megoldást ilyesmire írd át:

typedef struct Stat {
int id;
char name[100];
int goals;
} Stat;

typedef struct ListItem {
Stat data;
ListItem* next;
} ListItem;

- A jelenlegi program sok helyen hibás logikával működik. Ésszerű lenne addig eljutni, hogy egy for ciklussal létrehozol 5 elemet a memóriában, majd megszünteted a listát (malloc vs. free). Mindezt úgy, hogy a program nem leakel és nem fagy.
- Ha ez az alap funkció megy, akkor jöhet az elemek módosítása, törlése.
- Ha ez is megy, akkor jöhet a fájlból olvasás.
- Érdemes lenne git-et is használnod (ha már mutatták neked a suliban). Kezdőknek szerintem ideális Git kliens Windows-on a SourceTree. Ha viszont még nem láttál git-et, akkor lehet, nem most kell elkezdened, mert elég kihívás lesz a program összerakása.

Nem néztem át alaposan a kódot, de én is hozzáteszek két gondolatot, ami főként az átláthatóságot növeli:

- Globális változókat számold fel, csak arra jók hogy nem tudod követni, hol módosul a struktúra. Helyette struct akármi *-ként add át a függvénynek.
- Ahol nem tervezed módosítani, ott a "const" könnyíti az átnézést, hiszen a függvény paraméterátadásából már látod, hogy nem lesz módosítva.

void read_records_from_file(const char *filename, struct record *new_rec);
void write_records_to_file(const char *filename, const struct record *fst_rec);

Érdekességként megjegyzem, a Rust nyelv esetén a C-hez képest ezeket a rossz szokásokról leneveló módon valósították meg: a globális írható változó kizárólag unsafe {} blokkban kezelhető, a függvényekben pedig alapból "const"-os az átadás, ha visszaírna, akkor pluszban kell odaírnod, hogy &mut. Ha &mut-tal adod át és simán (readonly) elég lett volna, akkor az is fordítási Warning lesz.
Ezek a fegyelemre nevelő intézkedések most már hiányzanak nekem a C-ből.

Továbbá:
$ gcc -O2 -Wall bencehelp.c -o bencehelp

Ami hibát ez kiír a képernyőre (van bőven), annak a javításra ajánlott. A visszatérő értékek lekezeletlensége warning, de célszerű minimum egy if (.....) { print("itt hiba történt")} megoldással lekezelni.

A fentieket ha megfogadod, akkor egy jelenleginél sokkal átláthatóbb és jobb minőségű kódot írtál, amit akár más, akár később te magad is könnyebben látsz át.

Szerintem itt két dolgot kellene felismerni; az egyik az, hogy a topiknyitó olvtárs azóta már a prog.hu-n is túl jár, a másik az, hogy a felhasználói input feldolgozása hálátlan, érdektelen, de nem könnyű munka. Most hirtelen két rutin elkészítését javasolnám, az egyik általános string-beolvasás, a másik pedig szám-beolvasás, ez az előzőn alapulna.
Ilyesmi lehetne a kiindulás:


static int getint_range (const char *prompt, int mini, int maxi)
{
    int ok= 0;
    int n;

    for (ok= 0; !ok; ) {
	n= getint (prompt);
	if (n>=mini && n<=maxi) {
	    ok= 1;
	} else {
	    printf ("Hibas szam, %d es %d kozott kellene\n", mini, maxi);
	}
    }
    return n;
}

static int getint (const char *prompt)
{
    int ok= 0;
    long l;
    char numbuff [12];
    char *endptr;

    for (ok= 0; !ok; ) {
	getstr (prompt, numbuff, sizeof numbuff);
	l= strtol (numbuff, &endptr, 10);
	if (*endptr==0 && (long)((int)l) == l) {
	    ok= 1;
	} else {
	    printf ("Hibas szam '%s'\n", numbuff);
	} 
    }
    return (int)l;
}

static void getstr (const char *prompt, char *buff, size_t buflen)
{
    char tmp[256], *p;
    size_t len;
    int ok;

    for (ok=0; !ok; ) {
	fputs (prompt, stdout);
	fflush (stdout);

	p= fgets (tmp, sizeof tmp, stdin);
	if (p==NULL) {
	    printf ("Nem megy a stdin olvasasa\n");
	    exit (1);
	}
	len= strlen (tmp);
	if (len==0 || tmp[len-1]!='\n') {
	    printf ("Tul hosszu vagy hibas input\n");
	    continue;
	}
	tmp[--len]= '\0';
	if (len>0 && tmp[len-1]=='\r') tmp[--len]= '\0';
	if (len==0) {
	    printf ("Ures input? Probald ujra\n");
	    continue;
	}
	if (len+1>buflen) {
	    printf ("Tul hosszu input\n");
	    continue;
	}
	strcpy (buff, tmp);
	ok= 1;
    }
}

Szerk: hoppá, három lett, maradhat?