Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: C-aiheinen blogi, asiavirheitä?

Sivun loppuun

maz [10.09.2008 21:23:23]

#

joo-o. Eli innostuin tuossa kesällä kirjoittelemaan blogia c kielestä suomeksi. Ja koska tavoitteeni on kirjoittaa osin haastavistakin aiheista ja ihan suomeksi - madaltaakseni koodarinalkujen kynnystä syventää osaamistaan - olisi toivottavaa, että kaikki asiavirheet karsiutuisi heti alussa. Siis pieni pyyntö, mikäli joku jaksaa lueskella sitä läpi, ja tehdä pientä tarkistustyötä niin olen äärimmäisen kiitollinen. Ja jottei jengi luule minun vain mainostavan sivuani,en laita linkkiä tähän viestiin. Se on profiilissani.

Kray [10.09.2008 22:15:31]

#

Mielenkiintoisesti ja helposti ymmärrettävästi olet säikeet ainakin selittänyt :)

maz [10.09.2008 22:52:13]

#

Kiitän palautteesta :) Ja mikäli lisä esimerkkejä tai jonkin tietyn aiheen käsittelyä kaivataan, niin kertokaa. (ei kuitenkaan grafiikkajuttuja, niissä olen amatööri :D ) Ja ennen kaikkea huomauttakaa kaikista virheistä, niin isoista kuin pienistäkin :)

Metabolix [10.09.2008 22:54:41]

#

Et pyytänyt kommentteja kielellisestä ilmaisusta ja esitystavasta, mutta mainitsenpa kuitenkin, että minua ainakin kielivirheet häiritsivät jatkuvasti ja turhan pitkä ja toistava ilmaisu taas teki tekstistä padallisen riisipuuroa, jossa on vain pari mantelia. Jälkimmäisen ehkä voi antaa anteeksi sen perusteella, että noin oppaasta voi ehkä tietämättöminkin lukija saada jotain irti. :)

Minusta on väärin sanoa, että static toimisi C++:n kanssa eri tavalla kuin C:ssä, koska vanhat merkityksethän ovat aivan samat. Käyttötapoja vain tulee yksi (kaksi) lisää.

Väität myös, että kääntäjä laskisi, paljonko muistia ohjelman pino tulee vaatimaan. Näin se ei tee, ja paljon paikallisia muuttujia sisältävien, syvästi rekursiivisten funktioiden kanssa raja tulee vastaan melko helpostikin. Monet käyttöjärjestelmät sijoittavat pinon sellaiseen kohtaan muistia, että sen kasvamista on helppo valvoa ja että sitä voi helposti kasvattaa ohjelman tarpeen mukaan — tiettyyn maksimikokoon asti. Stack overflow ei ole lainkaan harvinainen virhe.

"— — käyttämättömästä muistialueesta "kedosta" (heap) — —"
Tämä on ainakin minulle aivan uusi käännös. Keto on luonnonkasveja kasvava viheralue. ^^ Keko ja kasa kuulostaisivat heapin käännöksinä tutummilta. Voin tosin olla vain asiasta tietämätönkin.

Voisit ehkä kutsua pinoa pinoksi, kun kerran selvä suomenkielinen sana on olemassa.

En käsitä, miksi funktio-osoittimet aina esitetään tuolla syntaksihirviöllä, joka luultavasti on monelle aloittelijalle aivan käsittätön. void*(*fp)(void*_void), selkeää? Mikset neuvoisi tuolla, että ne voi määritellä tutumminkin ja niin, että on varmasti selvää, että kyseessä on osoitin:

void *oikea_funktio(void *_void);

// Rumasti IMO, siis ainakin aloittelijan kannalta epäselvästi
typedef void *(*fosoitin) (void *_void);
fosoitin oso1 = oikea_funktio;

// Selkeästi, aivan kuin tavallinen funktioesittely, vain typedef edessä
typedef void *funktio (void *_void);
funktio *oso2 = oikea_funktio;

// Kutsu
oso1(NULL); oso2(NULL);

Funktio-osoittimeen sijoittamisessa ei tarvita &-merkkiä eikä funktiota kutsuttaessa *-viritelmää. Kyseessä ei ole mikään "uusimpien kääntäjien erikoisuus" vaan ihan oikea C:n standardin määrittelemä toimintatapa.

Vaikka onkin hyödyllistä osata lukea outoja määrittelyjä kuten int*(*(*)(float, char))(int, double), on tämäkin ehkä järkevämpää kirjoittaa parille riville, kas näin:

typedef int * funktio_A (int, double);
typedef funktio_A * funktio_B (float, char);
funktio_B *ptr;
// osoitin funktioon, joka ottaa floatin ja charin ja palauttaa osoittimen funktioon, joka ottaa intin ja doublen ja palauttaa osoittimen inttiin.

