previous next Title Contents Index

15. Kommentointi ja jakaminen osiin


Mitä tässä luvussa käsitellään?

* ohjelman kommentointi

* ohjelman jakaminen osiin

* ehdollinen kääntäminen

* makefile

* projekti

15.1 Kommentointi

"Löysin vuosi sitten kirjoittamani kolmen rivin aliohjelman. Siinä ei ollut yhtään kommenttia. Miten se hoiti tehtävänsä? Mietin asiaa kaksi päivää ja sitten ymmärsin miksi tehtävä oli triviaali eikä tarvinnutkaan kommenttia." (Vapaa käännös verkossa olleesta kirjeestä / vl).

Kommentoimme jatkossa aliohjelmat huolellisesti. Seuraavana on kuvattu eräs tapa. Tietenkin on miljoona muutakin tapaa, mutta ainakin seuraavan tavan ajatus kannattaa pitää mielessä.

15.1.1 Valmiin kommenttilohkon lukeminen

Aliohjelman kirjoittaminen aloitetaan vaikkapa seuraavan f.c - nimisen tiedoston lukemisella koodin sekaan (seuraavassa kursiivilla kirjoitetut osat eivät ole mukana tiedostossa):

komloh\f.cpp - pohja funktioiden kirjoittamiselle

	//***************************************************************************
	int                       //
	funktio(                  // Funktion eri paluuarvot                        
	                          //                                                
	  p1                     ,// s   Selitys Parametrin merkitys                
	  p2                     ,// t   Selitys                                    
	  p3                      // s   Selitys                                    
	)    
	/* 
	** Funktiolla ... Lyhyt (mutta riittävä) kuvaus siitä, MITÄ funktio tekee
	**
	** Globaalit: Globaalit muuttujat joita ohjelma tarvitsee
	** Muuttuu: Muuttujat (yleensä glob) joita muutetaan ja eivät ole param.listassa
	** Syöttö: Mistä saadaan syöttö
	** Tulostus: Mihin tulosteet
	** Kutsuu: Mitä aliohjelmia kutsutaan (usein tarpeeton rivi)
	** Tekijä:    Vesa Lappalainen
	** Pvm:       26.01.1997
	** Algoritmi: Miten funktio tekee tehtävänsä
	** Esimerkki: Muutama selventävä esimerkki
	- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
	{
	}
Lohkokommenteissa (kommenttia monta riviä peräkkäin) ** pyritään saamaan alkamaan sarakkeesta 1. Miksikö? Tällöin ohjelman dokumentointia voidaan automatisoida siten, että kerätään kustakin tiedostosta ne rivit, joissa on ** 1. sarakkeessa.

15.1.2 Parametrilistan kommentointi

Tämän jälkeen korjataan funktion tyyppi ja nimi sekä parametrilista (helpointa päällekirjoitustilassa). Tarpeettomat rivit poistetaan tai tarvittaessa kopioidaan rivi uudeksi.

Parametrilistan kommenteissa on esimerkiksi seuraavat symbolit:

	s   = syöttömuuttuja (tulee aliohjelmaan)
	t   = tulosmuuttuja (aliohjelma muuttaa tämän arvoa)
	s,t = parametri on sekä syöttö, että tulos
Mikäli joku muuttuja on puhtaasti syöttömuuttuja, mutta siitä huolimatta sen arvo muuttuu (esim. jotkin pätkimisaliohjelmat), kannattaa parametri mainita vielä Muuttuu: kohdassa.

15.1.3 Funktion toiminta ja muu esittely

Täytetään lyhyt kuvaus funktion toiminnasta. Täytetään globaalit: yms. kohdat. Kenttien tarkoitus on seuraava:
	Globaalit: globaalit muuttujat joita aliohjelma tarvitsee
	Muuttuu:   ne muuttuvat arvot jotka eivät ole parametrilistassa
	           = lähinnä globaalit muuttujat (hyvä aliohjelma ei 
	           näitä tarvitse)
	Syöttö:    pääte, tiedostot
	Tulostus:  pääte, tiedostot
	Kutsuu:    mitä EI- tunnettuja aliohjelmia tai makroja tarvitaan
	           C:n standardikirjastojen kutsuja ei tarvitse luetella
	           jolleivat ne ole kovin eksoottisia, ei myöskään samassa tiedostossa
	           tai projektissa olevia "tunnettuja" aliohjelmia
	Tekijä:    kuka tehnyt (jos triviaali aliohjelma, voidaan
	           jättää pois)
	Pvm:       milloin tehty ja myös muutokset
	Algoritmi: algoritmin karkea kuvaus ja mahdolliset lähdeviitteet 
	Esimerkki: esimerkkejä, joilla havainnollistetaan aliohjelman 
	           toimintaa ja saadaan itsellekin kirjattua ylös
	           se mitä ollaan tekemässä

Näistä tarpeettomat (triviaalit tai tyhjiksi jäävät) rivit poistetaan.

15.1.4 Koodin kommentointi

Itse ohjelmakoodi kommentoidaan seuraavasti:
*
selviä C- kielen rakenteita ei saa kommentoida. Ei siis
	i=5; /* sijoitetaan i on 5 */               /* TURHA! */
