Sekalaista

 

Hyvän koodin kirjoitus

Tämä on tärkeää! Ellet halua, että koodisi on lukukelvotonta spagettia, ota tästä parit neuvot onkeesi. Toki koodin kirjoitustapoja on yhtä paljon kuin ohjelmoijiakin, joten nämä pohdiskelut ovat hieman yksilöllisiä asioita.

Itse kirjoitin melko vakiintuneella tavalla koodia parin vuoden ajan, kunnes lopulta taivuin enemmistön tahtoon ja muutin hieman kirjoitustyyliäni (kuvitelkaapa senaikaisia ohjelmiani; puolet kirjoitettu vanhaan tyyliin ja puolet uuteen...). Esittelen nyt käyttämäni tyylin tässä. Se on ehkä kaikista standardein tapa, mutta ei suinkaan mitenkään parempi kuin muut. Parasta tapaa ei ole. Kannattaa käyttää sellaista tapaa mikä tuntuu itselle mukavimmalta. Tietenkin omaksumalla melko yleisen tyyliin pääsee ehkä vähän helpommalla. Tärkeintä on kuitenkin noudattaa tyyliään orjallisesti joka paikassa.

C++:ssa on mahdollista laittaa koodin sekaan ns. näkymättömiä merkkejä, joita ovat välilyönnit, tabuloinnit ja rivinvaihdot. Eli lause:

if (a < b); 

voidaan myös esittää:

if
(    a   <
	b
  )              ; 

..jos välttämättä halutaan. Tätä ominaisuutta voidaan hyötykäyttää näin: Huono pätkä:

if(a<b&&b==10||b+k==0){a = 10;cout<<"Terve";}

Sama pätkä paremmassa muodossa:

if (a < b && b == 10 || b+k == 0)
{
  a = 10;
  cout << "Terve";
}

Näin. Kumpi on selvempi? Välilyöntiä kannattaa käyttää ahkerasti. if:ssä jätin b+k:n yhteen, koska minusta se on selvempi niin. Lohkot on tapana sisentää näin:

if (a == 10)
{
  a = 5;
  b = 8;
}

Tai näin:

if (a == 10)
  {
  a = 5;
  b = 8;
  }

Lisäksi yleensä on tapana kirjoittaa ensimmäinen aaltosulku samalle riville:

if (a == 10) {
  a = 5;
  b = 8;
}

Tuo lieneekin nykyään yleisin tapa kirjoittaa lohkoja.

Muuttujille kannattaa antaa selvät nimet, ab-tyylisiä muuttujia kannattaa välttää. Laskurimuuttujina ne kyllä kelpaavat, mutta pidemmät nimet ovat silti pitkän päälle parempia: ne eivät vie enempää muistia tai ole hitaampia, niitä on vaan hitaampi kirjoittaa. Mutta on aika tylsää selailla koodia läpi ja selvittää, että mitäs siinä xp-nimisessä muuttujassa taas olikaan.

Muuttujien ja funktioiden nimissä kirjoitan eri osat isolla kirjaimella, siis vaikka muuttujat keskiArvo ja poistoputkenValisuodattimenKriittinenLampotila ja funktiot Liiku() ja AmmuAleksi(). Kuten huomaat, funktiot aloitan isolla kun taas muuttujat pienellä. Näin ne ainakin erottaa nopeasti. Luokan nimen kirjoitan isolla, mutta luokkaan kuuluvan olion pienellä - sehän on periaatteessa vain muuttuja. Miksi luokat isolla? Luokan muodostinfunktio on funktio, joten se isolla. Luokalla on sama nimi kuin muodostinfunktiolla, joten senkin pitää alkaa isolla.

Muutamat wanhat parrat käyttävät muuttujien nimessä unkarilaista notaatiota. Se tarkoittaa, että muuttujien alkuun laitetaan yksi merkki kertomaan muuttujan tyypistä. iNopeus tarkoittaisi int-muuttujaa, jossa pidetään nopeutta. Unkarilainen notaatio auttaa muistamaan muuttujan tyypin ja olemaan käyttämättä sitä väärin. C++ hoitaa kuitenkin muuttujien tyyppitarkastuksen niin hyvin, että unkarilaisen notaation käyttö on melko turhaa C++-ohjelmissa. Poikkeuksena kuitenkin on melko yleinen tapa laittaa osoitinmuuttujien eteen pieni p (pointer) ja viittauksien eteen pieni r (reference).

Tässä oli melko löyhä esitys siistin koodin kirjoituksesta. Jos kaipaat tarkkaa ja mahdottoman laajaa ehdotelmaa C++-standardista, siis siitä miten vaikka firman sisällä pitäisi kaikkien kirjoittaa ohjelmansa, jotta muut niitä ymmärtäisivät, niin katso ihan asiallisen oloinen selostus osoitteesta:

http://www.possibility.com/Cpp/CppCodingStandard.html

En voi olla vielä painottamatta sitä, että siisti merkintätapa on vasta ensimmäinen askel hyvään ohjelmointiin. Ohjelman todellinen selkeys ei tule kivasti laitetuista sulkumerkeistä, vaan hyvin tehdystä, selkeästä ja yksinkertaisesta luokkarakenteesta. Mutta merkitseminen on myös tärkeää, ja kokeneilla ohjelmoijilla se on jo niin verissä, että ohjelmakoodi ei tunnu kotoisalta ennen kuin siinä on merkinnät kohdallaan.

 

Muistialueet