Oma esimerkkimäärittelysi "int* *(*foo)(int *,char *)(double,int *);" meni pieleen. Jos haluat tuollaisella brassailla, helpoin tapa generoimiseen on kirjoittaa oikea asia esittämälläni yksinkertaisella tavalla, määritellä osoitin (yllä ptr) ja sijoittaa siihen jotain virheellistä, jolloin C++-kääntäjä ilmoittaa vaikkapa näin:
error: invalid conversion from 'void (*)()' to 'int* (* (*)(int*, char*))(double, int*)'

Tässä samalla katson kumonneeni väitteen, että tyyppimäärittelyt olisivat erityisen kryptisiä, koska, kuten näkyy, ne voidaan kirjoittaa hyvin ymmärrettävästikin. Tämä tosin vaatii sitä typedefiä, jonka itsekin neuvoit ratkaisuksi, mutta kyllä se typedeffikin on näin minusta selkeämpi. :)

Myös lopussa esittämäsi määrittely "char **(*foo)[](char *);" on pielessä. Hakemasi muoto on luultavasti "char *(*(*foo)[])(char*);".

Tässä nyt ajatuksia tällä kertaa.

Miksi et ole tarkistanut esimerkkiesi syntaksia? Aiheutat turhaa työtä itsellesi, kun joudut myöhemmin korjailemaan ja ehkä muuttamaan tekstiä, ja suurta harmia ja ärsytystä lukijoillesi, kun he eivät saa ohjelmiasi toimimaan.

maz [10.09.2008 23:17:50]

#

Vilpitön Kiitos asiallisesta palautteesta meta!

Korjaan virheitä joista ilmoitit, ja olet oikeassa siinä, että toimimattomat esimerkkipätkät ovat syvältä. Syy siihen miksen ole testannut koodia on se, että kirjoittelen blogia yleensä konella jossa ei ole kääntäjää asennettuna. Töissä taas ei suopeasti katsella, mikäli latailen koodinpätkiä työkoneelta nettiin :)

Summataanpa:
static: oikaisen väitteen että C ja C++:ssa static toimii eri tavoin.
stack -> pino, korjataan, samoin kääntäjän pinon estimointi väite. HYVÄ! kunnon kiistaton asiavirhe :) (Ja stack overflown olen saanut itsekin aikaan, tosin tilanteessa jossa luodessani uuden threadin, määritin itse stackin koon.

"kedosta" -> "keosta"

funktio-osoittimen esittely:
olen itse tottunut muotoon void *(*void_osoittimen_palauttava_funktio)(...);
Ja syy on se, että tämä muoto oikeasti kertoo sen mikä otus on kyseessä. Eli osoitin funktioon joka palauttaa osoittimen.

Samasta syystä käytän &-merkkiä ottaessani funktion osoitteen - &-merkki nimen edessä kertoo heti, että tahdon saada jonkin otuksen osoitteen selville. Samoin *-merkki funktiota kutsuttaessa osoittimen kautta kertoo, että tahdon käyttää sitä mihin osoitin osoittaa. oletko 100% varma siitä, että C:n standardi sisältää nuo lyhyemmät muodot?? Jos olet, niin silloin minun on syytä korjata tekstiä...

'näin luet tyyppimäärittelyjä' esimerkit: tartksitan kun pääsen kääntäjän ääreen.

Vielä kerran Kiitos, arvostan oikeasti vaivannäköäsi!

ByteMan [10.09.2008 23:21:27]

#

nyt on pakko pistää offtopic: vaihteeksi joku kestää rakentavaa palautetta.. se on hyvä.
kaikki täällä varmaan tietävät/arvaavat mihin viittaan..

Grez [10.09.2008 23:30:54]

#

Tuota, eihän kääntäjän omalla koneella tarvitse olla, että voi testata kääntymiset. Tai ainakin itsellä on useita Shell-ympäristöjä käytössä.

maz [10.09.2008 23:41:50]

#

Grez kirjoitti:

Tuota, eihän kääntäjän omalla koneella tarvitse olla, että voi testata kääntymiset. Tai ainakin itsellä on useita Shell-ympäristöjä käytössä.

Joo-o. opiskeluaikana oli yliopiston palvelimet. Joskus oli kotona linux serveri, mutta tällähetkellä on vaan vaimon läppäri ja kännykkä...

Metabolix [10.09.2008 23:55:07]

#

maz kirjoitti:

funktio-osoittimen esittely:
olen itse tottunut muotoon void *(*void_osoittimen_palauttava_funktio)(...);
Ja syy on se, että tämä muoto oikeasti kertoo sen mikä otus on kyseessä. Eli osoitin funktioon joka palauttaa osoittimen.

Eikö aivan sama selitys kelpaa myös muotoon void *funktio(...)? Määritellään siis funktiota kuvaava tyyppi, ja osoitinmuuttujat ovat luonnollisesti osoittimia tähän tyyppiin. Vertaa:

typedef double pituus;
pituus *osoitin_pituuteen;

typedef int funktio(void);
funktio *osoitin_funktioon;

Minusta tämä tapa olisi syytä ainakin esitellä, koska hyvä opashan on sellainen, joka tarjoaa runsaasti tietoa, vaikkei sille kaikelle välttämätöntä tarvetta olisikaan. :)