*
kuitenkin mikäli lauseella on selvä merkitys algoritmin kannalta, kommentoidaan tämä
	i=5; /* aloitetaan puolestavälistä */
*
ryhmitellään lauseet tyhjien rivien avulla loogisiksi kokonaisuuksiksi. Tällaisen kokonaisuuden alkuun voidaan laittaa kommenttirivi, joka kuvaa kaikkien seuraavien lauseiden merkitystä.
*
mikäli tekee mieli kommentoida lauseryhmä, kannattaa miettiä voitaisiinko koko ryhmä kirjoittaa aliohjelmaksi. Aliohjelman nimi sitten kuvaisi toimintaa niin hyvin, ettei kommenttia enää tarvittaisikaan. Kuitenkin jos näin suunnitellulle aliohjelmalle tulee iso kasa (liki 10) parametreja, täytyy asiaa ajatella uudestaan.
*
muuttujien nimet valitaan kuvaaviksi. Kuitenkin mitä lokaalimpi muuttujan käyttö, sitä lyhyemmäksi nimi voidaan jättää. i ja j sopivat aivan hyvin silmukkamuuttujien nimiksi ja p yms. osoittimen nimeksi (lokaalisti).
*
globaaleja muuttujia vältetään 'kaikin keinoin'
*
olioiden ansiosta globaalit muuttujat voidaan yleensä välttää kokonaan!
*
mikäli globaaleja muuttujia kuitenkin tarvitaan, kasataan ne yhteen struktuuriin
	typedef struct {
	  int jasen_maara;
	  int nayton_koko;
	...
	} globaalit_tyyppi;
	globaalit_tyyppi GLOBAALIT;
	...
	GLOBAALIT.jasenmaara=5; 
jolloin globaaleja muuttujia ei muuteta vahingossa!
*
tarvittaessa määritellään useita eri nimisiä globaaleja tietueita.
*
vakiotyyliset (alustetaan esittelyn yhteydessä eikä ole tarkoitus ikinä muuttaa) globaalit muuttujat on sallittu sellaisenaan ja niiden nimet kannattaa ehkä kirjoittaa isolla.
*
funktioiden paluuarvolle valitaan tietty tyyli, joka pyritään säilyttämään koko ohjelman ajan. Esimerkiksi 0 = onnistui ja muut virheilmoituksia.

15.2 Omat aliohjelmakirjastot

Aiemmin rakensimme joukon merkkijonojen käsittelyssä tarvittavia apuohjelmia. Nämä aliohjelmat voitaisiin kopioida suoraan myös kerhorekisteriimme. Käytännössä näin ei kuitenkaan kannata tehdä, sillä valmiiksi testatut aliohjelmat olisivat mukana vain turhaan lisäämässä käännösaikaa.

Tämän takia aliohjelmat kirjoitetaan omaksi tiedostokseen, vaikkapa nimelle mjonot.c. Aliohjelmien otsikot (esittelyrivit) ja muut kaikille tarkoitetut määrittelyt kirjoitetaan tiedostoon mjonot.h.

15.2.1 mjonot.h

