Pointer filozófia

 ( jbworld | 2009. június 2., kedd - 20:25 )

Üdv!

C/C++/Obj-C-ben irogatok programokat mostanság (C# felől érkezvén), és még senkinek sem kellett belenyúlnia ezekbe, ám ez nem marad mindig igy. Van egy (valószinűleg) mások számára elég idegesitő, ámde mindig konzisztensen követett stilusa annak ahogy a kódot irom:

int kever(pakli* a)
{
TAB kód;
TAB kód;
}

Ebben ugye két dolog van amit úgy látom a "profik" nem igy csinálnak: a pointer csillagozása, illetve a kapcsoszárójelek. A kapcsos zárójelről csak annyit, hogy szerintem ez inkább egyéni preferencia kérdése, és szerintem igy sokkal átláthatóbb. Na de a pointerek.

Egyszer tudom azt, hogyha ezt csinálom:
int* p1, p2;
akkor a p2 nem lesz pointer. Ez lehetne indok arra hogy inkább ne oda tegyem. De! Nekem valami filozófiailag bűzlik ezzel. Mert ha én deklarálok valamit, akkor:
TIPUS NÉV;
formában teszem. Márpedig p1 tipusa nem int, hanem int* - vagyis int-re mutató pointer.
Egyébként én pont emiatt kerülöm is az ilyen csoportos deklarációkat. Egyszerűen nem értem ezen kivül mi oka kell legyen annak, hogy a deklarációban máshová tegyem a csillagot.

Idegesiti-e vajon a kódomat néző többi embert ha ez a kód igy van formázva? Mert őszintén szólva engem idegesit a "szabványos" verzió. Ti milyen módon formázzátok a kódotokat?

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

valoban helytelen ugy

--
When in doubt, use brute force.

a * mar nem a tipus resze. ahogy csinalod, az meg szornyu.
nincs "int*" tipus, erted..

en ugy szoktam, hogy mindenutt tabolok, mindenutt (1 sorhoz is!) kirakom a { es }-t, a metodus szignatura-deklaraciojaval egy sorban kezdem a torzset. (ugyanez igaz osztalyra is)

Hát inkább a típus része, semmint a névé.
Amúgy ez egy egész aranyos leírás.

> Hát inkább a típus része, semmint a névé.

"TIPUS NEV;" , amikor az értékre van szükséged, írd le a "NEV"-et.
"TIPUS *NEV;", amikor az értékre van szükséged, írd le a "*NEV"-et.

Szóval a "pointerség" az a névhez kapcsolódik, aszerint hogy a név közvetlenül (nem pointerként) vagy közvetve (pointerként) hivatkozik az értékre. Szerintem.

De amúgy ahány nyelv, annyi szokás.

Elég szubjektív végül is, igaz.

Ha Stroustrup bácsihoz visszamegyünk, ő bizony azt írja:
"For a type T, T* is the type ‘‘pointer to T.’’ That is, a variable of type T* can hold the address of an object of type T."
Tehát kicsit nyakatekerten ugye, de létezik olyan, hogy T* típus.
(És igen, ő végig a ‘‘T* valtozo‘‘ kombinációt használja.)

Más: gyakran függvénydeklarációkban elhagyjuk a paraméterek nevét. Azaz a void fgv(T p); helyett csak egy void fgv(T); illetve mutatók esetében void fgv(T * p); helyett void fgv(T*); lesz. Vagyis egyszerűen csak típusmegjelölés.

Többféleképpen lehet értelmezni a mutató filozófiát, de a fentiek alapján nekem mindig is típus közelibb lesz. :)

> Tehát kicsit nyakatekerten ugye, de létezik olyan, hogy T* típus.

Nincs ellenemre ez a felfogás, de sajnos a szintakszis nem ennek megfelelően lett kialakítva. Nézzünk egy példát:

T *p1, v2;
v2 = *p1;
p1 = &v2;

A példából jól látszik, hogy a 'v2' az egy 'T' típusú érték neve, a 'p1' pedig egy 'T' típusú érték névének a neve. A '*' az egy operátor, ami egy név nevéből (p1) egy érték nevét (*p1) előállító műveletet jelöl. Az '&' operátor említendő még itt, ami egy érték névéből nevet előállító operátor.

Kicsit nyakatekert ez a "név neve" dolog, tán nem is tudtam érthetően leírni, de legalább összevág a szintakszissal.

> Vagyis egyszerűen csak típusmegjelölés.

A paraméter nevének elhagyása, az egyszerűen csak névelhagyás. Az hogy a név elhagyható, nem jelenti azt hogy a maradék szövegrész szintaktikailag egyetlen egységet kéne hogy képezzen (egy T* típusmegjelölést).

> de a fentiek alapján nekem mindig is típus közelibb lesz.

Nem gond, úgyis csak az számít, hogy ha kódot írsz, az működik-e vagy sem.

"> Tehát kicsit nyakatekerten ugye, de létezik olyan, hogy T* típus.

Nincs ellenemre ez a felfogás, de sajnos a szintakszis nem ennek megfelelően lett kialakítva. Nézzünk egy példát:"

A szintaxis szerintem csak azért olyan, hogy megőrizze a C-vel való kompatibilitást.

KisKresz

"A paraméter nevének elhagyása, az egyszerűen csak névelhagyás. "

Hat erted, ez az. :) A nevet el lehet hagyni, a tipust nem. Ha van * vagy &, akkor azt sem.
Ergo a * es az & jobban kozelit a tipushoz, mint a nevhez. :)

> Ergo a * es az & jobban kozelit a tipushoz, mint a nevhez.

Akkor a kifejezésben előforduló '*' és '&' nem ugyanaz, mint a deklarációban előforduló.

T *p1, v2;
v2 = *p1;
p1 = &v2;

Ellenben a névhez kötődő '*' és '&' ugyanazt jelentik, akár deklarációban, akár kifejezésben fordulnak elő.

Ezekben a deklarációkban mi a típus, amihez szerinted a '*' kötődik?

void (*signal(int sig, void (*disp)(int)))(int);

int add(int,int);
int (*op)(int,int)=add;

A példákban jól látszik, hogy a '*' a zárójelek miatt nem kapcsolódhat semmi máshoz, csak a 'disp' és az 'op' nevekhez. A zárójelek alkalmazása nem meglepő, ha a '*' egy operátor, aminek precedenciája van.

Mas vegerol kaptad el a temat ugy tunik.
Elismerem, aranyos ez a fuggvenymutatos pelda, de az is biztos, hogy 100 esetbol 90-szer nekem a hagyomanyos mutatos elgondolasok jutnak eszembe, amiket feljebb is irtam.

Ne keverjuk!

Van a deklaracio, ahol leirod a tipust, pl. int*

Es van a definicios reszben a * (azt hiszem, dereferencia nevu) operator, amivel a pointer altal mutatott ertekhez fersz hozza.

Rakjatok ra egy typedef-et oszt csa.
--

()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

Pedig de. C++-ban az összes *, &, const, és volatile a típus része. Ld. pl. std::set<Z*>

Ezért hát +sok az int*-nak, C++-ban legalábbis mindenképp. De ha mutatsz olyan cpp projektet, ahol nem így használják, az érdekel.

nem vagyok C++ guru, szoval tevedhettem is. :)

de ha a tipus resze lenne, akkor kutya kotelessege lenne az int* a, b deklaracio utan a b-nek is mutatonak lennie.

Igen, de sajnos C kompatibilitás szopó.. marad a typedef.

igy van.
a typedef tipus* pojnter;
pojnter a, b;
eseten meg azert lesz mindketto pointer, mert a typedef kulon hat a valtozokra, mintha egyesevel irtad volna.
Imadom.

--
"SzAM-7 -es, tudjátok amivel a Mirage-okat szokták lelőni" - Robi.