maz kirjoitti:

oletko 100% varma siitä, että C:n standardi sisältää nuo lyhyemmät muodot??

Kyllä tämä minusta niin sanoo.

ISO/IEC 9899/1999 (C99) kirjoitti:

A function designator is an expression that has function type. Except when it is the operand of the sizeof operator or the unary & operator, a function designator with type "function returning type" is converted to an expression that has type "pointer to function returning type".

Suomeksi: jos funktion nimen edessä ei ole operaattoria sizeof tai &, funktio muutetaan lausekkeeksi, jonka tyyppi on vastaava funktio-osoitin.

Kyseinen muunnos tapahtuu myös silloin, kun funktiota kutsutaan, eli toisin sanoen funktiokutsussa ei koskaan esiinny itse funktiota (function designator) vaan aina pelkästään funktio-osoitin (pointer to function). Tämän muunnoksen perusteella mitä tahansa funktiota kutsuttaessa voi laittaa sen eteen mielivaltaisen määrän tähtiä: f() on sama kuin (***f)(), koska f muuttuu funktion osoitteeksi, *f muuttaa tämän osoitteen taas funktioksi, minkä jälkeen *f muuttuu taas osoitteeksi ja niin edelleen. Samalla perusteella myös ilmaus &f tai muuttuja f_osoitin kelpaavat kutsuttaviksi sellaisenaan: ensimmäisessä muunnos tehdään eksplisiitisti, jälkimmäisessä taas muunnosta ei tarvita lainkaan.

maz [11.09.2008 00:17:49]

#

Metabolix kirjoitti:

maz kirjoitti:

funktio-osoittimen esittely:

typedef double pituus;
pituus *osoitin_pituuteen;

typedef int funktio(void);
funktio *osoitin_funktioon;

Minusta tämä tapa olisi syytä ainakin esitellä, koska hyvä opashan on sellainen, joka tarjoaa runsaasti tietoa, vaikkei sille kaikelle välttämätöntä tarvetta olisikaan. :)

Ah. Nyt vasta bonjasin mitä ajoit takaa :)
Joo-o, tuon näyttäminen tuntuu ihan järkevältä!

lainaus:

maz kirjoitti:

oletko 100% varma siitä, että C:n standardi sisältää nuo lyhyemmät muodot??

Kyllä tämä minusta niin sanoo.

ISO/IEC 9899/1999 (C99) kirjoitti:

A function designator is an expression that has function type. Except when it is the operand of the sizeof operator or the unary & operator, a function designator with type "function returning type" is converted to an expression that has type "pointer to function returning type".

Suomeksi: jos funktion nimen edessä ei ole operaattoria sizeof tai &, funktio muutetaan lausekkeeksi, jonka tyyppi on vastaava funktio-osoitin.

Kyseinen muunnos tapahtuu myös silloin, kun funktiota kutsutaan, eli toisin sanoen funktiokutsussa ei koskaan esiinny itse funktiota (function designator) vaan aina pelkästään funktio-osoitin (pointer to function). Tämän muunnoksen perusteella mitä tahansa funktiota kutsuttaessa voi laittaa sen eteen mielivaltaisen määrän tähtiä: f() on sama kuin (***f)(), koska f muuttuu funktion osoitteeksi, *f muuttaa tämän osoitteen taas funktioksi, minkä jälkeen *f muuttuu taas osoitteeksi ja niin edelleen. Samalla perusteella myös ilmaus &f tai muuttuja f_osoitin kelpaavat kutsuttaviksi sellaisenaan: ensimmäisessä muunnos tehdään eksplisiitisti, jälkimmäisessä taas muunnosta ei tarvita lainkaan.

Näyttää siltä että olet oikeassa... Viimeinen oljenkorsi: myös ennen C99 standardia?

Ai niin, nippu pikaisia korjauksia naputeltu :)
Kiitti vielä kerran!

Metabolix [11.09.2008 00:34:02]

#