Tiedosto mjonot.h voisi olla vaikkapa seuraavanlainen:
	/* Makro jolla saadaan muuttujan nimi ja koko peräkkäin */
	#define N_S (nimi) nimi,sizeof(nimi)
	
	/****************************************************************************/
	/* vakiot syötön onnistumiselle                                             */
	#define  SYOTTO_OK 2
	#define  EI_MAHDU 1
	#define  OLETUS 0
	#define  TIEDOSTO_LOPPU - 1
	#define  VIRHE_SYOTOSSA - 2
	
	extern char *VALIMERKIT;
	
	/****************************************************************************/
	char *tee_jono(char *);
	int f_lue_jono(FILE *, char *, int);
	int lue_jono(char *, int );
	...
	int wildmat(register char *, register char *);
Tämä tiedosto on ehkä helpointa tehdä kopioimalla kaikki alkuperäiset aliohjelmat tiedostoon ja tämän jälkeen tuhoamalla aliohjelmien suoritusosat. On olemassa myös ohjelmia, jotka tekevät tiedostosta näitä prototyyppitiedostoja.

Huomattakoon, että myös seuraavia muotoja voi esiintyä .h - tiedostoissa:

	int lue_jono(char *jono, int max_pituus);
	...
	extern int lue_jono(char *jono, int max_pituus);
	...
	int lue_jono(char *, int);
	...
	extern int lue_jono(char *, int); 
Funktioiden yhteydessä extern oleminen tai puuttuminen ei kuitenkaan haittaa mitään. Toisin on muuttujien kanssa!

Huom! Nyt funktioiden esittelyjen perään täytyy muistaa laittaa puolipiste!

15.2.2 mjonot.c

Vastaavasti itse tiedosto, jossa aliohjelmat ovat, olisi seuraavan näköinen:

ali\mjonot.c - aliohjelmia merkkijonojen käsittelyyn

	/****************************************************************************/
	/*  
	** M J O N O T . C
	**
	** Yleisiä merkkijonojen käsittelyyn liittyviä aliohjelmia.               
	**
	...
	** Aliohjelmat:
	**    tee_jono               -  luo uuden merkkijonon jonne jono kopioidaan
	**    kopioi_jono            -  kopioi kork. annetun määrän merkkejä
	...
	**                             merkkijonossa
	**    wildmat                -  vertaa onko sana == maski, missä maskissa
	**                             voi olla jokeri- merkkejä
	...
	*/       
	
	#include <stdio.h>
	#include <stdlib.h>
	#include <string.h>
	#include <ctype.h>
	#include "mjonot.h"
	
	char *VALIMERKIT=" .,- ;:?!";  
	
	/****************************************************************************/
	char                    /* = jonon kopion osoite                            */
	*tee_jono(              /* NULL = ei voida kopioida                         */
	  char *jono            /* s   Kopioitava merkkijono                        */
	)
	...
	... Kuten ennenkin ...
	...                                       
	
	/****************************************************************************/
	int                       /*                                                */
	wildmat(                  /* 0 = jono täsmää maskiin                        */
	                          /* 1 = jono ei täsmää maskiin                     */
	  const register char *s ,/* s   Tutkittava jono                            */
	  const register char *m  /* s   Maski, johon jonoa verrataan               */
	)
	/*
	** Funktiolla tutkitaan täsmääkö annettu jono verrattavaan maskiin.
	** Maski saa sisältää seuraavia erikoismerkkejä:
	**   * vastaa 0-n merkkiä
	**   ? vastaa mitä tahansa yhtä merkkiä
	**
	** Algoritmi: Kysymysmerkki ja tavallinen kirjain normaalisti
	**            Jos tulee vastaan tähti joka ei ole jonon lopussa,
	**            niin ongelmahan on oikeastaan
	**            (koska tähän asti kaikki on ollut oikein)
	**            "Onko loppujono sama kuin toisen jonon loppu JOSTAKIN
	**             kohdasta alkaen"?
	**            Siis kokeillaan sovittaa loppujonoa aliohjelman itsensä
	**            (rekursio) avulla kaikkiin mahdollisiin loppupaikkoihin.
	** Esimerkki: s = "Kissa" m = "*ss*" -> 0
	**                          = "*ss"  -> 1
	** Vika alkuperäisessä algoritmissa:
	**   Jos m="*a" ja s="" ja s[1]!=0 (mikä tietysti sallittua!)
	**   niin vastaus oli 0.
	**   Korjattu 29.1.1994/vl muuttamalla rekursion jälkeinen
	**             if (!*++s) return 1;
	**   muotoon
	**             if (!*s || !*++s) return 1;
	----------------------------------------------------------------------------*/
	{
	  while (*m) {                    /* Jos kokeiltavaa jonoa on jäljellä      */
	    if (*m == '?') {              /* Jos kysymysmerkki, niin kaikki kelpaa  */
	      if (!*s) return 1;          /* paitsi jos jono on loppunut!           */
	    }
	    else if (*m == '*') {         /* Jos * niin katsotaan onko viimeinen    */
	      if (*++m)                   /* Jollei * viimeinen, niin kokeillaan    */
	        while (wildmat(s, m))     /* loppujonoa jokaiseen eri paikkaan.     */
	          if (!*s || !*++s) return 1;/* Jos jono loppuu kesken ei täsmää!   */
	      return 0;                   /* Muuten samat (* viimeinen tai loppujono*/
	    }                             /* täsmäsi)                               */
	    else if (*s != *m)            /* Jos samat merkit niin tietysti OK!     */
	      return 1;
	    s++; m++;                     /* Kummassakin jonossa eteenpäin          */
	  }
	
	  return *s;                      /* Jos jono loppui yhtäaikaa, niin OK!    */
	}
	...

