Pointer filozófia

Fórumok

Ü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ások

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é.

"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

> 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.

> 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

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.

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.

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.

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.

Milyen fasza lenne, ha lehetne zarojelezni ;) azaz a


(int *) p1, p2, p3;

ekvivalens lenne a


int *p1, *p2, *p3;

sorral.