> int* p1, p2;

"Every declaration should be for a single variable, on its own line, with an explanatory comment about the role of the variable. Declaring multiple variables in a single declaration can cause confusion regarding the types of the variables and their initial values. If more than one variable is declared in a declaration, care must be taken that the type and initialized value of the variable is known." (CERT Secure Coding Standards)

Azért ezzel nem tudok egyetérteni: "with an explanatory comment about the role of the variable"

Mert ugye két dolog lehetséges:

int n; // Number of Anyamkinnya
...

Na törjön le az ilyen keze.

Ha viszont:

int numOfAnyamkinnya; // Number of Anyamkinnya
...

akkor tökéletesen felesleges a komment.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

> int numOfAnyamkinnya; // Number of Any
> akkor tökéletesen felesleges a komment.

Azért az némi magyarázatot igényelhetne, hogy miért "sima" int, és nem unsigned int. Vagy hogy lehet-e == 0.

A kommenteknek nem az a lenyege, hogy mi van ott a kodban, hanem miert van ott.

http://www.codinghorror.com/blog/archives/000749.html

A kapcsos zárójelekkel mi lenne a gond?
Ami a *-ozást illeti, én is így jelölöm, én is így látom logikusnak!
Nem így kéne, ez rendben van, de pl. hogy néz ki ha referenciaként használunk egy függvényben tömböt?
int fvNeve(int *&tomb) {
}
Ez így nem ocsmány? :)

Egyébként én is ma indítottam hasonló topicot, indentation-ökről, kapcsoszárójelekről.
És arra a következtetésre jutottam, hogy fölösleges "szabványos" módon programozni.. (márha lennének egyáltalán szabványok).
K&R vagy ANSI ha C++-ról van szó, és TAB vagy SPACE.
Ma reggel még úgy gondoltam hogy mától Stroustrup módra fogok C++-ban programozni, de amint belelapoztam a könyvbe, láttam hogy hányaveti átgondolatlan a kód kinézete.
Például:
case Lexer::NAME:
{TAB double &v = table[Lexer::ASSIGN) v = expr(true);
TAB return v;
}

Rendben, ha valaki kulon sorba szereti a kapcsos zárójelet .. de abba a sorba minek ír ??
A zárójelezés már a suliban jelen lévő dolog volt: van aki sűrűn, valaki szellősen írt jegyzetből szeret tanulni. Sztem ez ugyanerről szól.

Nem vagyok benne biztos, hogy nem fordításkor keveredett ez bele...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

+++
Nem az első kiadásnál jár már a könyv, és nem is mondanám, hogy hányaveti az ürge. :)

És arra a következtetésre jutottam, hogy fölösleges "szabványos" módon programozni.. (márha lennének egyáltalán szabványok).

Kivéve ha a kedvenc szövegszerkesztőd kedvenc nyelvi támogató modulja szigorúan egy formázást támogat.

Pocsek szovegszerkeszto lehet...
--

()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

C#-ban tudomásom szerint referenciák vannak, és nem pointerek, akkor a C++ nyelv esetén miért ragaszkodsz a pointerekhez? Felesleges, sőt, helytelen gyakorlat. A referencia biztosan mutat valahova (létező, azonos típusú objektumra), a pointer meg leginkább oda, ahova (nem) akarom.

Ez most komoly?

while (!sleep) sheep++;

Kutyafuttában írtam, ha esetleg rövidnek találod, ezért van. Mi a baj vele?

Formázásról meg teljesen értelmetlen beszélni, az a lényeg, hogy aki írja, el tudja olvasni. Ha meg nem egyedül írja a kódot, akkor meg vannak megegyezésen alapuló formázási stílusok (pl. K&R legtöbb esetben, kivéve...).

Hogy a címhez kapcsolódjak: a T* az ideális választás, mert ha a "T* a" vagy "T *a" után az "a" változóval dolgozol, akkor annak a típusa egy T-re mutató pointer, azaz "T*". Ezért idegen számomra még mindig a "T *a", ami meg C-ben gyakori.

Csak a hozzaszolasodbl ugy tunt, mintha a pointereket mindenhol lehetne / erdemes lenne helyettesiteni referenciaval C++-ban, ami azert nincs feltetlen igy.

while (!sleep) sheep++;

Lényegében minden függvényparaméter esetén érdemes helyettesíteni referenciával. A konkrét példában határozottan ebben a kontextusban szerepelt. Persze, nem minden esetben működik e megoldás, például sztringeket (karaktertömböket) csak mutatóként lehet átadni, vagy pedig std::string-re konvertálva referenciaként, de ez utóbbi elég ronda.

A referenciával van egy olyan gond, hogy nem látszik hívásakor, hogy az adott változó megváltozhat a fv hatására.

Emiatt pl Qt-ben minden constref ami lehet, és mutató ami megváltoztatja az argumentumot.

Nekem ez kicsit idegen, többnyire elfelejtem (kevés ilyen van), de van benne logika...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Több, mint tíz éve C++-ozok, de nekem speciel tökmindegy, hogyan van indentálva egy kód, hol a csillag, stb. Nekem konkrétan annyi igényem van, hogy legyen konzisztens egy projekten belül.

Én pont így használom ahogy te.

Egyébként meg tökmindegy, csak konzisztens legyen projekten belül. Ha te kezdted a projektet te dirigálsz, ha máshova kontárkodsz, kövesd az ottani szokásokat.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Én is int*-ot írok, dolgoztam projektben, ahol mindenki így tett. Meg olyanban is, hogy egyedül én írtam így.

Tök mindegy. El fogják tudni olvasni így is, úgy is.

Sőt, akkor is, ha int * p1-et írsz.

És én is pont azért csinálom így (meg mindenki, aki ugyanígy írja), mert úgy gondolják, hogy int* az az int-re mutató pointer típus, és p1 a változó.

*p1 meg az értéke (p1 változóból a * operátor kiveszi a zokszigént)

G

Hmmm én is programozok C-ben (bár magamtól tanultam senki sem segített, így tuti vannak hibáim) azt hittem eddig, hogy a kettő ekvivalens:
int* p1, p2, p3;
int *p1, *p2, *p3;
Mondjuk ez eddig nekem hibát nem okozott, mert én mindig a 2. megoldást használtam. Nekem jobban tetszik, ill. kevésbé valószínű, hogy tévedek.

mert a 2. a helyes, HA 3 pointert akarsz.

A * a változóra hat, nem a típusra. Azért lehet a *-ot hozzáírni a típushoz, mert a szabvány megengedi (pedig csalóka).

--
"SzAM-7 -es, tudjátok amivel a Mirage-okat szokták lelőni" - Robi.

Az első esetben int*, int, int jön létre.
A másodikban int*, int*, int*.

Nem ekvivalensek.

Igen már tudom. Ugyan olyan mintha:
int *p1, p2, p3; Szerncsére csak magamnak progamozok ( C-ben, közözben Delpizni kell :( ), és mindíg a 2. utat használtam. Szóval véletlenül a jót.

Figyelj!

Szokd meg, hogy egy sorban egy valtozot deklaralsz, es nem lesz semmi gubanc

Raadasul meg a kod is olvashato lesz.

Milyen fasza lenne, ha lehetne zarojelezni ;) azaz a

(int *) p1, p2, p3;

ekvivalens lenne a

int *p1, *p2, *p3;

sorral.

typedef int* int_ptr;
int_ptr p1, p2, p3;

typedef void* pointer

lolz, lattam valahol ilyet

--
When in doubt, use brute force.

Haha, epic typedef:))

glib-nek hívják. És persze micsoda dolog elhagyni a "g" betűt, ami nem "g"-vel kezdődik... na olyan nincs. Tehát:

typedef void* gpointer;

;)