typedef struct //rivi 17 { float x, y, z; float u, v; } vertex; typedef struct //rivi 23 { vertex vertex[3]; } triangle; typedef struct //rivi 28 { int numTriangles; triangle *triangle; } sector;
Tuo koodinpätkä aiheuttaa seuraavat herjaukset:
:25: error: declaration of ‘vertex triangle::vertex [3]’ :18: error: changes meaning of ‘vertex’ from ‘struct vertex’ :31: error: declaration of ‘triangle* <anonymous struct>::triangle’ :24: error: changes meaning of ‘triangle’ from ‘struct triangle’
Mitä tuolle keksisi tehdä?
Ei pitäisi käyttää tyypin nimeä muuttujan nimenä. Tuossa nyt tapahtuu suunnilleen sama kuin jos määrittelisit näin:
int int = 10;
Kääntäjä ei voi siis tietää, viittaatko vertexillä tyyppiin vai vertex-nimiseen muuttujaan. Hyvä tapa on merkitä tyypit jotenkin. C:ssä melko perinteisesti käytetään t_tyyppi-merkintää, kuten selviää standardikirjastoistakin: t_time, t_clock, eli t_vertex. Linuxin kernelin ohjelmointityylissä kielletään käyttämästä typedefejä (tai defineä) structien nimeämiseen, jolloin tyypin nimeksi tulee aina kirjoittaa struct tyyppi. Tietenkin tällöin pitää olla käytössä oikeasti C-kääntäjä, koska C++ sotkee tuonkin käytännön. itse merkitsen tyypit T-kirjaimella (tai osoitinten tapauksessa P-kirjaimella) Pascalista oppimaani tapaan: TVertex. Tätä monet C:n käyttäjät pitävät huonona tapana, mutta kukin koodaa tavallaan.
Okei, asia selvä. Vielä kuitenkin ilmeni yksi minulle tuntematon ongelma seuraavasta koodinpätkästä:
sector1.triangle = malloc(numTriangles * sizeof(Ttriangle));
numTriangles on ihan int luku, ja sector1.triangle on sama on sama kuin tuossa ensimmäisessä koodissa näkyy.
Ainiin se virheilmoitus unohtui. ^^
invalid conversion from ‘void*’ to ‘Ttriangle*’
Sinun koodissasi se t_ näyttäisi paljon selkeämmältä kuin T, kun käytät muutenkin pieniä kirjaimia enimmäkseen. :)
Tarvitset tyypinmuunnoksen muuttaaksesi muistinkäsittelyfunktion antaman tyypittömän osoittimen osoittimeksi tiettyyn tyyppiin. (Toiseen suuntaan muunnosta ei erikseen tarvita.) Eli sulkuihin laitetaan tyyppi, joksi sulkujen jälkeinen asia eli mallocin palautusarvo halutaan muuttaa.
int **osoitin = (int **) malloc(sizeof(int *)); osoitin[0] = (int *) malloc(sizeof(int)); free(osoitin[0]); free(osoitin);
Öh, en nyt ihan käsittänyt noita kahta ensimmäistä riviä, vaikka aivojen kuormitus yrityksen aikana lähenteli 110%. Pitääkö tuo eka rivi siis kirjoittaa vaikka näin:
int Kana = (int) malloc(sizeof(t_triangle));
Onko siis tuon jälkeen int Kanassa tuon t_trianglen koko ihan inttinä?
Tuosta toisesta rivistä en käsittänyt mitään... :D
Eieiei. Esimerkissä siis varasin int**-muuttujaan int*:n ja siihen yhden intin.
t_triangle *Kolmio = (t_triangle *) malloc(sizeof(t_triangle));
Ööh... Ehkä tämä kuuma ilma on tehnyt tehtävänsä, mutta minä jokatapauksessa olen pudonnut kärryiltä. Mitä tuo koodi nyt niinku tallentaa tuohon t_triangle *Kolmio -juttuun? Ja mitä tuo *Kolmio tuossa tekee?
Tumpelo kirjoitti:
typedef struct { int numTriangles; t_triangle *triangle; } t_sector;
Sieltä löytyy ihan omasta koodistasi se mainittu t_triangle *Kolmio
.
Muistinvarauksessa varatun muistin määrää ei tallenneta minnekään ohjlemasi saataville, joten sitä pitää itse kuljettaa muuttujassa. Sitä varten tuossa t_sector-structissa on numTriangles-muuttuja. Todennäköisesti siis teet käytännössä jotenkin tähän tapaan:
t_sector sektori; sektori.numTriangles = 1000; /* Varmaankin luet sen tiedostosta oikeasti... */ sektori.triangle = (t_triangle *) malloc(sektori.numTriangles * sizeof(t_triangle));
Noh, ymmärryksen puolella vielä ongelmia, mutta sain toimimaan joten ehdin perehtyä tuohon kohtaan sitten myöhemmin.
Usein syntaksi jo estääkin tyyppien nimien käytön muuttujaniminä, mutta vaikkei niin tapahtuisikaan, tapaa kannattaa välttää, sillä se tekee koodista epäselvää luettavaa.
C:ssä malloc
in paluuarvoa ei kannata (lähes) koskaan castata, sillä se piilottaa nekin tyyppivirheet, jotka kääntäjä muuten havaitsisi.
Jos koodin on tarkoitus olla C++:aa, silloin koko juttu kannattaa hoitaa eri tavalla: ei todellakaan typedef
ejä eikä ensinkään malloc
ia.
Joo, tosiaan C-koodi kannattaa tallentaa .c-tiedostoon ja kääntää gcc:llä g++:n sijaan, niin se toimii C:n standardien mukaan. IDEt useimmiten päättelevät tiedostopäätteen perusteella, kumpaa kääntäjää pitää käyttää, ja tallentaessa tahtoo nykyään tulla C++:n tiedostopääte oletuksena, jos ei erikseen päätettä kirjoita.
Aihe on jo aika vanha, joten et voi enää vastata siihen.