Kaukonäkötesti eli C++ tutti numero 4 ===================================== OSA IV Neljäs osamme leikiskelee ajatuksella muinaisista roomalaisista, jotka suuressa viisaudenpuolessaan keksivät mainion numerointitavan, ja jossa suurien lukujen esittäminen on mahdottomanpuoleista ja pienienkin melkoista miettimistä vaativaa. No eihän tuo oikiasti vaikiata ole vai mitäs sanotta esimerkkilöisyysluvustamme IVXII, jokas pitäisi tulkattavan nummeroksi 42. Niin kertolasku taisi ollansa suosiossansa myöhemmmin, kun esimerkkilöisemme olisi wanhemmalle tavalla XXXXII, jostas joo. Sanomattakin lienee selviö, ettäs 42 miljoonaa olisi aivan liiallisen vaikeahko lukuinen tuolla merkitätten. Notta laskeskellaan sitten vaan tämmöttisillä hassuloisilla luvuilla. Niin, paitsi jos tätä C++ tuttia kirjoitellaan aivan liian paljon, kuten 42 miljoonaa osaansa, niin ongelmiahansa tuossa tuleepi. Pitääpi varmaan silloin kylläs jo jollekullensa valitella, notta keksikääpä pian jonkunlainen sydeemi, nottas ei tarvihe tuon numeroinnin loppumisensa takia loputella myös tätä tuttisarjaansa, joka jo ties monennessako osassansa kohtaapi roomalaisen numerointinsa rajoitteensa. Niin ja siihen asiaan... Disclaimer ---------- Tässäpä ei ole disclaimeria tai mitään muuta varoitusta, koska sellaiset on todettu täysin turhiksi. Tai no ei oikeasti ole, joten kaipa tähän pitää pieni sitten kirjata. Enpäs siis ota vastuuta esimerkkien toimivuudesta tai asioiden oikeellisuudesta. Muista elämää suuremmista asioista en taidakkaan uskaltautua tässä puhelemaan. Jos kuitenkin muutaman sanan suuremmanpuoleisista esikuvistansa. Siis semmotisista, joita immeiset sitten ihannoipi ja haluavatten semmottisia ollansa. Tiesittekös muuten, nottei vihervörmöteillä ole tuollaisenkaltaista ihannoimisrakennetta, vaikka keltavörmöteille semmottinen on? Keltavörmöteillä on johtajahahmonsa, jota he ihannoitsevat ja palvovatten yli kaiken. Tämä johtajahahmoinen saapi kaiken, mitä haluaapi. Ja sekös sitten keksii haluta vaikka mitä, kuten paljon ravintoa, viihdykettä sekä kaikista parhaimman näköiseisen naaraspuoleisen vörmötin. Niin kappasta, kun tästä valitusta naarasvörmötistäkin tulee palvonnan kohteensa, joten kaikkihan tahtovatten olla sitten tuollainen kauniimmanpuoleinen, notta tulisi valitumman näköiseksi hän. Koska vörmöteillä ei ole mitään tekemistä C++ tutoriaalin kanssa, unohtakaamme nämä ja jatkakaamme itse asianpuoleista. Hiups ja huips. Ja kaikille tiedoksi vielä, että tätä saa levitellä vapaammanpuoleisesti, aivan kuten naapurin tyttö saa levitellä omia antejaan, kunhan ei ihan julkeasti mene levittelemään. Tai no kyllä tätä saa levittää kun ei sitä ole toistaiseksi laissakaan kielletty. Tai no on, sillä tämähän on teos ja tekijänoikeuden alainen, mutta kun näistä teoksista saa määrätä se tekijänoikeuden haltija, joka sattuu olemaan minä, niin minäpä päätänkin, että tätä saa levitellä. Ei kyllä menis läpi tämä selitys siinä naapurin tyttösen tapauksessa, vaan vankilaan pistäsivät tai ainakin sakkoja antasivat. On kyllä ihme touhua. Mutta enpä voi mitään, niin on joku minua viisahampi mennyt päättämään. Tosiaankin semmoinen lisäys vielä, että se paritus on kieällettyä niin eipä mennä sutenööriperkeleet perimään tämän levityksestä maksua elikkäs ilmahteeksi pitääpi siirrellä muille immeisille. Vakioaloitus ------------ Tähän perinteiseen tapaan vakioaloitus. Puhukaamme siis vakioaiheista ihan samalla tapaa kuin edellisissä osissansa. Eiku, tarkoitus oli vakioista puhua. Elikkätä, tässäpä meillä on tapoja määritellä jokin vakioksi elikkäs muuttumattomamman puoleiseksi. Aluksi otettakoon normaalinpuoleinen tapaus, jossa muuttujan arvon ei tahdota muuttuvan. Toinen tapaus voisi ollakkin semmoinen, jossa ei tahdota jonkin muun asian, kuten osoittimen, muuttuvan. Esimerkkiänsä: ---[ vakio.cpp ]--- #include using namespace std; int main() { //Määritellään a vakioksi, muuttujan a arvoa ei voi muuttaa const int a = 10; //int const a = 10; //Sama kuin edellä //Tavallinen muuttuja int a2 = 11; //Vakio-osoitin, tämän osoittimen osoittama arvo on vakio. const int* b = &a; //Osoitinvakio, tämän osoittimen arvoa ei voi muuttaa, osoittimen //osoittaman muuttujan arvo voi muuttua, koska sitä ei ole määritetty //vakioksi int* const c = &a2; //Vakio-osoitinvakio, tämä osoitin osoittaa vakioon, eikä osoittimen //tai osoitetun vakion arvoa voi muuttaa const int* const d = &a; //Tulostaa: 10 10 11 10 cout << a << " " << *b << " " << *c << " " << *d << endl; //Määritellään uusi vakio const int a3 = 12; //Asetetaan vakio-osoittimelle uusi arvo. Itse osoitin ei ole vakio, //vain sen osoittama arvo b=&a3; //Asetetaan muuttujalle uusi arvo a2=13; //Tulostaa: 10 12 13 10 cout << a << " " << *b << " " << *c << " " << *d << endl; return 0; } ------------------- Vakionpuoleisia vermehiä tulisi käyttää aina kuin mahdollista. Näin estellään ei-tahdotut sijoitukset ja muut hassut. Tuota constia voi tunkea luokkiinkin ja toimii samaan tapaan kuin osoittimiensa kanssa. Niin. Koskas on kiirus enkäs jaksa tähän alkaa pidempiä jorinoita jorisemaan, niin seuraavanpuoleinen aiheistus. Taulukot -------- Olkoot meillänsä tarvetta pitää muistissamme kymmenen arvoa. Noh, yksinkertaisesti varataan kymmenen muuttujaa, eikös juu? Noh, mutta entäs jos meillä onkista satatuhatta arvoa, niin entäs sitten? Kirjaappa vaan kuules käsin jokaikinen hikinen arvo erillisnimiseen muuttujaiseen. Alkaen aaaaa, ja tästä permutoiden joukossansa {a..z}, jolloista abcde:n kautta jjjjj ja tästä lopulta zzzzz tai kuin monta nuita nyt ikinä siihen sataantuhanteen tarviikaan. Ja käyttelyhän on helppoa kuin mikä! Tai sitten ei. Toisenlainen toteutus lienee paikallansa. No taulukko ois varmaan ihan kiva vaihtoehto. Sinne kun saapi helposti viitattua ja säädettyä kaikkeansa. Esimerkkiä: ---[ taulu.cpp ]--- #include using namespace std; int main() { //Taulu, jossa kymmenen alkiotansa int taulu[10]={1,2,3,4,5,6,7,8,9,10}; //Kaksiulotteinen taulu, jossa 2*5 alkiota elikkäs 10. int taulu2[2][5]={{1,2,3,4,5},{6,7,8,9,10}}; //Tulostetaan taulut for (unsigned int i=0; i<10; i++) { //taulu2[i/5][i%5], lasketaan i:n perusteella oikeat indeksit, //ensimmäinen välillä 0..1 ja toinen 0..5 cout << taulu[i] << "==" << taulu2[i/5][i%5] << " "; } cout << endl; //Täytetään taulu arvoilla. Numerointi nollasta, joten ensimmäinen alkio //on 0 ja kymmenes 9 for (unsigned int i=0; i<10; i++) taulu[i]=i*2; //Tulostetaan taulu for (unsigned int i=0; i<10; i++) cout << taulu[i] << " "; cout << endl; //Osoitin tauluun int *tmp = taulu; //Tulostetaan for (unsigned int i=0; i<10; i++) { cout << *tmp << " "; tmp++; } cout << endl; //Luodaan dynaamisesti uusi kahdeksanalkioinen taulukko tmp = new int[8]; //Asetetaan arvot ja tulostetaan for (unsigned int i=0; i<8; i++) { tmp[i]=8-i; cout << tmp[i] << " "; } cout << endl; //Tuhotaan taulukko ja vapautetaan sen varaama muisti delete [] tmp; return 0; } ------------------- Näinikään. Kun osaamme taulukoida (onneksi tätä ei ole käytetty edellisissä osissa ainakaan kaikissa esimerkeissä), niin ottakaamme käsittelyyn osoittimet, joita jo tuossa äsken (onneksi emme aiemmin ainakaan ihan jokaisessa esimerkissä) sivusimme. Joten lasit täyteen juomaa ja menoksi! Siis juoma saa olla vapaavalintaista tai sellaista, jota sattuu saatavilla olemaan. Jos muuta ei ole, niin hanasta saa yleneesä vettä ja se juomaksi laskettakoon. Jos vesilaskukin on unehtunut maksamatta, kyllä järvissä ja muissa muoliman lätäköissänsä vettä piisaa joka Matille ja Maijalle. Jos ei piisaa, niin sitten ei varmaan tarvi edes jatkaa ja voitte kuolla kaikki poies. Mutta kunhan oletta juomanne saaneet, siirtykäämme etiäpäin. Osoitus ja viittaus ------------------- Ei ole kohteliasta osoitella sormillansa, joten osoitellaan osoittimillansa. Osoitinhan on vermes, joka kertoo, notta missä on jotain muuta. Siis olkoon meillä muuttujanpuoleinen, jolla on varattuna muistia ja hieno arvo. Osoitin sitten on vermes, joka kertoopi, notta missä päin muistia elikkäs missä osoitteessa tuo mahtaa olla. Tämä osoitinkin sitten vie oman muistinsa eli riippuen laitteistostansa se viepi sen määräisen verran memmoryä. Ja toisin kuin C:ssä C++ on vahvasti tyypitetty, joten täällä osoittimella on oltava tyyppi. Siis kerrotaan, notta mitä tyyppiä se osoitettu data onkaan. Tietysti on "olematon" osoitintyyppi void*, joka osoittaapi mitä tahansa dataa. Ja tässä vain sojoitellaan kohti muualla olevaa datanpuoleista, joten joo. Ja kun tuohon dataan voipi sitten sojottaa vaikka kuin monta vermettä, joten pitääpi olla tarkemmanpuoleinen, nottei ihan kaikkea mene sekaisin saamaan. Toinen asiainen on viittaukset. Nämä ovatten käteviä, kun pitää esim. parametrina antaa muuttujan arvon sijasta itse muuttuja, siis siten, notta muuttujan arvo muuttuupi siellä kutsuvassa päässänsä myös. Toki tämän voisi tehdä osoittimilla, mutta viittaukset on kätevämpiäpätevämpiä. Yhdistetty mäkiviikko-osoitin-viittaus -esimerkki: ---[ osoita.cpp ]--- #include using namespace std; void invSwap(int a, int b) { int tmp=a; a=b; b=tmp; } void ptrSwap(int *a, int *b) { int tmp=*a; *a=*b; *b=tmp; } void refSwap(int &a, int &b) { int tmp=a; a=b; b=tmp; } int main() { int a1=5,a2=7; cout << a1 << " " << a2 << " -> " //Väärä tapa yrittää toteuttaa vaihtofunktio, arvot eivät muutu invSwap(a1,a2); cout << a1 << " " << a2 << endl; a1=5;a2=7; cout << a1 << " " << a2 << " -> " //Annetaan parametrina osoittimet, arvot muuttuu, mutta toteutus ikävän //mallinen ja ongelmia, jos annetaan virheellinen osoitin, esim. //ptrSwap(&a1,NULL); ptrSwap(&a1,&a2); cout << a1 << " " << a2 << endl; a1=5;a2=7; cout << a1 << " " << a2 << " -> " //Käytetään viitteitä, ei eroa invSwap-kutsuun, funktion määrittelyssä //vain &-merkit ilmaisemassa viitettä. refSwap(a1,a2); cout << a1 << " " << a2 << endl; //Tavallinen int-muuttuja int a=1; //Osoitin int-muuttujaan, &a tarkoittaa muuttujan a osoitetta. int *pa = &a; //Int-osoitin ei osoita minnekään int *pa2; //Merkkijono string s = "merkkijono"; //Osoitin string-luokkaan string *ps = &s; //Osoitin tuntemattomaan tyyppiin (tässä int) void *pv = &a; //Char-taulukko char c[] = "C-merkkijono"; //Char-osoitin, alustetaan osoittamaan ennaltamäärättyyn arvoon char *pc = "C-merkkijono-osoitin"; //Tulostetaan muuttujan a arvo, osoittimen pa osoittaman datan arvo sekä //muuttujan a osoite ja osoittimen pa arvo eli muistipaikka johon se //osoittaa. Loppuun vielä pa:n osoite. cout << a << "==" << *pa << " " << &a << "==" << pa << " " << &pa << endl; //Sama void-osoittimen kanssa, pitää ilmaista minka tyyppistä dataa //void-osoitin osoittaa cout << a << "==" << *((int*)pv) << " " << &a << "==" << pv << " " << &pv << endl; //Tulostetaan alustamattoman osoittimen osoittaman datan arvo, //osoittimen arvo ja osoite cout << *pa2 << " " << pa2 << " " << &pa2 << endl; //Muunnetaan void-osoitin int-osoittimeksi pa2=(int*)pv; //Tulostetaan int-osoittimen osoittaman datan arvon, osoittimen arvo ja //osoite cout << *pa2 << " " << pa2 << " " << &pa2 << endl; //Tulostetaan s:n arvo, string-osoittimen arvo, s:n osoite, //string-osoittimen arvo ja osoite cout << s << "==" << *ps << " " << &s << "==" << ps << " " << &ps << endl; //Void-osoittimen arvoksi string-osoittimen arvo pv = ps; //Tulostetaan int-osoittimeksi muunnettu void-osoitin, s:n osoite,' //pv:n arvo ja osoite. Koska pv:n osoittaman datan arvo on oikeasti //string-tyyppinen, seuraa tästä muunnoksesta se, että näytetään //int-tyyppin verran dataa kyseisestä muistiosoitteesta. cout << *((int*)pv) << " " << &s << "==" << pv << " " << &pv << endl; //Tulostetaan char-taulukon ensimmäinen alkio, arvo ja osoite cout << c[0] << " " << c << " " << &c << endl; //Tulostetaan char-osoittimen osoittama arvo, osoittimen arvo ja osoite cout << *pc << " " << pc << " " << &pc << endl; //Char-osoittimen arvoksi char-taulukon ensimmäisen alkion osoite pc=c; cout << *pc << " " << pc << " " << &pc << endl; return 0; } -------------------- Nyt sitä osataan osoitella ja viittelöidä vaikka mitä. Ja tässä kun vielä alkaa se sitten sojottamaan kohti taivahia, kun näkeepi jotain kummalisenpuoleista, notta ollanpa kiltimmänpuoleisesti juu, kun se joulukin meni jo melkein! Merkkijonoista -------------- Tämmöisistä hassuista vielä. Kun jo niistä stringeistä, siis string-luokasta, puheskeltiin aiemmin, niin mainitsenpa vielä näistä C-tyylisistä merkkailujonoisista, joita ei pitäisi enää käyttämän kunnon C++ -ongelmoijien. Kun ei niissä muuten mitään huonoa ole, mutta kun niissä on niin paljon huonoa, kuten suuret mahdollisuudet ylivuoteluun (valkovuotelun lisäksi), tietoturpa- aukkoihinsa ja kaikkeen muuhun pahaan. Otettakoon esimerkkilöisiä. Varoituksen tapainen sitten ihmisille, notta väärällä tavallansa tässä esimerkkilöidään, älkääpä tehkö näin tahi joulubugi ei tuo sitten lahjoja seuraavalla kerralla! ---[ merkkijono.cpp ]--- #include using namespace std; int main() { //Määritellään merkkijonoinen const char *p = "Testimerkkailujono"; //Lasketaan sen pituus, siis tuon merkkijonon unsigned int len=0; //*p:n arvo on nolla, kun tuo loppuupi eli siellä on se //nolla-terminaattori (Arska-terminaattori ei kelpaa) while (*p++) ++len; //Tässä vaiheessa p osoittaa merkkijonon viimeisen alkion jälkeiseen //arvoon... Siis, kyllä! Tuon lasketun merkkijonon null-terminatorista //seuraava merkki. Mutta eihän sitä ole? No kyllä siellä muistissa //jotain on, mutta mitä? Tulostetaanpa tämä hieno merkkijono ja äsken //laskemalla saatu pituus!!! (Varoitus, tulostaa jotain "tuntematonta" //muistialuetta, joka sattuu olemaan kohdalla, kunnes tulee seuraava //nollatavu. Minulla tuo näyttää tulostavan kokonaisuudessansa //", pituus: , pituus: 18" eli kääntäjä lienee laittanut merkkijonot //peräkkäin muistiin. Jokin toinen kääntäjä saattaa tehdä toisin ja //tulostus voi olla mitä tahansa. Sinua on varoitettu) cout << p << ", pituus: "<< len << endl; //Korjataanpa hieman, eli vähennetään saatu pituus sekäs tuo //null-terminator (ilman tuon huomioonottamista tuo jäisi ensimmäistä //merkkiä vaillensa). p-=len+1; //Tulostetaan nyt hieman korjattua cout << p << ", pituus: "<< len << endl; //Alustetaan merkkijono char* tmp="jotain"; //Asetetaan ensimmäiseksi merkiksi iso J. Tämä tuottaa minulla //segmentaatiovirheen, koska tuolle alueelle ei oikeasti saa //kirjoittaa... Siis kyllä, muistinsuojausvermehiä. Uhkarohkeat saavat //epäkommentoida seuraavan rivin. //tmp[0]='J'; //Luodaan char-taulukko ja alustetaan se jollakin arvolla. char tmp2[] = "testi"; //Asetetaan tmp osoittamaan taulukon ensimmäiseen arvoon tmp=tmp2; //Muutetaan ensimmäinen merkki isoksi T-kirjaimeksi. Nyt tämä toimii, //sillä tuolle muistialueelle (char-taulukko) saa kirjoittaa. tmp[0]='T'; //Tulostetaan. cout << tmp << endl; //Muutetaan vielä 6. merkki huutomerkiksi. Mutta tuo onkin null- //terminator, joten nyt taas päästään vuotamaan yli... tmp[5]='!'; //Tulostetaan tmp ja sen perään "<". Tässä minulla sattuu olemaan ties //mitä dataa null-terminatorin jälkeen, ja tuloste on: Testi!ÿ¿öÿÿÿ< cout << tmp << "<" << endl; //Jos oikein ilkeäksi ruvetaan, niin heitetäänpä vähän taulukon reunojen //yli jotain "kivaa". Tiedä sitten, että mitä tuolta tulee //ylikirjoitettua. Mutta tätä ET halua ohjelmissasi tapahtuvan. for (unsigned int i=0; i<8; i++) tmp[i]='A'; //Ja ulostetaan... cout << tmp << "<" << endl; //Ja testiksi hieman vuotelua stringillä: string tmps = "testi"; //Ensimmäiseksi merkiksi T tmps[0]='T'; //Kuudenneksi merkiksi huutomerkki. Tämä ei tee mitään sillä //ylikuormitettu [] -operaattori pitää huolen, ettei varatun //muistialueen yli kirjoiteta. tmps[5]='!'; //Ja tulosteisuus cout << tmps << "<" << endl; } ------------------------ Niin, kokeillaanpa tuota, jolloin tuo tulostaapi: "Jouluun on 0 päivää 3 tuntia 21 minutia ja 39 sekuntia." Eiku eipäs tulostakaan, sillä tuohan oli joku vanha esim. Erkki! Mutta! KOLOME YÖTÄ JOULUUN ON, LASKIN AIVAN ITSE!!!!!1!1! Eikun se tais mennä jo. Vai onkosta tuo sittenkin kolome tuntia? Niinhän se mahtaapi olla. Ihmeisyys kun tuo toimiipi edelleensä. Niin, tosin ajoitushan tuossa on hassu, sillä eihän joulu oikeastinsa ala reilun kolmen tunnin kuluttua, vaan silloinhan alkaapi jouluaattoinen. Ja siis tuon päiväisen keskiöinen hetkilyömänsä, jolloin kellot kilkattaapi. Siis ainakin tällä aikavyöhykkeellänsä. Eräillä toisillansa se onpi jo alkanut ja toisillansa se alkaapi vasta myöhemmin. Ja joidenkin mielestänsä pitäisi vielä yö nukkua. Ja mitä ihmeen pilikunviilausta ja sitä toisenlaista aktiviteettia. Niin, kyllä. Tämän otsakkeisen voisi laitella jonnekin tämän tuttiosion keskivaiheillensa, notteivat tiedä, missä järjestyksessä olen asioita kirjoitellut. Näinpä teen. Ja lisäänpä harhautukseksi, notta seuraavaksi puhellaan. Mutta enpäs kerrokkaan, notta mistä tahi mitä! No okei, hevonpaskaista ja palturia. Ootteko nyh tyytyväisemmänpuoleisia? Hä, hä? Bittitason operointia --------------------- Niin, jotta jokainen bittinen on vaivan arvoinen, jokainen bittinen on tärkeä? No onhan ne, jos tahtoopi olla tarkka biteistään. Kun normaalistihan char on yhden tavun kokoinen kokonaisuutensa, niin entäpä, jos ei tarvitakaan koko vermehettä tahi tarvitaan kaksi 4-bittistä muuttujaista. Tai kolme 2-bittistä. No eipä hätiä, meillähän on vaikka mitä. Katsellaanpa: ---[ bitti.cpp ]--- #include #include using namespace std; int main() { //Hiano bitti-tietuerakenne struct bits { unsigned char a:2; //2 bittiä eli 0..3 unsigned char b:6; //6 bittiä eli 0..63 unsigned char c:1; //1 bitti eli 0..1 unsigned char d:5; //4 bittiä eli 0..31 char e:3; //3 bittiä etumerkillä eli -4..3 }; bits b; //Tarkoituksella ylisuuri arvo. Tapahtuu sama kuin "normaalien" tyyppien //kanssa eli ylärajan umpeuduttua aloitetaan alusta. Tästä pitäisi tulla //siis 2 (0..3, 4..7==0..3, 8..10==0..2) b.a=10; //Seuraavat kuuluvat arvoalueeseensa b.b=60; b.c=1; b.d=1; b.e=-4; //Tulostetaan kaikkien arvot, muunnos int-tyypiseksi, sillä oletuksena //char-tyyppiset arvot tulostetaan ASCII-merkkeinä. cout << (int)b.a << " " << (int)b.b << " " << (int)b.c << " " << (int)b.d << " " << (int)b.e << endl; //Tulostetaan koko vermeen viemä koko tavuissa. Pitäisi tulostaa 3, //sillä käytetty tietotyyppi on char ja bittejä kuluu 17 kpl (2,125 //tavun verran). Jos käytetään tyyppiä int tai short, kuluu 4 tavua //(siis 32-bittisellä suorittimella; yksi int tai kaksi short). cout << sizeof(b) << endl; //Toinen tapainen on bitset, joka on STL-vermehiä, määritetään //10-bittinen vermes, alustetaan arvolla 0x100 (256==100000000b) bitset<10> bits(0x100); //Seuraavat lienevät itsestäänselittäviä... if (bits.any()) cout << "Jokin bitti asetettu" << endl; else cout << "Mikään bitti ei ole asetettu" << endl; cout << bits.count() << " bitti(ä) asetettu" << endl; cout << "1. bitti "; if (bits[0]) cout << "asetettu" << endl; else cout << "pois" << endl; cout << "8. bitti "; if (bits[7]) cout << "asetettu" << endl; else cout << "pois" << endl; cout << "9. bitti "; if (bits[8]) cout << "asetettu" << endl; else cout << "pois" << endl; cout << "9. bitti "; if (bits.test(8)) cout << "asetettu" << endl; else cout << "pois" << endl; cout << "10. bitti "; if (bits[9]) cout << "asetettu" << endl; else cout << "pois" << endl; //Vaihtaa jokaisen bitin tilan vastakkaiseksi bits.flip(); cout << bits.count() << " bitti(ä) asetettu" << endl; //Nollaa kaikki bitit bits.reset(); cout << bits.count() << " bitti(ä) asetettu" << endl; //Asettaa kaikki bitit bits.set(); cout << bits.count() << " bitti(ä) asetettu" << endl; bits[3]=0; bits[6]=0; cout << bits.count() << " bitti(ä) asetettu" << endl; bitset<4> bits2(string("1011")); cout << bits2.count() << " bitti(ä) asetettu" << endl; cout << sizeof(bits) << " " << sizeof(bits2) << endl; cout << bits << " " << bits2 << endl; return 0; } ------------------- Don't bit me! Pure bittiä! Lailaalalal. Lehmät ne lentää talvella, kun muuten utareet jäätyypi. Tittiritit tit tii! Niin, maanantaina muuten satoi lunta, melkoista paljonkin. Sittenpä tuiskusi ja pyrytti. Niin ja ne perkeleen lehmät vaan pilvien päällä lentelivät auringonpaistehessa. Tämmöstä peliä ei paljoa vedellä, oottakoot vaan kun tulee kesä ja sieltä laskeutuvatten, niin lihoiksi laitan mokomat ja teen piffiä. Näin. Operaattorien sidontajärjestys ------------------------------ Niin siis, notta missä järjestyksessä nuo eri operaattorit suoritetaan? Matemaatikot kutsuisivatten tätä laskujärjestykseksi, muttas täällähän tuo onpi sitten sidontaan elikkäs joopati. Kun kaikkihan tietävät, että: 1+3*4/5-2 == (1+((3*4)/5))-2 Muttas miten tuo säännöstellään? Kas näie, ja ylempänä listassa olevat ovat korkeammalla järjestyksessä, alimmaiset matalampana. Lisäksi joillakinsa on sama järjestys (esim. kerto- ja jakolasku), jolloin ne suoritetaan assosiatiivisuutensa mukaan vasemmalta oikealle. Esim. 2 - 1 + 3 = 1 + 3 = 4 eikä 2 - 4 = -2, vaikka yhteenlasku on listassansa korkeammalla, mutta oikeasti nuo ovatten samanarvoisia elikkäs suoritetaan vasemmalta oikialle. Muttas tuolla onpi muutama oikialta vasemmalle assosiatiivinen, kuten sijoitus, elikkä: int a,b,c; a = b = c = 5; Tällöistä kaikkien arvo on 5, koska aluksi c=5, sitten b=c ja lopuksi a=b. Operaattori Selitys ----------- ------- :: Globaali näkyvyys, ::joku :: luokka::joku :: nimiavaruus::joku . Tietorakenteen jäsen, rakenne.joku -> Osoitin rakenteeseen, rakenteen jäsen, osoitin->joku [] Indeksi, joku[x] () Funktiokutsu, joku(x) () Tyypin muodostus, tyyppi(x) ++ Kasvatus lopussa, joku++ -- Vähennys lopussa, joku-- typeid typeid(joku) _cast Tyypinmuunnos (const,dynamic,reinterpret,static) const_cast(joku) ++ Kasvatus alussa, ++joku -- Vähennys alussa, --joku ~ Bitettäinen ei, ~joku ! Looginen ei, !joku - -joku + +joku * Käänteinen viittaus (joku==osoitin), *joku & Osoite, &joku () Tyypinmuunnos, (tyyppi)joku new Muistinvaraus, new tyyppi delete Muistin vapautus, delete joku ->* osoitin->*joku .* rakenne.*joku () Laskujärjestys, (joku+x)*y * Kertolasku, joku*x / Jakolasku, joku/x % Jakojäännös, joku%x + Lisäys, joku+x - Vähennys, joku-x << Bittisiirto vasemmalle, joku<> Bittisiirto oikealle, joku>>x < Pienempi kuin, joku Suurempi kuin, joku>x >= Suurempi tai yhtäsuuri kuin, joku>=x == Yhtäsuuri, joku==x != Erisuuri, joku!=x & Biteittäin ja, joku&x | Biteittäin tai, joku|x && Looginen ja, joku&&x || Looginen tai, joku||x ?: Ehtolause, joku?tosi:epä = Sijoitus, joku=x *= /= %= += -= <<= >>= &= |= ^= Yhdistetty sijoitus, joku*=x throw Poikkeus, throw joku , joku, muu Elikkäs joo. Mainitaa pitää harrastaa kohdissa muutamissa. Biteittäin tarkoittaapi, notta jokaiselle bitille vastaavissa operandeissansa tehdään tuo toiminne, esim. 20&5=4 (10100b & 00101b = 100b, eli jokaista bittiä verrattaneen tuolla ja-vermehellä). Mutta looginen tarkoittaapi boolean-tyyppistä vertailuansa, kuten: if (a>1 && a<9) { } Tuossansa sitten blokki suoritettaneen, jos a on välillä 2..8. Tuossa vielä hienosti tuo osaapi tehdä siten, notta ensin vertaillaan a>1, jos tuo on palturia niin hypätään suosiolla ohi, jos taas ei, niin vertaillaan a<9 ja tällöin jos tuo on palturia, hypätään ohi, muutoin suoritetaan tuo. Muutama esimerkki: 1 + 2 * 6 / 3 - 4 + 6 Tulos on 7, sillä: = 1 + 12 / 3 - 4 + 6 = 1 + 4 - 4 + 6 = 5 - 4 + 6 = 1 + 6 = 7 Tämmöttinen: int a=5; 1 + 2 - (-3 + + 4) * ++a Tulos on -3, sillä: = 1 + 2 - (-3 + 4) * 6 = 1 + 2 - 1 * 6 = 1 + 2 - 6 = 3 - 6 = -3 Ja vielä: int a; while (a = 077 & lueArvo() != 0) Kyseinen lause sulkujen kanssa, kuten kääntäjä sen tulkitsee while (a = (077 & (lueArvo() != 0)) ) Yllättäen meillä onkin mahdollisesti ongelma, sillä vaikka lueArvo() palauttaa joskus nollan, suoritus pääättyy, mutta muutoin tuon toiminta on epäjärkevää, sillä a:n arvoksi tulee joko 1 tai 0 (jos suoritus päättyy). Tarkoitettu järjestys lienee tämämuotoinen: while ((a = (077 & lueArvo())) != 0) Tässä siis luetaan arvo, tehdään bitittäinen ja, sijoitetaan a:han ja tarkistetaan oliko se erisuuri kuin nolla. Sulkuja kannattaa käyttää, sillä ne selkeyttävät koodia ja estävät ei-tarkoitetut järjestykset. Ja kun sulut ei maksa mitään, paitsi pari tavua per pari lähdekoodissa, on tuo sikahalpa verrattuna siitä saatavanpuoleiseen hyötyynsä. Niin. Keksikääpä vaikka itse omia ihmekoukeroitanne ja purkailkaa niitä nätimmänpuoleisiksi vermeheiksi tahi etsikääpä jostakin valmiita tuollaisia, minä en jaksa alkaa nuita miettimään. Juu, pitäs varmaan nukkuloida taas vaihteeksi, kun en ole sitten kolmannen tutin nukkunutkaan! Tai no olen, mutta en ole teillensä siitä maininnut, joten niitä ei lasketa. Ettäs muutaman viikon univelkaiset tämän mukaan shitten. Hurprömps. Esitystavat ----------- Niin, tuota dataa voidaan kirjailla soodiin erinäköisillä tavoillansa. Ei tarvihe välttämättä sanoa noin, kun voipi tehdä näin. Tai sitten tehdään nuin ihan vaan sen takia, koska se on kätevintäpätevintä niin. Koska viherhyypän pesintään on vielä aikaa, ehdinkin tämän vielä sepostella teillensä. Niin, ajasta tuli taas mieleen, että voisikin kehittää tuollaisen YMT eli Year Mean Time, jossa aikaa mitataan erona keskiyöhön (eli klo 00:00) vuoden ensimmäisenä päivänä (1.1.). Tuossa sitten aikaa sekunteina juostaisiin ja kustaisiin. Näin vuoden sekunit saataisiin esitettyänsä 24 bitillä. Tästä jäisi 32-bittisestä kokonaisluvusta vuotta varten vielä 8 bittiä elikkäs vuosi-ikkuna-akkuna olisi 256 vuotta. Muttas ei kukaan noin tyhmiöjärjestelmää tekisi, joten reilusti vaan muuta vermettä. Eli vuoden sekunteja varten 3 tavua ja vuotta varten 2 tavua, ja kyllä saadaan aikaa taakse ja etiäppäin runsahasti. Tyhmiä ovatten he, jotka suossa rämpivätten. Tosin samallapa tuossa voisi 64-bittisiä lukuja otella käyttöhön, sillänsä se olisi järkevimmänpuolehista. Niin, mutta minun murheeni ei. Merkkaillaanpa lukua. ---[ esita.cpp ]--- #include using std::cout; using std::endl; int main() { //Määritellään erilaisia vermehiä int a=42; int b=0x2A; int c=052; char d='\052'; //Ulostellaan ne cout << a << " " << b << " " << c << " " << (int)d << endl; //Määritellään wchar-tyyppinen merkkijono. Tällä voidaan esittää //useampia merkkejä kuin perinteisesti, sillä yhtä merkkiä varten //varataan useampi kuin yksi tavu. wchar_t *hum1 = L"Humm"; //Tavallinen merkkijono char *hum2 = "Humm"; //Tulostetaan nämä. Huomaa, että hum1:n arvo ei tulostu cout << hum1 << " " << hum2 << endl; //Tulostetaan yhden merkin koko cout << sizeof(wchar_t) << " " << sizeof(char) << endl; wchar_t *t1=hum1; char *t2=hum2; //Tulostetaan jokainen merkki molemmista jonoista for (unsigned int i=0; i