Olemme lisänneet kirjastoon muutamia uusia aliohjelmia, joiden tarpeellisuuden totesimme jo kerhorekisterin suunnitteluvaiheessa.

Kirjastossa on mm. wildmat- aliohjelma merkkijonojen samaistamiseksi, kun jonossa saa esiintyä jokerimerkkejä * ja ? (vrt. MS- DOS).

Tehtävä 15.148 wildmat (opettavainen)

Pöytätestaa wildmat- aliohjelma syötöllä Kissa ja *ss*. Ohje: Kirjoita normaalin pöytätestin mukainen taulukko. Koska funktio kutsuu itseään, kannattaa jokaista uutta itseään kutsumista varten kirjoittaa oma uusi sarake uuden kutsukerran muuttujista. Osoittimet s ja m kannattaa säilyttää muodossa, jossa ne näyttävät loppumerkkijonon. Esimerkiksi:
	aluksi  s = "Kissa";  *s='K'
	s++  - > s = "issa"    *s='i'
	s++  - > s = "ssa"     *s='s'

15.2.3 Kirjaston testaus

Kirjaston testausta varten voimme kirjoittaa vaikkapa ohjelman t_mjonot.c:

komloh\t_mjonot.c - testiohjelma merkkijonokirjastolle

	#include <stdio.h>
	#include "mjonot.h"
	
	/****************************************************************************/
	/* testiohjelmat:                                                           */
	
	void lue_jono_testi(void)
	{
	/* täytä! */
	}
	
	int f_lue_jono_testi(void)
	{
	/* täytä! */
	}
	
	void lue_kok_testi(void)
	{
	/* täytä! */
	}
	
	void lue_jono_oletus_testi(void)
	{
	  int paluu;
	  char st_ole[50],st[50] = "Ankka Aku";
	
	  printf("Testi loppuu, kun painat ^Z.\n");
	  printf("1234567890123456789012345678901234567890\n");
	
	  do {
	    strcpy(st_ole,st);
	    paluu=lue_jono_oletus("Anna jäsenen nimi",19,33,st_ole,st,50);
	    poista_tyhjat(st);
	    jono_alku_isoksi(st);
	  } while ( paluu >= OLETUS );
	}
	
	void wild_testi(void)
	{
	  char jono[80],maski[80];
	  while (!feof(stdin)) {
	    printf("Anna jono ja maski>");
	    scanf("%s %s",jono,maski); /* Älä käytä oikeasti!!!! */
	    printf("%d <-  %s %s\n",wildmat(jono,maski),jono,maski);
	  }
	}
	
	int main(void)
	{
	#if 0
	  lue_jono_testi(); 
	#endif
	#if 0
	  f_lue_jono_testi(); 
	#endif
	#if 0
	  lue_kok_testi();
	#endif
	#if 0
	  lue_jono_oletus_testi();
	#endif
	#if 1
	  wild_testi();
	#endif
	  return 0;
	}