Tietokoneessa on muistia, johon se säilöö tietoa. Siinä perusteet. Sitten vähän syvällisempää tietoa. Muisti voidaan jakaa eri alueisiin. C++-kielen kannalta alueet ovat seuraavat:

Ensiksi mainittakoon pino (stack). Se on muistialue, joka toimii LIFO-periaatteella, Last In First Out. Siis mitä sinne on viimeksi laittanut, sen sieltä myös saakin. Tieto kasataan pinoon kuin lautaset kasaan. Pinoa käytetään paikallisten muuttujien ja funktion parametrien säilyttämiseen ja sen koko on rajattu - ympäristöstä, kääntäjästä ja sen asetuksista riippuen.

Globaali muistialue pitää sisällään kaikki globaalit muuttujat ja koodialue itse ohjelmakoodin. Prosessorin sisällä olevat rekisterit (register), joita on muutama, mutta jotka ovat myös ihan käsittämättömän nopeita, voidaan laskea tavallaan myös muistiksi. Niiden käyttö riippuu prosessorista ja kääntäjästä, yleensä niitä käytetään pinon kaverina pienen ja väliaikaisen tiedon säilyttämiseen.

Kaikki loppu onkin sitten villiä ja vapaata muistia. Vapaan muistin alueelta voi ohjelma varata muistia käyttöönsä. Vapaata muistia käytetään sen osoitteiden avulla - siis osoitinmuuttujien avulla.

 

C++:n varatut sanat

Varatut sanat vaihtelevat hieman kääntäjittäin, mutta melko yleispätevä lista on tässä:

auto
break
case
catch
char
class
const
continue
default
delete
do
double
else
enum
extern
float
for
friend
goto
if
inline
int
long
mutable
new
operator
private
protected
public
register
return
short
signed
sizeof
static
struct
switch
template
this
throw
typedef
union
unsigned
virtual
void
volatile
while

Monissa kääntäjissä on käytössä myös mm. nämä varatut sanat:

asm
far
huge
interrupt
near

volatile ja register

Muistia on montaa plaatua ja eri nopeutta - ja ehdottomasti nopeimpia ovat prosessorin sisäiset rekisterit. Niitä ei ole paljon, joten muuttujien jaottelu rekisterien ja itse keskusmuistin välillä on hankala homma, joka yleensä kannattaa jättää kääntäjän tehtäväksi. Jotain vaikutusta voi ohjelmoijalla näissä henkimaailman asioissa kuitenkin olla. Kun kääntäjä saa sijoittaa haluamiaan muuttujia rekistereihin, nopeuttaa se ohjelmaa joskus huomattavastikin. Joskus kuitenkin tämmöinen temppuilu voi aiheuttaa ongelmia. Tällainen esimerkki ovat keskeytykset.

Keskeytyksiä löytyy lähinnä DOS-ympäristöstä. Ne ovat ohjelmanpätkiä, jotka jonkun tietyn tapahtuman laukaisemana (esim. näppäimen painallus) keskeyttävät itse ohjelman ja tekevät jotakin. Kun kääntäjä on siirtänyt jonkun muuttujan rekisteriin ja kesken kaiken hyppää arvaamatta keskeytys, joka käsittelee muuttujaa - siis sen muistissa olevaa versiota - on tuloksena hyvin epämiellyttävä sekasotku. Mutta onneksi on volatile: varattu sana, joka voidaan kirjoittaa muuttujamäärittelyn eteen ja joka kertoo kääntäjälle, että kyseistä muuttujaa voi joku taustaohjelmista yllättäen muuttaa. Kääntäjä antaa muuttujan olla koko ajan muistissa eikä yritäkään nopeuttaa sen käyttöä rekisterikikoilla.

register tekee sitten mitä lupaakin: käskee kääntäjän sijoittaa muuttuja rekistereihin, mikäli vaan mahdollista. Joskus tämä voi johtaa nopeampaan ohjelmaan. Nyrkkisääntö on kuitenkin: älä käytä registeria. Se yleensä vaan sotkee kääntäjän optimointipasmat - nykyajan kääntäjät kuitenkin tekevät hommansa vähintääkin vuosien taidolla ja kokemuksella.

 

Komentoriviparametrit

#include <iostream.h>
int main(int argc, char* argv[])
{
	cout << "Parametreja annettiin " << argc << " kipaletta." << endl;
	for (int i=0; i< argc; i++)
		cout << i << ". parametri on " << argv[i] << endl;
		
	return EXIT_SUCCESS;
}

Esimerkki oli kopioitu melkein suoraan Libertyn kirjasta. Se vaan iskee selkeydellään kuin miljoona volttia, joten turha sitä on mitenkään sotkea. Juttuhan on siis komentoriviparametrit, se litania mitä käynnistettävän ohjelman perään monissa käyttöjärjestelmissä voi kirjoittaa. Niihin pureudutaan kiinni kahdella muuttujalla, arcg ja argv, jotka main(..) saa osakseen. argc tulee sanoista argument count ja argv on argument vector. Nimet voivat olla mitä vaan, mutta nämä kaksi kaverusta ovat vakiinnuttaneet itsensä jo standardiksi. argc kertoo paljonko parametrejä on - itse ohjelman nimi mukaanlukien. Siis argc on aina ainakin yksi. argv on taas taulukollinen osoittimia parametrimerkkijonojen alkuun (siis määrittelyn char* argv[] sijasta kävisi myös char** argv tai char argv[][]). argv[0] osoittaa ensimmäisen merkkijonoon, joka on ohjelman nimi. argv[1] on sitten ensimmäinen parametri, jos sellainen on, jne..

Takaisin