Kyllä se ainakin kääntyi ilman varoituksia GCC:llä lipuilla -ansi -pedantic -Wall. Vanhempaa standardia en jaksa ruveta netistä kaivamaan, voit toki itse tarkistaa asian, jos epäilyttää.

ville-v [11.09.2008 17:15:46]

#

C:ssä on kyllä jo explode()n tavoin toimiva funktio, nimittäin strtok() (string.h). Tosin toimii paljon käytännöllisemmin kuin PHP:n funktio koska ei palauta taulukkoa.

Grez [11.09.2008 17:25:09]

#

ville-v kirjoitti:

C:ssä on kyllä jo explode()n tavoin toimiva funktio, nimittäin strtok() (string.h). Tosin toimii paljon käytännöllisemmin kuin PHP:n funktio koska ei palauta taulukkoa.

Ei se kyllä minusta ole exploden tavoin toimiva. Vai mitä mahtaisi tulla jos toteutat seuraavan strtok():lla:

explode('<p>','Mikko<p>Reijo<p>Tapani<p>Ville<P>Aapo')

ville-v [11.09.2008 17:31:49]

#

Grez kirjoitti:

ville-v kirjoitti:

C:ssä on kyllä jo explode()n tavoin toimiva funktio, nimittäin strtok() (string.h). Tosin toimii paljon käytännöllisemmin kuin PHP:n funktio koska ei palauta taulukkoa.

Ei se kyllä minusta ole exploden tavoin toimiva. Vai mitä mahtaisi tulla jos toteutat seuraavan strtok():lla:

explode('<p>','Mikko<p>Reijo<p>Tapani<p>Ville<P>Aapo')

Eiköhän se mene

 data = "Mikko<p>Reijo<p>Tapani<p>Ville<p>Aapo";
muuttuja[0] = strtok(data, "<p>");
muuttuja[1] = strtok(NULL);
muuttuja[2] = strtok(NULL);
muuttuja[3] = strtok(NULL);
muuttuja[4] = strtok(NULL);

Funktion kun laittaa vielä silmukkaan niin hyvä tulee.

Grez [11.09.2008 17:36:15]

#

Ja oletan, että tuon jälkeen

muuttuja[0]=="Mikko"
muuttuja[1]=="Reijo"
muuttuja[2]=="Ta"
muuttuja[3]=="ani"
muuttuja[4]=="Ville"

ville-v [11.09.2008 17:43:54]

#

Grez kirjoitti:

Ja oletan, että tuon jälkeen

muuttuja[0]=="Mikko"
muuttuja[1]=="Reijo"
muuttuja[2]=="Ta"
muuttuja[3]=="ani"
muuttuja[4]=="Ville"

Voi olla että erotin pitää valita järkevämmin. C:ssä on sentään jonkinlainen kontrolli siinä, millaista dataa käyttäjä syöttää, joten jos erottava merkki on esimerkiksi \025, voi sen syöttämisen estää käyttäjältä. Html-käsittelijää ei sentään taida tulla C:ssä eteen.

os [11.09.2008 20:18:54]

#

ville-v kirjoitti:

Voi olla että erotin pitää valita järkevämmin. C:ssä on sentään jonkinlainen kontrolli siinä, millaista dataa käyttäjä syöttää, joten jos erottava merkki on esimerkiksi \025, voi sen syöttämisen estää käyttäjältä. Html-käsittelijää ei sentään taida tulla C:ssä eteen.

mitä ?

Millä perusteella C:ssä on yhtään enempää kontrollia siitä, mitä dataa ohjelmalle syötetään? C:llä on varmasti käsitelty myös HTML:llää, esimerkiksi ensimmäisissä webbiselaimissa. Vastaavaa merkkijononkäsittelyä voi edelleen joutua tekemään myös C:llä.

Grez [11.09.2008 20:27:45]

#

Niin, pointtihan mulla oli tuossa, että explode on merkkijonohajotin, jossa erottimena voi olla mikä tahansa merkkijono.

strtok taas pystyy pilkkomaan merkkijonoa ainoastaan yksittäisten merkkien perusteella, eikä mikään erottimena käytetyistä merkeistä voi olla hyötytekstin joukossa.

Sikäli siis se "ei minusta ole exploden tavoin toimiva"

maz [11.09.2008 22:56:22]

#

Itse asiassa explode on paljon mukavampi käyttää. strtok kun pitää kirjaa tokeneista sisäisesti kutsujen välillä Ts. on riskaabeli monisäikeisessä ohjelmassa (strtok_r on tosin reentrantti versio). Lisäksi strtok ja strtok_r muuttavat ensimmäistä argumenttiaan, eivätkä ainakaan kaikissa toteutuksissa toimi const charien kanssa.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta