Írtam egy ilyet:
void nfree(void **p) {
if (p) {
free(*p);
*p = NULL;
}
return;
}
Nem akarok típust cast-olni minden híváskor, és nem akarok emiatt warningot látni. Valahogy a realloc(), free() megoldja, hogy a fordító nem nyüszít, bármilyen típusú pointert is adok át. Néztem az stdlib.h-t is, de talán valamilyen attribútum lesz a megoldás, viszont nem tudom mi és miképp.
Mit kell tennem, hogy például ez ne adjon warning: passing argument 1 of ‘nfree’ from incompatible pointer type [-Wincompatible-pointer-types] figyelmeztetést? Már a cast-oláson illetve a warningok globális kikapcsolásán túl.
Megoldás
Az első hozzászólásban apal által adott megoldás nyerte el tetszésemet. Lefordult warning nélkül, ami persze érthető.
Köszönöm a segítséget!
- 918 megtekintés
Hozzászólások
void __nfree(void **p)
{
/* ...*/
}
#define nfree(x) __nfree((void **)(x))
Nem a legszebb workaround, de ugye amit mondasz az nem teljesen igaz, mert a realloc() meg free() az void*-ot eszik es nem void**-ot. Ugyanakkor az ketsegtelen hogy egy jo kerdes miszerint egy `pointer *` tipus hogy vedheto be - siman lehet hogy van erre valami __attribute__ (( ... )).
- A hozzászóláshoz be kell jelentkezni
Jaj, de egyszerű, s hogy ez nekem mennyire nem jutott eszembe! Köszönöm. :)
Azt tudom, hogy a realloc(), free() az adattípusra mutató pointert eszi. Az nfree() megoldásomnak a pointer címét kell átadni, s a hely felszabadítása után NULL pointert ír bele, ezzel biztosan el lehet kerülni a double free és a use after free típusú hibákat. Jó, az utóbbi esetben NULL pointer dereference lesz, de abba legalább azonnal belepusztul, s könnyen kiderül a baj.
Itt nem arra gondolok, hogy az ember benéz valamit, hanem előfordul, hogy hibakezelés, egyebek miatt másodjára is felszabadítanánk a helyet. A free(NULL) viszont legitim, s akkor nem kell rettentő bonyolult adminisztrálás ehhez.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
De evvel az a baj, hogy az nfree(42) és nfree("kiscica") is szó nélkül lefordul.
- A hozzászóláshoz be kell jelentkezni
Van szebb ötleted? Nem az a cél, hogy egy idomítatlan majom ne tudjon hibás kódot lefordítani. Abból is lenne baj, ha p = malloc(256); nfree(p); az amit írok, a helyes p = malloc(256); nfree(&p); helyett.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
void nfree(void* ptr) {
void** typed_ptr = (void**)&ptr;
if (typed_ptr == NULL || *typed_ptr == NULL) {
return;
}
free(*typed_ptr);
*typed_ptr = NULL;
}
- A hozzászóláshoz be kell jelentkezni
Ez sajna nem jo:
p=malloc(1024*sizeof(double));
fprintf(stdout,"p=%p \n",(void *)p);
nfree(p);
fprintf(stdout,"p=%p \n",(void *)p);
Amibol ez lesz:
$ ./x
p=0x55dbf946c010
p=0x55dbf946c010
- A hozzászóláshoz be kell jelentkezni
nfree(&p); a helyes hívás.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
igazad van, gyorsabban írtam, mint ahogy gondolkodtam :)
#define nfree(ptr) do { \
free(ptr); \
ptr = NULL; \
} while (0)
akkor marad ez
- A hozzászóláshoz be kell jelentkezni
Láttam már ezt a do {...} while (0) megoldást macroban. Mi a lényege? Mennyivel másabb a sima kapcsos zárójelhez képest?
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
A pontosvesszovel valo lezarast igy tudod jol biztositani minden korulmenyek kozott. Es ezaltal pl azt hogy egy if () utan { ... } nelkul is jol tudod hasznalni es az tortenik amit szeretnel hogy tortenjen.
- A hozzászóláshoz be kell jelentkezni
Ezt végig kellene diszkutálni, mikor lehet baj a sima {} használatából, de elhiszem.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
nekem nem remlik hogy c-ben csak ugy random helyekre berakhatsz { }-t. igaz nem is probaltam sose :) de nem is lattam ilyet sose.
szerk: kiprobaltam, gcc megeszi, de akkor is fura.
- A hozzászóláshoz be kell jelentkezni
#define foo1(x) { do_something(); do_something_else(); }
#define foo2(x) do { do_something(); do_something_else(); } while(0)
if ( condition )
foo1(z);
else
what_ever();
Probald ki: foo1(z) nem fog lefordulni, a foo2(z) meg menni fog (es azt fogja csinalni amire a kolto gondol!)
- A hozzászóláshoz be kell jelentkezni
Ha jól sejtem, az else előtti pontosvessző egy üres utasítás lesz, s az else csak úgy a semmiből tör elő, mint a gejzír, if nélkül.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Pontosan: `error: 'else' without a previous 'if'`
- A hozzászóláshoz be kell jelentkezni
if (x==1) DoSomething();
else DoSgElse();
Az else előtt nem lenne jó { ... };
szekvencia, ezért trükközünk így:
#define DoSomething() do { ... } while (0)
- A hozzászóláshoz be kell jelentkezni
Jaja, ez is teljesen jo megoldas :) Egy fokkal nagyobb lesz a kod, de az extrem beagyazott rendszereken kivul nem hiszem hogy barhol szamitana ez. Igaz, extrem beagyazott minimal rendszereken relatve keves a dinamikus allokacio.
- A hozzászóláshoz be kell jelentkezni
Ez most beágyazottal kommunikál ugyan, de linuxos PC-n fut, szóval egy konzolos linuxos program. :) Itt nem kritikus minden byte és minden nanosecundum.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
szerintem a while(0)-t minden kozepesen szar vagy annal jobb fordito kioptimalizalja siman. foleg hogy elegge elterjedt az ilyen celu hasznalata.
- A hozzászóláshoz be kell jelentkezni
Pontosan :) Bar ha megsem optimalizalja ki az duplan rossz mert mikroarchitekturalisan a hatrafele torteno felteteles ugrasok preferaltan "likely"-k, mig az elorefele ugrasok az "unlikely"-k.
- A hozzászóláshoz be kell jelentkezni
"likely"
Mivanmivanmivan? :)
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Ha egy instruction pipeline talalkozik mar a fetch/decode szakaszban egy hatrafele ugrassal, akkor - noha nem tudja hogy a feltetel az teljesulni fog-e mert a pipeline meg nem jutott el odaig - szoval akkor feltelezi hogy a hatrafele ugras azert nagyobb esellyel fog megvalosulni mint ahogy a kod tovabbmegy. Es pl nem dobja el az instruction cache-t mondvan hogy "ah, joesellyel ujra fog kelleni majd mindjart onnan valami, es mar azt is tudom hogy kb micsoda". Egy elorefele torteno felteteles ugrasnal meg nem fog csak azert prefetch-et inditani (a cache tobbi reszenek a karara) csak azert mert "jaj, lehet hogy elore kell majd ugrani".
- A hozzászóláshoz be kell jelentkezni
Tehát azt mondod, hogy nem a következő utasítást prefetch-eli, hanem azt, ahova ugrana vissza?
Fura, mert hardwareben agyatlanul könnyű ezt implementálni. A következő utasítást prefetch-eljük, amíg az aktuálist végrehajtjuk. Ezért, ha ugrunk, elbuktuk a prefetch-et, s az ugrás után kell egy fetch majd egy végrehajtás. Kis PIC-eknél ezért kétszer hosszabb időben az ugrás, mint a többi utasítás. Valójában nem az ugrás a hosszabb, hanem az utána lévő utasítás előtt van egy fetch, de az gyakorlatilag ugyanaz.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Ennel sokkal egyszerubb. Nezz elso korben egy fetch -> decode -> execute -> writeback pipeline-t. Hogy a felteteles ugras az elore vagy hatrafele mutat, az a decode szakasznal a relativ offset legfolso bitjebol mar azonnal kiderul:
- Ha ez a bit 0, akkor az ugras elorefele mutat majd, az az "unlikely" eset, tehat nem csinalsz semmit, mert a fetch altal behozott utasitast nagy valoszinuseggel ugyis fel kell dolgoznod. Tehat itt ez a preferalt.
- Ha ez a bit az 1, akkor pedig egy sima offset aritmetikanak (az egy darab offset szelessegu full adderrel vett) eredmenyet vizsgalva le tudsz szolni a cache modulnak hogy "jaj, barmi is legyen, ne urits ki semmi cache-t mert ugyis kellni fog majd onnan valami." Mivel ez nem a pipeline resze, ezert az elektronikanak a kritikus uthosszat nem fogja novelni mert parhuzamosan tortenik ez az osszeadas magaval a pipeline-nal. Szoval megintcsak jol ki tudja hasznalni a mikroarchitektura a dolgot.
Es akkor gondolj bele abba hogy egy ennel hosszabb instruction pipeline eseteben a decode elotti reszt nyugodtan tekintheted egy L(-1) cache-nak is akar, vagy abba hogy pl egy szelesebb instruction busz eseten a busz kimeneti regiszteret pedig akar egy L0-s cachekent is felfoghatod. Ezek a fenti dolgok pl mar egy 16bites ISA szelesseg es egy 32bit szeles instruction bus eseten is megjelennek mar azonnal (tehat 50% hogy mar ezzel is nyersz egy orajelet, ha keveset kell visszaugranod... oke, ez a gyakorlatban busy wait-es eseteket leszamitva nem gyakori, de ugye ki hasznal mar elesben 32 bites ISA buszokat).
- A hozzászóláshoz be kell jelentkezni
Ja, hogy az utasítás pipeline bizgeréli a cache controllert. Jellemzően kisebb, egyszerűbb hardware-ekben gondolkodom. :) Egyébként világos, amit írtál. Bár hozzáteszem, a while (0) simán megy tovább, szóval ott mindenféle szólás nélkül is megvan már a prefetch is, és a cache tartalom is. Előre ugrás inkább akkor válik nyűgössé, ha az valóban ugrás és nem a következő utasítás.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Nem feltetlen a "bizgereles" szakkifejezest hasznalnam itten, inkabb ugy mondanam hogy "hint-et ad" :) Peldaul mar az is lehet egy hint hogy nem kell "kapkodni" a prefetch szakasszal. Ezzel az "L0 cache" el tudja engedni a kovetkezo oraciklusban a rendszer busz-matrixat vagy arbiter-et, es igy egy masik varakozo (pl DMA vezerlo, vagy memory mapped periferia) fogja nyerni azt az orajelet tutira. Mert masik esetben akkor (az egyebkent nagyobb prioritasu) fetch az elveszi ugyan az orajelet mas elol, de tok feleslegesen mert joesellyel ugyis dobni fogja majd azt az utasitast amit kiszedett. Szoval ezzel mar egy olyan mikrovezerlonel is nyerhetsz, amiben multi-master konfiguracio van. Azaz pl egy DMA. De meg ez sem kell feltetlen, mert egy Neumann architekturanal pedig egy sima load/store jellegu muveletet se kell hogy feleslegesen visszatartson (ami ugye mar kicsit hatrebb van, kicsivel vagy joval a decode szakasz utan).
- A hozzászóláshoz be kell jelentkezni
Szerintem arra gondolt, hogy egy függvényhívás és egy értékadás lesz minden macro hívásból, nem pedig csak egy függvényhívás.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Hazudnék, ha azt mondanám, hogy értem, ez miért kell megegye az eltérő típust.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Egy kis magyarázat a void** problémához: https://c-faq.com/ptrs/genericpp.html
- A hozzászóláshoz be kell jelentkezni
Koszi, ez nagyon jo/hasznos. Meg ezt a hivatkozast is erdemes megnezni: https://c-faq.com/null/machexamp.html
- A hozzászóláshoz be kell jelentkezni
Off: ez még a Nyomasek Bobó-féle listára is felkerült.
- A hozzászóláshoz be kell jelentkezni
Jogos, mert nem tudja a dereferált pointer által mutatott adatszerkezet méretét, de felépítését, szerkezetét sem. Ellenben itt szerintem ezzel semmi baj, mert a free()-nek tényleg csak a lefoglalt memória címe kell, a kernel nem tud arról, mit tároltunk oda, azt hogyan használtuk fel. Csak adott címtől adott hosszúságú terület.
Az viszont izgalmasabb, amit a cikkben hivatkoznak, miszerint a NULL pointer nem mindig (void *) 0, amiből az következik szerintem, hogy az if (p != NULL) lesz a hordozható alak, az if (p) nem ekvivalens vele, bár éppen PC-n jó, meg PIC32-n is, ami MIPS.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Ez két külön bobóság, az egyik, hogy Strupstrupnak hála a NULL esetleg nem pointer, a másik az, hogy teoretikusan lehet olyan platform, ahol 0-t (vagy NULL-t) adsz értékül egy pointernek, de a compiler valami más, nemnulla értéket generál belőle (pl. 0xdeadbeef).
Ettől még a két if-ed ekvivalens.
- A hozzászóláshoz be kell jelentkezni
Mert a fordító okosan csinálja, nem pedig bután numerikusan 0-ra vizsgál? Ha így van, az viszont jó hír.
NULL esetleg nem pointer
Ha nem pointer, akkor mi? Már csak azért is pointernek gondolnám jónak, mert pointerrel hasonlítjuk össze, illetve pointer típusú változó kapja értékül.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Strupstrup uram szerint
#define NULL 0
- A hozzászóláshoz be kell jelentkezni
PIC32 MIPS-en is 0 volt, mígnem feltalálták valamelyik verziótól, hogy legyen (void *) 0. Elég nehezen értem, hogy a 0, mint szám, hogyan hasonlítható pointerrel, bár az is igaz, hogy nem változóban van, nincs is típus adva neki, szóval kezdek kicsit elveszni, hogy is van ez.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Inkabb az a kerdes hogy az adott architektura mit is tarol a 0 cimen (es/vagy melyik 0 cimen a sok kozul). Ha eleve nem megengedo (mint pl az ARM), vagy nincs is oda kepezve semmi (RISC-V) vagy nagyon tudod hogy mit akarsz csinalni ha oda irsz (klasszik x86, vagy az AVR) akkor siman lehet NULL = 0 = szam, ami nulla. Ha van MMU-d, akkor is lehet NULL = 0, foleg ha a magasabb privilegizalasi szinten sem lesz gond ebbol (ld. elobbi lista). A fentebb linkelt doksi is inkabb (szamomra legalabbis) egzotikus architekturak eseten mondja azt hogy a NULL az nem a szam ami nulla. Es ott biztos megvan az oka.
- A hozzászóláshoz be kell jelentkezni
Nem erre gondolok, hanem az alábbi két lehetőségre:
#define NULL 0
#define NULL ((void *) 0)
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
C-ben a 'void *' generikus pointer, de Strupstrup uram ezt nem tűrhette, ezért a char *p= (void *)p
inkompatibilis pointerek közötti értékadás lenne. Cserébe bevezette azt a szabályt, hogy az integer nulla literál kompatibilis minden pointerrel.
- A hozzászóláshoz be kell jelentkezni
Ha jól értelek, itt az a trükk, hogy nem bármilyen számra igaz ez, csak a 0-ra.
Kicsit olyan érzésem van, mint amikor a nullvektorról azt mondjuk, hogy az mindenre merőleges, mert akkor továbbra is igaz, hogy merőleges vektorok skalárszorzata nulla. :)
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Jöhet a slusszpoén? Miután tönkreb...arkácsolta a NULL-makrót, kitalálta a `nullptr` cimű okosságot.
- A hozzászóláshoz be kell jelentkezni
Itt én már le vagyok maradva. Az meg mi? Én a kódjaimban - nem C++, csak C -, mindig a NULL makrót használom, ha ilyesmi kell. Eddig nem volt bajom belőle.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
> Strupstrup uram
az meg ki? ismerni kene?
- A hozzászóláshoz be kell jelentkezni
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Egyszer őszintén megmondta a tutit, kár hogy csak hoax volt: https://www-users.york.ac.uk/~ss44/joke/cpp.htm
- A hozzászóláshoz be kell jelentkezni
Nekem ez a kedvenc definiciom:
#define NULL __null
- A hozzászóláshoz be kell jelentkezni
A preprocessorok, statikus elemzők is szeretik az ilyen `#define va_list _gcc_builtin_va_list` -szerű typedef-eket.
- A hozzászóláshoz be kell jelentkezni
És akkor ki kell nyomozni, hogy a __null mi a fene. Gondolom, az ilyesmire a fordító dokumentációja adja meg a választ.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Vagy rögtön a cpp ${CPPFLAGS} -C -dDI foo.c foo.E
- A hozzászóláshoz be kell jelentkezni