Pointteri p osoittaa struktuurin alkioon. Miten saan muutettua sen osoittimeksi itse struktuuriin? Vieläpä standardi-C:llä, jos mahdollista!
C-pointteriongelma
Iso Ansi
10464
0Äänestä
Muut lukivat myös
Odottaa vastausta
Näihin keskusteluihin ei ole vielä vastattu. Ole ensimmäinen.
Vastaukset 10
Vanhimmat
Tarkoitko siis struktuurilla struct-tietuetta? Pitäisi tietää mihin alkioon se pointteri osoittaa. En usko, että on mitään geneeristä kikkaa, joka toimii joka tilanteessa. Parempi koodata se niin, ettei moista muunnosta tarvi.
Osoitin structiinhan on sama kuin osoitin sen ensimmäiseen alkioon, joten siinä erikoistapauksessa se onnistuu. Koodinpätkä selkeyttäisi ongelmaa huomattavasti.
Jos tiiät mihin alkioon p osoittaa lasket monettako struktuurin tavua se osoittaa ja muutat pointteria niin että se osoittaa ko. tavumäärän taaksepäin.. noin periaatteessa. Eri asia kannattaako tommosia säätöjä edes miettiä ei kuulosta kovin loppuunsuunnitellulta
Mikäli tajusin oikein, olette tekemässä jotain seuraavaa (C :aa C:lla):
struct A {
Some information;
void (*funkPtr)(struct A *a,Params *whatEver);
};
struct B {
struct A first; //HUOM alussa
//some extra information
};
nyt B:n osoittimesta saa osoittimen aika helposti
ja typecastaus onnistuu. KUITENKIN, jos first ei
ole alussa, niin homma menee monimutkaisemmaksi.
Joutuu laskemaan biteittäin, miten monta bittiä pitää vähentää alusta. Tämä on aika tuskaista ja
ankeaa puuhaa. Tuo ylläolevakin esimerkki rikkoo C:n pyhintä ansi-standardia, mutta toiminee käytännössä.
Eräs vaihtoehto (kauniskin :) tietty on, tehdä seuraava jäynä:
struct Owner;
struct Member {
struct Owner *owner;
//some data
};
struct Owner {
//some data
struct Member *member;
};
//alustaa tiedon oikein
//kaksisuuntaiset linkit on aina hieman
//suxoreita
void Owner_setMember(struct Owner *owner, struct Member *member) {
owner -> member = member;
member -> owner = owner;
}
struct Owner* Member_getOwner(struct Member *member) {
return member -> owner;
}
tähän tapaan... (Kannattaa tosiaan harkita vielä.
Olioiden palauttaminen ei kuulu olio-ohjelmointiin, eikä kai funktionaaliseenkaan tyyliin)
#include
typedef struct
{
char data;
int data2;
} Rakenne;
Rakenne *HaeRakenne(int *data)
{
Rakenne Tmp; /* antaa kääntäjän laskea :) */
/* Lasketaan offset tavuina */
int off = &Tmp.data2 - &Tmp; /* kääntäjä valittaa */
return (Rakenne *)(data - off);
}
int main (void)
{
Rakenne Tmp;
Rakenne *pTmp;
int *d = &Tmp.data2;
pTmp = HaeRakenne(d);
if (pTmp == &Tmp)
{
puts("onnistui");
}
return 0;
}
Asiallinen yritys, mutta esimerkkisi toimii korkeintaa "onnellisten tähtien alla", kun struktuurin aligment sattuu menemään sopivasti. Kokeile vaikka antaa kääntäjällesi optio, jolla struktuurit pakataan (ts. jäsenten väliin mahdollisesti jäävä tyhjä tila poistetaan) ja esimerkkiisi lakkaa toimimasta saman tien.
Jos "kääntäjä valittaa", se on jo yleensä merkki siitä, ettei homma mene aivan standardin mukaan, vaikka sattuisikin toimimaan.
offsetof() näyttää toimivan samalla periaatteella, mutta siinä on käytössä NULL osoitin eikä muutujaa. Ja segmentointi varmasti sotkee lisää. Huono ratkaisu...
Tuota aligment ongelmaa en ymmärrä, eikös väli ole sama kun osoitteilla pelataan (kääntäjän luulisi tietävän jäsenten välit) ?
Offsetof on osa standardia ja sen vuoksi se "toimii". Sen sijaan standardi ei määrittele miten offsetof laskutoimituksen tekee.
Jos kääntäjäsi käyttää NULL-osoitinta ja vähennyslaskua, jokin toinen kääntäjä johonkin toiseen ympäristöön, kenties, saattaa käyttää jotakin aivan muuta tapaa.
Offsetof on standardin mukainen ja siirrettävä. NULL-osoittimen ja vähennyslaskun käyttö ei sitä ole.
Alignment (valitan painovirhepaholaista ed. viestissäni) on hauska juttu varsinkin näissä "isoissa" koneissa. Alignment-vaatimusten vuoksi dataelementit sijoitetaan muistissa esim. parilliseen tai neljällä jaolliseen osoitteeseen. Näin ollen jos struktuurissa on peräkkäin vaikkapa char- ja long-tyyppiset muuttujat, näiden väliin jää todennäköisesti yksi tai kolme tavua tyhjää tilaa (gap). Tämän vuoksi struktuurin koko ei välttämättä ole sama kuin sen elementtien yhteenlaskettu koko.
Intelin prossut toimivat (tiettävästi edelleen) ilman alignment-säätöjäkin, mutta suorituskyky heikkenee. PC-maailman ulkopuolelta löytyy paljon ympäristöjä, joissa alignment on vaatimus, ei toivomus.
Tällainen "pointteriongelma" saattaa todellakin tulla eteen vaikkapa data-elementejä ketjutettaessa. Ongelmaan pitää löytyä myös standardin mukainen ratkaisu.
Edellisissä vastauksissa olikin jo kerrottu, että osoitinta (p) täytyy vähentää struktuurissa edellä olevien jäsenten kokoa vastaavasti. Tähän tarkoitukseet on standardissa määritelty oma työkalunsa, makro nimeltä offsetof (stddef.h). Makro palauttaa struktuurin nimetyn jäsenen offsetin ts. "sijainnin" ottaen huomioon alignmentin jne.
Offsetti on byteinä, eikä standardi tunne sellaista datatyyppiä. Tässä kohtaa onkin pakko tehdä pieni oletus. Helpoin sellainen on olettaa, että char (ts. sen koko) on sama kuin byte. Mutta ehkä sittenkin varmistamme asiaa niin pitkälle kuin mahdollista...
p on siis osoitin struktuurin s jäseneen j.
Osoitin (q) ko. struktuuriin lasketaan näin:
q = (struct s *)((char *)p - offsetof(s,j)/sizeof(char));
Tiedän, ettei tuokaan yhtälö toimi kaikissa ympäristöissä. 10 kpl laatikko tyhjiä CD-R-levyjä ensimmäiselle, joka vastaa ja kertoo mikä yhtälössä on vikana ja mitä ympäristöä tarkoitin! Toinen 10 kpl laatikko sille, joka pistää yhtälön paremmaksi. Sähköpostiosoite mukaan.
...kun tämä viestiketju putoaa pois ohjelmointipalstan 1. sivulta.
Mitä ilmeisemmin ongelma on aivan muualla, eli suunnitteluvaiheessa, jos tuollaista täytyy jo alkaa suunnittelemaan. Miksi yleensäkään edes haluat tehdä tuollaista ? Vaikuttaa purkkaviritelmältä, joka ei taatusti tule toimimaan kuin tietyssä alustassa tietyillä arvoilla tiettynä kellonaikana, eli keskiyöllä täydenkuun aikaan, jos silloinkaan.
void-osoittimen käyttöä voisi testata, mutta parempi vain suunnitella koko homma kunnolla alusta asti.