Huomattakoon, että nyt täytyy olla lainausmerkit lauseessa
	#include "mjonot.h"
Jotta ohjelma voidaan kääntää, tarvitsemme projektin tai MAKEFILEn. Kunnes olemme ne käsitelleet, voidaan "kerho.h" tilapäisesti korvata "kerho.c". Normaalisti c- tiedostoja EI SAA "includeta"!

15.3 Ehdollinen kääntäminen

Edellisessä pääohjelmassa testattava osa voidaan valita muuttamalla #if - lauseissa esiintyviä 0 ja 1:iä.

Kyseessä on esiprosessorin direktiivi, jolla määrätään minkä osan esiprosessori antaa varsinaiselle kääntäjälle käsiteltäväksi.

Tällä periaatteella voidaan rakentaa ohjelmia, joissa tekstit tulevat eri kielillä (englanti, ruotsi, suomi) sen mukaan, mitä ohjelmassa on määritelty.

Samalla tempulla teimme myös lue_merkki - aliohjelman toimimaan joko Turbo- C:ssä tai standardi ANSI- C:ssä sen mukaan onko määritelty sana __TURBO__ (huomaa kaksi _ kummallakin puolella!):

	#ifdef __TURBOC__
	#  define GETCH
	#  include <conio.h>
	#endif
	...
	char lue_merkki(void)
	{
	#ifdef GETCH
	  ... /* lukeminen conio- kirjaston avulla */
	#else
	  ... /* lukeminen standardikirjaston avulla */
	#endif
	}
Usein kääntäjään liittyy kääntäjän määrittelemiä symboleja, joiden olemassaoloa testaamalla voidaan tehdä laiteriippuvia osia. Juuri tällainen oli __TURBOC__. Ehtona voi olla myös jokin oma sana kuten edellä GETCH. Usein itse määriteltyjä sanoja käytetään estämään .h- tiedoston moninkertaiset esiintymät:

ali\mjonot.h - estetään monikertainen käyttö

	... 
	#ifndef MJONOT_H
	#define MJONOT_H
	... /* varsinainen h- tiedoston sisältö */
	#endif
Nyt voidaan pääohjelmassa kirjoittaa
	#include "mjonot.h" /* Lukee mjonot.h - tiedoston kokonaan */
	#include "mjonot.h" /* Ei tee mitään koska MJONOT_H määr. */

15.4 Header- tiedostot ja prototyypit

Kutakin isompaa aliohjelmakokonaisuutta varten kirjoitetaan oma header- tiedosto (.h - tiedosto). Kääntäjän toiminta on sellainen, että kun kääntäjä tulee esimerkiksi lauseeseen
	int a,i;
	...
	a = ynnaa_yksi(i); 
voidaan suluista päätellä aina, että kyseessä on aliohjelman kutsu. Loppu onkin kääntäjälle vaikeampaa. Aliohjelmalle pitäisi välittää oikean tyyppinen parametri. Kääntäjä voi yrittää arvata, että esimerkin aliohjelma olisi muotoa
	int ynnaa_yksi(int luku); 
koska sille välitetään parametrina int - tyyppinen luku. Mikäli arvaus osoittautuisi vääräksi ja aliohjelma olisikin esitelty
	double ynnaa_yksi(double luku); 
olisi käännös mennyt väärin arvauksen kohdalta, koska kokonaisluku ja reaaliluku talletetaan aivan eri tavalla. Virhe voidaan normaalisti välttää sillä, että kirjoitetaan (esitellään) aliohjelma ennen sen käyttöä.

Aliohjelmakirjastojen kanssa on hankalampaa. Jottei aliohjelmia tarvitsisi esitellä useita kertoja, on aliohjelman parametrien tyypit mahdollista esitellä prototyypillä

	int lue_jono(char *jono, int max_pituus); 
Kun kaikkien tarvittavien aliohjelmien prototyypit laitetaan .h - tiedostoon, ja tämä tiedosto luetaan esiprosessorin
	#include "mjonot.h"
direktiivillä käännöksen aikana koodin mukaan, saadaan kullekin aliohjelmalle oikea tyyppi.

