815338A Ohjelmointikielten periaatteet 2006, Harjoitus 6 Ratkaisut

Kuudennessa harjoituksessa tarkastellaan aliohjelmien parametrityyppejä, toteutusta ja abstrakteja tietotyyppejä.

Tehtävä 1.

Kirjoita  Javalla aliohjelma (metodi), joka laskee parametrina saamansa mielivaltaisen kolmiulotteisen kokonaislukutaulukon alkioiden summan. Testaa myös metodiasi.

Ratkaisu.

Tiedostossa H6T1_asum.java on Java-ohjelma, jonka metodi summaa laskee kolmiulotteisen kokonaislukutaulukon alkioiden summan. Huomaa, miten taulukon dimensiot saadaan length -attribuuttia käyttämällä.

Tehtävä 2.

Kirjoita C++ -kielellä geneerinen aliohjelma, joka laskee mitä tahansa numeerista tietoa sisältävän taulukon alkioiden summan. Ohje: Katso mallia luentojen geneerisestä lajittelufunktiosta.

Ratkaisu.

Tiedostossa H6_T2.cpp on toteutettu vaadittu aliohjelma. Pääohjelmassa funktiota sovelletaan sekä kokonaisluku- että reaalilukutaulukon alkioiden summaamiseen.

Tehtävä 3.

Kirjoita C tai C++ -kielellä funktio laske_summa, joka saa parametrinaan yksiulotteisen reaalilukutaulukon, sen alkioiden lukumäärän ja prototyyppiä
	double val_fun(double)

olevan funktion. Funktio laske_summa palauttaa summan parametrina saamansa funktion arvoista sovellettuna taulokn alkioihin. Sovella funktiotasi laskemaan taulukon

double taulu[] = {1.0,2.0,3.0}

neliöiden summa ( = 1.02 + 2.02 + 3.02 = 1.0 + 4.0 + 9.0 = 14.0), ts. parametrina annetaan funktio

double square(double x)
{
return x*x;
}

Ratkaisut.

Tiedostossa H6_T3.cpp on ominaisuus toteutettuna C++:lla. Huomaa, että funktion paluuarvo ja parametrit ovat osa funktion tyyppiä, joten ne on annettava aliohjelman parametrina saavan funktion parametrilistassa. C-kielinen koodi ei olennaisesti poikkea C++ -koodista.

Tehtävä 4.

C -kielessä voidaan saada pinon sisältö näkymään (väärin)käyttämällä printf -funktiota. Kun funktiota kutsutaan ilman parametreja seuraavasti:
	printf("Pinossa on nyt:\n%p\n%p\n%p\n%p\n");
saadaan pinosta 16 ylintä tavua heksamuodossa näkyviin. Kirjoita C-kielinen funktio
#include <stdio.h>
#include <string.h>

void stack_fun(char *strParam)
{
char mjono[8];
printf("Pinossa on nyt:\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n");
strcpy(mjono,strParam);
printf("Pinossa on nyt:\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n");
}
ja kutsu tätä jokin merkkijono parametrina. Mikäli käytät Visual Studio.NETiä, joudut kääntämään ja ajamaan release -version ohjelmasta, jotta pinon näyttäminen toimisi. Etsi merkkijonosi merkkien ASCII -koodit taulukosta. Löydätkö merkkejä pinosta? Mitä tapahtuu, jos parametrimerkkijono on pitempi kuin 7 merkkiä? Mitä seuraamuksia tällä voi olla (muista että myös paluuosoitetta säilytetään pinossa)?

Ratkaisu.

Ohjelmakoodi on tiedostossa H6_T4pino.c. Mikäli ajat ohjelmaa Visual Studio.NETillä, on käännettävä ja suoritettava Release -versio, muuten debuggaus -versio sotkee tulostuksen. Jos käytät muuta c-kääntäjää, saattaa pino olla hieman erilainen. Joka tapauksessa parametrijonon pitäisi löytyä jostain kohtaa pinosta.

