1.23k likes | 1.34k Views
Olio-ohjelmoinnin perusteet luento 2: Muuttujista ja funktioista. Jani Rönkkönen LTY/Tietotekniikan osasto Kalvot on muokattu Sami Jantusen luentokalvoista viime vuodelta. VAROITUS!!!!!!. Tällä luennolla käsitellään perusasioita, joilla ei ole sinänsä paljon tekemistä oliopohjaisuuden kanssa!
E N D
Olio-ohjelmoinnin perusteetluento 2: Muuttujista ja funktioista Jani Rönkkönen LTY/Tietotekniikan osasto Kalvot on muokattu Sami Jantusen luentokalvoista viime vuodelta.
VAROITUS!!!!!! • Tällä luennolla käsitellään perusasioita, joilla ei ole sinänsä paljon tekemistä oliopohjaisuuden kanssa! • Esimerkit ovat esitetty ei-oliopohjaisella tavalla! • Tarkoitus on vain havainnoillistaa perusasioita • Toivottavasti osaat soveltaa näitä jatkossa, kun puhumme lisää luokista!
Sisältö • Muuttujat • Muuttujien määrittely ja tietotyypit • Osoittimet (pointer variables) • Viittaukset (reference variables) • Funktiot • määrittely ja tiedon välitys • muuttujien näkyvyysalueet • oletusparametrit (default parameters) • uudelleenmäärittely (overloading) • avoimet funktiot (inline) • rekursio
Muuttujista… • Tietokoneohjelmat manipuloivat tietoa • Meillä on siis tarve tallettaa tietoa • Muuttujat: • Kuvaavat tiettyä muistin muistipaikkaa • Sisältävät tietyn tiedon • Kun määrittelet muuttujan: • Varaat tilaa muistista • Annat muuttujalle nimen • Teet tämän kerran kutakin muuttujaa kohden
Kuinka luodaan muuttuja • Formaatti: <tietotyyppi> <muuttujan nimi>; • Muuttujan nimeksi voit valita melkein mitä vaan • Esimerkkejä: byte age; float gpa; String name; char letterGrade; • Nyt kun muuttujat on määritelty • Ei tarvitse määritellä niitä uudestaan • Voit käyttää muuttujan nimeä jatkossa tiedon lukemisessa ja kirjoittamisessa Computer's Memory age gpa ? ? name letterGrade ? ?
C++ varatut sanat • Näitä sanoja et kuitenkaan voi käyttää sellaisenaan muuttujien nimissä: • auto, bool, break, case, catch, char, class, const, continue, default, do, double, else, enum, extern, float, for, friend, goto, if, inline, int, long, namespace, new, operator, private, protected, public, register, return, short, signed, sizeof, static, struct, switch, template, this, throw, try, typedef, union, unsigned, void, volatile, while
Sallittuja muuttujien nimiä • Ei saa olla varattu sana • Ei saa alkaa numerolla • Ei saa sisältää symboleja kuten • Kuten #, @, !, ^, &, /, ), tai välilyönti • Poikkeukset: alaviiva _, ja dollarimerkki $ • Esimerkkejä: byte $theValue; // legal char test_value; // legal double double; // not legal int rum&coke; // not legal bool true or false; // not legal for two reasons!
Muuttujien alustamisesta • Tarkoittaa alku-arvon antamista muuttujalle • Arvo annetaan = -operaattoria käyttäen • Kopioi oikealla olevan tiedon vasemmalla määriteltyyn muuttujaan. Muuttujan tyyppi ja tiedon formaatti pitää olla yhteensopiva • Alustettava tieto voi olla vakio tai toinen muuttuja! • Esimerkkejä: int age; age = 15; char letterGrade = ‘B’; char yourGrade = letterGrade;
Tietotyypeistä • Tietotyyppi kertoo mikälaista tietoa muuttuja voi pitää sisällään • Jotkin tietotyypit ovat “sisäänrakennettuja” ohjelmointikieleen • Voimme myös määritellä tietotyyppejä itse (puhutaan tästä lisää myöhemmin) • Erilaisia tietotyypin muotoja: • Yksinkertaiset tietotyypit • Monimutkaiset tietotyypit (Valmistettu yksinkertaisista tietotyypeistä)
Yksinkertaiset tietotyypit • Sisäänrakennettuja • Numerot: • short (melko pienet numerot) • int (suuret numerot) • long (TOSI suuret numero)
Yksinkertaiset tietotyypit • Desimaaliluvut: • float (Ei niin tarkka kuin double ) • double (Aika tarkka) • Tarkkuudella tarkoitetaan kuinka monta lukua tulee desimaalin jälkeen • Muita: • char (pitää sisällään merkkejä kuten ‘a’, ‘A’, ‘1’, ‘ ‘) • bool (pitää sisällään totuusarvon true tai false)
No kuinka iso se on?: sizeof( ) • sizeof( ) kertoo kuinka paljon tietotyyppi vie tavuja • Esimerkki: int myInt; cout << sizeof (myInt) << endl;
Etumerkki vai ei? • C++ -kielessä voi määritellä merkki- ja kokonaislukumuuttujat etumerkittömäksi. • Etumerkillisille ja etumerkittömille kokonaisluvuille varataan yhtä paljon muistitilaa • Luvut ovat oletusarvoisesti etumerkillisiä • Etumerkittömän edut: • Etumerkittömät tietotyypit voivat siis sisältää suuremman joukon positiivisia lukuja
Perustietotyypeistä • Se kuinka monta tavua kukin tietotyyppi vie tilaa on riippuvainen kääntäjästä ja käyttöjärjestelmästä! • Tässä kuitenkin esimerkkejä:
Miksi koolla on väliä • Eri tietotyypit vie eri määrän tavuja muistista • Et voi laittaa suuritilaisemman muuttujan sisältöä pienenpään muistitilaan: short s = 5; long l = 5; long l = s; short s = l; long long long short short short
Tyyppimuunnos (Type Casting) • Tapahtuu kun pistät jotain enemmän tilaa vievää tietoa pienempään muistipaikkaan • Tiedon tarkkuustaso saattaa heikentyä • Formaatti: <pienempi muuttuja> = (<pienempi tietotyyppi>)<isompi muuttuja>; • Esimerkki: long myLong = 17; short myShort = (short)myLong; // We are squeezing myLong into myShort!
short vai long? • Jos on pienikin mahdollisuus, että muuttujaan tulee suurempi arvo kun tietotyyppiin mahtuu, niin valitse isompi tietotyyppi.
Etumerkittömien kokonaislukujen ylivuoto Sama ilmiö kun auton matkamittarissa: • Kun on maksimi saavutettu, niin aloitetaan uudestaan nollasta • Esim: unsigned intmyInt =65535;cout<< myInt << endl;myInt++;cout<< myInt << endl;myInt++;cout<<myInt<<endl; Mitä tapahtuu?? Tuloste: 65535 01
Etumerkillisten kokonaislukujen ylivuoto • Suurinta positiivista arvoa seuraa negatiivisin mahdollinen arvo • Esim: int myInt =32767;cout<< myInt << endl;myInt++;cout<< myInt << endl;myInt++;cout<<myInt<<endl; Mitä tapahtuu?? Tuloste:32767-32768-32767
unsigned short int luku1;unsigned short int luku2;unsigned short int luku3; • Liian työlästä? • Altis kirjoitusvirheille • Ratkaisuehdotuksia: • unsigned short int luku1, luku2, luku3; • Peitenimi (Typedef)
Peitenimi (Typedef) #include <iostream.h> typedef unsigned short int USHORT; void main() { USHORT Leveys = 5; USHORT Korkeus; ...
Yhteenveto • Muuttujat ovat nimettyjä soluja tietokoneen muistissa • On olemassa erilaisia tietotyyppejä säilyttämään erilaista tietoa • Eri tietotyypit vievät eri määrän tilaa • Kaikki muuttujat määritellään samanlaisen formaatin mukaisesti
VakioistaMiksi vakioita? -Esimerkki • Oletetaan, että koulussa yhdelle luokalle hyväksytään 15 oppilasta • Oletetaan myös, että koulun oppilasmäärä saadaan kerrottuna luokkien lukumäärä luokan koon kanssa: int luokkienLukumaara = 20; int oppilasmaara = 15 * luokkienLukumaara; • Mikä vikana?
Miksi vakioita? • Emme halua kovakoodata arvoja • Työlästä muuttaa • Emme myöskään halua laittaa pysyviä arvoja muuttujiksi • Joku saattaa vahingossa muuttaa niiden arvoja
#define • Hieman vanhentunut tapa määritellä vakio • Esim:#define oppilaitaLuokassa 15 • Ei ota mitään kantaa tietotyyppiin • Esikäsittelijä pelkästään korvaa tekstin toisella • Kääntäjä siis näkee oppilaitaLuokassa-tekstin sijasta luvun 15
Const • Suositeltavampi tapa #define sijasta:const unsigned short int oppilaitaLuokassa = 15; • Kääntäjä tietää minkä tyyppinen vakio on.osaa valvoa, että vakiota käytetään oikein • Vakiota ei voi muuttaa ohjelman suorituksen aikana. Jos vakio halutaan muuttaa, tulee ohjelma kääntää uudestaan.
Luetellut vakiot • Muuttujatyyppi, johon voi sijoittaa vain etukäteen määriteltyjä arvoja. • Luetellun vakion määrittely:enum VARI {PUNAINEN, SININEN, VIHREA, VALKOINEN, MUSTA}; • Määrittelee sanan VARI • Määrittelee vakiot PUNAINEN (arvo 0), SININEN (arvo 1),… • Kullekin vakiolle on mahdollista määritellä arvo erikseen:enum VARI {PUNAINEN=100, SININEN, VIHREA=500, VALKOINEN=501, MUSTA=700}; • Lause tuottaa vakiot PUNAINEN=100, SININEN=101, VIHREA=500, VALKOINEN=501 ja MUSTA=700 • Luetellut vakiot ovat käteviä kun käsitellään rajallista alijoukkoa (värejä, viikonpäiviä, ym…) • Ohjelmakoodin luettavuus paranee
Luetellut vakiot käyttöesimerkki #include <iostream.h> void main() { enum Paivat {Sunnuntai, Maanantai, Tiistai, Keskiviikko, Torstai, Perjantai, Lauantai }; Paivat vapaaPaiva; cout << “Minkä päivän haluat pitää vapaata (0-6)? “; cin >> vapaaPaiva; if (vapaaPaiva == Sunnuntai || vapaaPaiva == Lauantai) cout << “Viikonloppu on vapaata muutenkin!” << endl; else cout << “OK. pannaan asia muistiin” << endl; }
Lisäys- ja vähennysoperaattorit • ++ korottaa muuttujan arvoa yhdellä int counter = 5; int counter = 5; counter++; counter = counter +1; • += korottaa muuttujan arvoa annetulla määrällä int counter = 5; int counter = 5; counter += 3; counter =counter + 3; Sama kuin Sama kuin Huomaa: Myös --, -=, %=, /= versiot
Lisäys- ja vähennysoperaattorit • Voidaan toteuttaa joko etuliitteenä (esim. ++counter;) tai jälkiliitteenä (esim. counter++;) • Lopputulos sama paitsi jos muuttujaa käytetään samassa tilanteessa vielä arvon muutoksen lisäksi johonkin muuhun
Etu- ja jälkiliitelisäys esimerkki … void main() { int minunIka=40; int sinunIka=40; cout << “minun ikä on:” << minunIka++ << endl; cout << “sinun ikä on:” << ++sinunIka << endl; … } Mitä tapahtuu?? Tuloste: minun ikä on:40 Sinun ikä on:41
OsoittimistaOletteko nähneet tällaista? • Ohjelmoijan painajainen! • Johtuu osoittimen väärinkäytöstä
Tarkastellaan hieman muistia!Muistin osoite sinisellä, Arvot mustalla, Muuttujan nimi punaisella 1 -4717 2 -901 3 76 4 -0 5 98131 6 -1038 7 -554 8 7462 9 312 11 3619 10 -6 12 -4717 13 60981 14 4148 21 -78781 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 28 -9 22 -901 23 -6 24 6720 25 -4717 26 -19 27 21511 32 9 35 7851 29 17 31 -651 34 -896761 30 -6561 33 761 37 9996 38 674547 39 -6868 40 -1 41 5431 42 -4717 36 -6
Määritetään int-muuttujaint myInt; 1 -4717 2 -901 3 76 4 -0 5 98131 6 -1038 7 -554 8 7462 9 312 11 3619 10 -6 12 -4717 13 60981 14 4148 21 -78781 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 28 -9 22 -901 23 -6 24 6720 25myInt -4717 26 -19 27 21511 32 9 35 7851 29 17 31 -651 34 -896761 30 -6561 33 761 37 9996 38 674547 39 -6868 40 -1 41 5431 42 -4717 36 -6
Mitä tuli tehtyä? • Määrittelemällä int- muuttujan varasimme juuri ja juuri tarpeeksi muistitilaa int- luvulle • Ei hajuakaan missä päin muistia muuttujamme sijaitsee • Tietokone valitsee muistiosoitteen “satunnaisesti” • Mikä arvo on varaamassamme muistipaikassa? • Voimmeko tulostaa muistipaikan arvon? • Tulostaisimme –4717! (roskaa)
Kopioidaan 42 muuttujan osoittamaan muistipaikkaanmyInt = 42; 1 -4717 2 -901 3 76 4 -0 5 98131 6 -1038 7 -554 8 7462 9 312 11 3619 10 -6 12 -4717 13 60981 14 4148 21 -78781 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 28 -9 22 -901 23 -6 24 6720 25 myInt 26 -19 27 21511 42 32 9 35 7851 29 17 31 -651 34 -896761 30 -6561 33 761 37 9996 38 674547 39 -6868 40 -1 41 5431 42 -4717 36 -6
Osoittimet (Pointers) • Antaa mahdollisuuden päästä käsiksi muistipaikkaan, missä tieto sijaitsee • Osoitin määritellään seuraavanlaisella formaatilla: <tiedon tyyppi> *<muuttujan nimi>; • Example: int *ptr;
Määritetään int osoitinint *ptr; 1 -4717 2 -901 3 76 4 -0 5ptr 98131 6 -1038 7 -554 8 7462 9 312 11 3619 10 -6 12 -4717 13 60981 14 4148 21 -78781 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 28 -9 22 -901 23 -6 24 6720 25myInt 42 26 -19 27 21511 32 9 35 7851 29 17 31 -651 34 -896761 30 -6561 33 761 37 9996 38 674547 39 -6868 40 -1 41 5431 42 -4717 36 -6
Mitä nyt tuli tehtyä? • Loimme uuden muuttujan joka tulee osoittamaan int tietoon • Huomaa, että osoitin ei vielä osoita myInt sisältämään tietoon • Mitä jos yritämme tulostaa osoittimen sisällön?
cout << ptr;(ptr:n arvoksi tulostuu 98131) 1 -4717 2 -901 3 76 4 -0 5ptr 98131 6 -1038 7 -554 8 7462 9 312 11 3619 10 -6 12 -4717 13 60981 14 4148 21 -78781 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 28 -9 22 -901 23 -6 24 6720 25myInt 42 26 -19 27 21511 32 9 35 7851 29 17 31 -651 34 -896761 30 -6561 33 761 37 9996 38 674547 39 -6868 40 -1 41 5431 42 -4717 36 -6
Ongelma • Kuinka voimme saada myInt osoitteen, jotta ptr voi osoittaa sinne? • Muista, että voimme vielä käyttää myInt:ä suoraankin int someInt = myInt; • Tarvitsisimme todella keinon tallettaa myInt-muuttujan osoite • Meidän ei tarvitse tallettaa myInt muuttujan arvoa osoittimeen (vain osoite)
& operaattori • & operaattorin avulla päästään käsiksi muuttujan muistiosoitteeseen • Mitä seuraavanlainen komentorivi tulostaisi näytölle? cout << &myInt << endl;
Mitä tapahtuisi?cout << &myInt; 1 -4717 2 -901 3 76 4 -0 5ptr 98131 6 -1038 7 -554 8 7462 9 312 11 3619 10 -6 12 -4717 13 60981 14 4148 21 -78781 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 28 -9 22 -901 23 -6 24 6720 25myInt 42 26 -19 27 21511 32 9 35 7851 29 17 31 -651 34 -896761 30 -6561 33 761 37 9996 38 674547 39 -6868 40 -1 41 5431 42 -4717 36 -6
Laitetaan osoitin osoittamaan… • Nyt pitäisi laittaa “ptr” osoittamaan myInt -muuttujan sisältämään tietoon ptr = &myInt; ptr on osoitin, joten se olettaa että sen arvoksi annetaan osoite & -operaattorilla saamme myInt muuttujan osoitteen ja kopioimme osoitetiedon ptr-osoittimen arvoksi
Ennen 1 -4717 2 -901 3 76 4 -0 5ptr 98131 6 -1038 7 -554 8 7462 9 312 11 3619 10 -6 12 -4717 13 60981 14 4148 21 -78781 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 28 -9 22 -901 23 -6 24 6720 25myInt 42 26 -19 27 21511 32 9 35 7851 29 17 31 -651 34 -896761 30 -6561 33 761 37 9996 38 674547 39 -6868 40 -1 41 5431 42 -4717 36 -6
Jälkeenptr = &myInt; 1 -4717 2 -901 3 76 4 -0 5ptr 25 6 -1038 7 -554 8 7462 9 312 11 3619 10 -6 12 -4717 13 60981 14 4148 21 -78781 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 28 -9 22 -901 23 -6 24 6720 25myInt 42 26 -19 27 21511 32 9 35 7851 29 17 31 -651 34 -896761 30 -6561 33 761 37 9996 38 674547 39 -6868 40 -1 41 5431 42 -4717 36 -6
Mitäs tämä tekisi?ptr = myInt; 1 -4717 2 -901 3 76 4 -0 5ptr 98186 6 -1038 7 -554 8 7462 9 312 11 3619 10 -6 12 -4717 13 60981 14 4148 21 -78781 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 28 -9 22 -901 23 -6 24 6720 25myInt 42 26 -19 27 21511 32 9 35 7851 29 17 31 -651 34 -896761 30 -6561 33 761 37 9996 38 674547 39 -6868 40 -1 41 5431 42 -4717 36 -6
Hupsista!ptr = myInt; 1 -4717 2 -901 3 76 4 -0 5ptr 42 6 -1038 7 -554 8 7462 9 312 11 3619 10 -6 12 -4717 13 60981 14 4148 21 -78781 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 28 -9 22 -901 23 -6 24 6720 25myInt 42 26 -19 27 21511 32 9 35 7851 29 17 31 -651 34 -896761 30 -6561 33 761 37 9996 38 674547 39 -6868 40 -1 41 5431 42 -4717 36 -6
Osoittimen sisältö • Kuinka osoittimen sisältö saadan luettua? • Käytetään *-operaattoria • Esimerkki: cout << *ptr << endl; //Tulostaa ptr-osoittimen //osoittaman muistipaikan sisällön!
Seurataan osoitinta ja tulostetaancout << *ptr << endl; 1 -4717 2 -901 3 76 4 -0 5ptr 25 6 -1038 7 -554 8 7462 9 312 11 3619 10 -6 12 -4717 13 60981 14 4148 21 -78781 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 28 -9 22 -901 23 -6 24 6720 25myInt 42 26 -19 27 21511 32 9 35 7851 29 17 31 -651 34 -896761 30 -6561 33 761 37 9996 38 674547 39 -6868 40 -1 41 5431 42 -4717 36 -6