Vastaavasti koko ohjelmaan saattaa tulla eri tiedostojen välisiä globaaleita muuttujia, ja kuitenkin muuttujan saa esitellä vain yhdessä paikassa. Jotta kaikki tiedostot näkisivät globaalit muuttujat, voidaan ne esitellä header- tiedostossa extern - määreellä. Itse varsinainen muuttuja varataan sitten siinä TASAN YHDESSÄ tiedostossa, johon se "eniten" kuuluu. Tällöin muuttujaan ei tule extern - määritystä.

	kerho.h:
	  extern const char *VERSIO;
	...
	kerho.c
	  const char *VERSIO = "18.2.1992"
	..
	muissa tiedostoissa esimerkiksi:
	  printf("Versio %s\n",VERSIO); 
Siis yleensä header- tiedostoon tulee
*
#define - vakiot
*
typedef - esitellyt tietueet
*
#define - esitellyt yleiskäyttöiset makrot
*
extern - muuttujien esittelyt (hyi!)
*
funktioiden prototyypit
*
luokkien esittelyt
Header- tiedostoon EI yleensä kirjoiteta varsinaisia kääntyviä lauseita, kuten muuttujien varaamista tai ohjelmakoodia (paitsi inline- funktioiden koodi)!

15.5 Kääntäminen ja linkittäminen

Header - tiedosto oli siis kääntäjää varten.

Kukin kokonaisuuteen kuuluva osa voidaan kääntää erikseen. Lopuksi nämä osat linkitetään yhteen, jolloin muodostuu haluttu valmis ohjelma.

Ongelmana on sitten muistaa kääntää uudestaan aina kaikki muuttuneet osat. Tietysti voisimme aina varmuuden vuoksi kääntää kaikki osat, mutta tämä veisi turhaa aikaa.

Tätä varten on MAKE- niminen ohjelma, jolle kirjoitetaan ohjetiedosto siitä, mitkä tiedostot muodostavat valmiin työmme ja miten eri tiedostot riippuvat toisistaan.

Esimerkiksi mikäli mjonot.c tiedostoon tulee muutos, tarvitsee se kääntää sekä suorittaa linkitys uudestaan. Mikäli mjonot.h muuttuu, pitää sekä mjonot.c ja t_mjonot.c kääntää uudelleen.

15.5.1 Make- ohjelma

Esimerkiksi HP- UX - ympäristössä kerho- ohjelman tarkistukset suorittava versio voitaisiin kääntää seuraavan tiedoston (kirjoitettu nimelle makefile) avulla:

komloh\makefile - kerho-ohjelman C-version kääntäminen Unixissa

	# makefile kerho- ohjelmaa varten
	kerho: kerho.o kerhorak.o kerhoets.o kerhotar.o kerhoali.o\
	       kerhotal.o kerhoopt.o kerholra.o                   \
	       mjonot.o pvm.o help.o
		cc  - Aa - o kerho *.o - lm
	.c.o:
		cc - c - Aa $*.c
Huomattakoon, että cc- alkuisten (c- kääntäjän kutsu Unixissa) rivien on oltava sisennetty TAB- näppäintä käyttäen. Seuraavassa hieman selityksiä:
	kerho:   -  tiedosto kerho riippuu näistä .o (obj)
	           tiedostoista
	  cc ... -  jos jossakin .o:ssa on uudempi päiväys kuin kerho
	           tiedostossa, luodaan uusi kerho tällä komennolla
	           - Aa  = ANSC- C käännös
	           - o   = tulostiedosto (output)
	           - lm  = linkitetään matematiikkakirjasto
	.c.o:    -  .c tiedostoista tehdään .o tiedosto seuraavasti
	           jos .c:n päiväys on uudempi kuin .o:n. 
Käännös ja ajaminen suoritetaan seuraavasti
	$ make[RET]
	...
	$ kerho[RET]
	...
	Tällä ohjelmalla... 

15.5.2 Projektit

Useissa integroiduissa ympäristöissä on mahdollisuus tehdä projekteja, eli ilmoittaa mitkä ovat työhömme vaikuttavia tiedostoja. Usein näiden projektien käyttäminen on helpompaa kuin MAKE- tiedostojen tekeminen.

Projektin merkitys on vastaava kuin MAKE- tiedostonkin: pitää kirjaa siitä, mitkä tiedostot kuuluvat kokonaisuuteen. Toisaalta projektitiedostoa tarvitaan erityisesti linkityksessä.