Alla on esimerkkitulostus gcc-kääntäjällä tuotetusta ohjelmasta, kun parametrina on merkkijono "morjens":
Kutsuvan funktion osoite:
0040126C
Pinossa on nyt:
73D955FD
00000001
00045120
00000023
0240FF70
00000000
73DAE790
00000023
73DA31CF
0240FF60
00401293
0240FF58
0240FF70
Pinossa on nyt:
0240FF58
00000001
00045120
00000023
0240FF70
00000000
73DAE790
6A726F6D
00736E65
0240FF60
00401293
0240FF58
0240FF70
Parametrimerkkijonon merkkien ASCII -koodeja heksamuodossa:

m = 6D,
o = 6F,
r = 72,
... 
s = 73
Merkkijono siis kopioituu pinoon (sen päällä on printf-funktion pinoa), koska paikalliselle merkkitaulukolle varataan muisti pinosta. Paluuosoite (00401293) on pinossa merkkijonon jälkeen. Kun parametrimerkkijonoa kasvatetaan, merkit kirjoittuvat pinoon ja koska pinossa on jossain kohtaa paluuosoite, voidaan riittävän pitkällä merkkijonolla muuttaa paluuosoite, mikä voi aiheuttaa epätoivottuja seurauksia. Tosin .NET normaaleilla käännösasetuksilla sisällyttää koodiin puskurin ylitystarkistuksen, joten ajettaessa tulee virhe.

Tehtävä 5.

Pino voidaan toteuttaa taulukkoa käyttämällä C++ -luokkana, jonka runko on seuraava:

// Tiedostossa Pino.h

class Pino {
private:
int *pino_osoitin;
int koko;
int huippu;

public:
Pino (int pinonkoko);
~Pino () ;
void push(int luku);
void pop( ) ;
int top( ) ;
bool empty( ) ;
};

// Tiedostossa Pino.cpp
#include "Pino.h"
Pino ::Pino (int pinonkoko) {
pino_osoitin = new int [pinonkoko];
koko = pinonkoko;
huippu = -1;
}

Pino ::~Pino () {
delete [] pino_osoitin;
}

void Pino ::push(int luku) { /*funktion runko …*/}
void Pino ::pop( ) { /* funktion runko …*/ }
int Pino ::top( ) { /* funktion runko …*/ }
bool Pino ::empty( ) { /* funktion runko …*/ }

Täydennä funktioiden rungot ja testaa luokkaasi ohjelmassa. Lisää luokan destruktoriin tulostuskäsky ja luo Pino-olio sekä pino- että kekomuistiin ja havainnoi, milloin oliot tuhotaan. Huomaa: Tässä versiossa pinon on tarkoitus toimia siten, että päällimmäinen alkio saadaan kutsumalla funktiota top, kutsu ei poista alkiota pinosta, vaan se poistetaan kutsumalla funktiota pop, joka ei palauta mitään. Funktiolla empty voidaan kysyä onko pino tyhjä.

Ratkaisu.

Tiedostossa Pino.h on luokan esittely ja tiedostossa Pino.cpp luokan funktioiden koodi. Jäsenmuuttujat on määritelty näkyvyydeltään private -tyyppisiksi. Huomaa, että muuttuja huippu osoittaa pinon huippua taulukossa ja on -1 mikäli pino on tyhjä. Luokkaa testataan tiedostossa H6T5main.cpp, jossa luodaan pino- ja kekodynaamiset Pino-oliot. Mikäli käännät koodit g++ -kääntäjällä, voit tehdä sen kirjoittamalla komentoriville kaikkien käännettävien tiedostojen nimet, ts. esimerkiksi


g++ Pino.cpp H1_main.cpp -o h1.exe

kääntää ohjelman h1.exe -nimiseksi.

Tehtävä 6.

Kirjoita Java -versio tehtävän 5 pinosta.

Ratkaisu.

Java -luokka voisi olla kuten tiedostossa PinoTest.java, joka sisältää myös luokkaa testaavan pääohjelman. Koodi muistuttaa muuten C++ -luokkaa, mutta luokan esittelyä irrallaan luokan koodista ei ole. Jäsenmuuttujien näkyvyydet ovat samat; Javassa ei välttämättä tarvita jäsenmuuttujaa pinon koolle, koska se voidaan kysyä pinona toimivalta taulukolta (length). Tässä kuitenkin muuttujaa on käytetty.