Mikäli merkkijonontestaus esimerkkimme mjonot.c käännettäisiin irrallisena, syntyisi tiedosto mjonot.obj. Tähän ei vielä projektia tarvita. Vastaavasti voidaan kääntää pääohjelma t_mjonot.c. Vieläkään ei projektia tarvittaisi. Ongelmaksi tuleekin linkitys. Päämodulissa viitataan aliohjelmaan lue_jono. Mistä linkittäjä arvaisi etsiä tätä aliohjelmaa tiedostosta mjonot.obj?

Tämä tieto löytyy projektista, johon esimerkissämme mainittaisiin ne C- tiedostot, jotka kääntämisessä tarvitaan, eli

	t_mjonot.c
	mjonot.c
Mikäli C- kielistä koodia ei ole olemassa, voidaan projektiin usein ilmoittaa myös jo käännettyjä tiedostoja, eli esimerkiksi
	t_mjonot.c
	mjonot.obj
Siis linkittäjä etsii aliohjelmia ja globaaleita muuttujia projektissa mainituista tiedostoista C- kielen standardikirjastojen lisäksi.

Esimerkiksi Turbo- C:ssä valitaan aluksi uuden projektin luonti. Tämän jälkeen projekti- ikkunaan osoitetaan ne *.c tiedostot, joita haluamme käyttää. Voidaan myös lisätä *.obj tai *.lib tiedostoja, mikäli lähdekielisiä tiedostoja ei ole.

Header- tiedostoja (*.h) ei koskaan laiteta projektiin!

Itse kääntäminen suoritetaan sitten normaalisti.

Projektin perusteella kääntäjä ensin kääntää kaikki ne .c- tiedostot, joista ei vielä ole .obj - tiedostoa tai .obj- tiedoston päiväys on vanhempi kuin .c- tiedoston. Tämän jälkeen linkitetään kaikki projektissa mainitut .obj- tiedostot yhdeksi .exe- tiedostoksi.

Huomattakoon että projektissa tulee olla tasan yksi .c- tiedosto, jossa on main- funktio.

15.6 Kerho- ohjelman jako osiin (ks. jako.15- hakemisto)

Kirjoittamisen ja ylläpidon kannalta voi olla helpompi jakaa ohjelma muutamaan loogiseen osaan. Nyt on valmiiksi kirjoitettuna jäsenrekisterin runko- osa. Tällä rungolla voidaan testata tietorakenteiden toimivuus: Rungossa meillä on käytössä alkeellinen näytölle tulostava aliohjelma.

Rungossa osa aliohjelmista on hyvin yleisluonteisia tulostus/luku aliohjelmia ja nämä kaikki voitaisiin siirtää vaikkapa tiedostoon nimeltä ioali.c. Muuten jako kannattaa tehdä luokkapohjaisesti:

*
kerhomain.cpp - pääohjelma
*
jasen.cpp - yksittäisen jäsenen käsittely
*
jasenet.cpp - jäsenistön käsittely
*
kerho.cpp - kerho kokonaisuudessaan
*
naytto.cpp - näyttöön liittyvä kerhon käsittely
Vastaaviin .h- tiedostoihin tulee kunkin luokan esittely
	class cLuokka {
	 ...
	};
Varsinainen todellinen koodi tulee sitten vastaaviin .cpp - tiedostoihin.

Lisäksi käytämme tietysti valmiita kirjastoja sekä aikaisemmin tehtyä merkkijonokirjastoa mjonot.

On turha toivo, että keksisimme kaikki määritykset ja aliohjelmat kerralla. Tehtävää täytyy hahmotella palanen kerrallaan. Kun jokin homma tuntuu venyvän liian pitkäksi tai monimutkaiseksi, määrittelemme tehtävän useampaan alatoimintoon ja toteutamme nämä toiminnot sitten aliohjelmina/luokkina. Aliohjelmien/metodien parametrit saattavat vielä myöhemmin muuttua, kun huomataan saman tehtävän käyvän sekä tähän että tuohon tehtävään. Esimerkiksi etsiminen ja selailu käy samantien myös korjailuun ja poistoon. Ainoana erona on, että korjaus- ja poistonäppäimet eivät ole pelkässä etsimisessä sallittuja.


previous next Title Contents Index