====== C keele kiirkursus ====== ===== Programmi ülesehitus ===== Põhimõtteliselt võib C-keele programmi kirjutada ükskõik mis kujul, kas või üherealisena, sest kompilaator eeldab vaid süntaksireeglite järgimist. Samas on selguse ja lihtsuse huvides ikkagi soovitatav tähelepanu pöörata ka programmikoodi stiilile. Tüüpiline C-keele programmi ülesehitus: /* Päisefailide kaasamine */ #include #include /* Makro-deklaratsioonid */ #define PI 3.141 /* Andmetüübid */ typedef struct { int a, b; } element; /* Globaalsed muutujad */ element e; /* Funktsioonid */ int main(void) { // Lokaalsed muutujad int x; // Programm printf("Tere maailm!\n"); } ===== Kommentaarid ===== Programmi saab kirjutada teksti, mida ei kompileerita ja mis on programmeerijale abiks selgitamisel või märkmete tegemisel. Samuti saab kommentaare kasutada programmilõikude ajutiseks täitmisest kõrvaldamiseks. Näide kahte liiki kommentaaridest: // Üherealine kommentaar. // Kommentaariks loetakse kahe kaldkriipsu järel olevat teksti /* Mitmerealine kommentaar Kommentaari algus ja lõpp määratakse kaldkriipsude ja tärnidega */ ===== Andmed ===== ==== Andmetüübid ==== C-keele baasandmetüübid: ^ Tüüp ^ Miinimum ^ Maksimum ^ Bitte ^ Baite ^ | (signed) char | -128 | 127 | 8 | 1 | | unsigned char | 0 | 255 | 8 | 1 | | (signed) short | -32768 | 32767 | 16 | 2 | | unsigned short | 0 | 65535 | 16 | 2 | | (signed) long | -2147483648 | 2147483647 | 32 | 4 | | unsigned long | 0 | 4294967295 | 32 | 4 | | float | -3.438 | 3.438 | 32 | 4 | | double | -1.7308 | 1.7308 | 64 | 8 | Sulgudes olevat sõna "signed" ei pea kasutama, kuna vaikimisi ongi andmetüübid bipolaarsed. AVR mikrokontrolleril //int// = //short// \\ PC arvutil //int// = //long// \\ C-keeles puudub spetsiaalne teksti-andmetüüp. Selle asemel kasutatakse //char// tüüpi massiive (nendest edaspidi) ja ASCII "tähestikku", kus igal tähel ja märgil on oma järjekorranumber. ==== Muutujad ==== Programmis saab kasutada kindlat andmetüüpi mälupesasid - muutujaid. Muutujate nimed võivad sisaldada ladina tähestiku tähti, numbreid ja alakriipsu. Nimi ei tohi alata numbriga. Muutuja deklareerimisel kirjutatakse selle ette andmetüüp. Väärtuse omistamiseks muutujale kasutatakse võrdusmärki (=). Näide muutujate kasutamisest: char c; // char tüüpi muutuja c deklareerimine c = 65; // Muutujale c väärtuse omistamine. c = 'A'; // A on ASCII märgisüsteemis samuti väärtusega 65 // int tüüpi muutuja i20 deklareerimine ja algväärtustamine int i20 = 55; // Mitme unsigned short tüüpi muutuja deklareerimine unsigned short x, y, test_variable; ==== Konstandid ==== Konstante deklareeritakse samamoodi nagu muutujaid, kuid ette lisatakse //const// võtmesõna. Konstantide väärtust ei saa programmi käigus muuta. Näide kasutamisest: const int x_factor = 100; // int tüüpi konstandi määramine ==== Struktuurid ==== Baasandmetüüpidest saab //struct// võtmesõnaga struktuure koostada. Struktuur on justkui kombineeritud andmetüüp. Tüüpi deklareeritakse //typedef// võtmesõnaga. Näide struktuurist andmetüübi loomise ja kasutamise kohta: // Uue punkti andmetüübi deklareerimine typedef struct { // x ja y koordinaat ning värvikood int x, y; char color; } point; // Punkti muutuja deklareerimine point p; // Punkti koordinaatide määramine p.x = 3; p.y = 14; ==== Massiivid ==== Andmetüüpidest võib koostada massiive (jadasid). Massiiv võib olla ka mitmemõõtmeline (tabel, kuup, jne). Näide ühe- ja kahemõõtmelise massiivi kasutamisest: // Ühe- ja kahemõõtmelise massiivi deklareerimine char text[3]; int table[10][10]; // Teksti moodustamine märgimassiivist text[0] = 'H'; // Täht text[1] = 'i'; // Täht text[2] = 0; // Teksti lõpetamise tunnus (null-bait) // Tabeli ühe elemendi muutmine table[4][3] = 1; ===== Avaldised ===== Muutujate, konstantide ja väärtust tagastavate funktsioonidega saab koostada avaldisi (tehteid). Avaldise väärtusi saab omistada muutujatele, neid saab kasutada funktsiooni parameetritena ja erinevates tingimuslausetes. ==== Aritmeetilised ==== C-keeles toetatud aritmeetilised tehted on liitmine (+), lahutamine (-), korrutamine (*), jagamine (/) ja mooduli võtmine (%). Näited aritmeetiliste tehete kasutamisest: int x, y; // Mooduli võtmine, korrutamine ja väärtuse omistamine, x saab väärtuse 9 x = (13 % 5) * 3; // Liitev-omistav operaator, x väärtuseks saab 14 x += 5; // Ühe lahutamise kiirmeetod, x väärtuseks saab 13 x--; ==== Loogilised ==== Loogilised tehted on eitus (!), loogiline korrutamine (&&) ja loogiline liitmine (||). Näide tehete kasutamisest: bool a, b, c; // Algväärtustamine a = true; b = false; // Eitus // c väärtus tuleb väär sest tõest eitati c = !a; // Loogiline korrutamine // c väärtuseks tuleb väär, sest üks operandidest on väär c = a && b; // Loogiline liitmine // c väärtus tuleb tõene, sest üks operandidest on tõene c = a || b; **NB!** //bool// andmetüüp on pärit C++ keelest ja C-keeles see tegelikult puudub, sest selle asemel on kasutusel täisarvud, kus 0 tähistab väära ja iga muu arv tõest väärtust. Kuid mugavuse pärast on //bool// Kodulabori teegis siiski kasutusel ja see on defineeritud kui //unsigned char//. Konstant //true// tähistab seal väärtust 1 ja //false// väärtust 0. ==== Võrdlused ==== Arvude väärtuste võrdlemisel saadakse loogilised väärtused. Võrdlustehted on samaväärsus (==), erinevus (!=), suurem (>), suurem-võrdne (> =), väiksem (<) ja väiksem-võrdne (< =). Näide kasutamisest: int x = 10, y = 1; // Suurem-kui võrdlustehe, mis on tõene // Tehte ümber on sulud vaid selguse pärast bool b = (5 > 4); // Erinevuse tehe // Tehte tulemus on väär b = (4 != 4); // Aritmeetiline, võrdlus ja loogiline tehe üheskoos // b tuleb väär, sest esimene loogilise korrutise operand on väär b = (x + 4 > 15) && (y < 4); ==== Bititehted ==== Bititehted on andmetega binaarses arvusüsteemis tegelemiseks. Neid saab rakendada kõigi täisarv-tüüpi andmetega. Bititehted sarnanevad loogiliste tehetega, kuid erinevad selle poolest, et tehe teostatakse iga bitiga eraldi, mitte kogu arvuga. C keeles on bititeheteks inversioon (~), konjunktsioon (&), disjunktsioon (|), antivalentsus (^), nihe vasakule (<<) ja nihe paremale (>>). // Märgita 8-bitise char-tüüpi muutuja deklareerimine // Muutuja väärtus on kümnendsüsteemis 5, kahendsüsteemis 101 unsigned char c = 5; // Muutuja c disjunktsioon arvuga 2 (kahendsüsteemis 010) // c väärtuseks saab 7 (kahendsüsteemis 111) c = c | 2; // Bitinihe vasakule 2 võrra // c väärtuseks saab 28 (kahendsüsteemis 11100) c = c << 2; Bititehted on hädavajalikud mikrokontrollerite registrite kasutamisel. Täpsemalt tutvustab neid AVR registrite peatükk. ===== Funktsioonid ===== Funktsioon on programmilõik, mida saab selle nime järgi täitmiseks välja kutsuda. Funktsioonil võivad olla parameetrid ja funktsioon võib tagastada ühe väärtuse. Kui funktsioon ei tagasta väärtust, on selle tüüp //void//. Kui funktsioonil pole parameetreid, tuleb vanemat C-keele kompilaatorit kasutades //void// kirjutada ka parameetrite deklaratsiooni asemele. Näide liitmisfunktsioonist ja tagastamisväärtuseta funktsioonist: // Kahe int tüüpi parameetriga funktsioon, mis tagastab int-tüüpi väärtuse int sum(int a, int b) { // Kahe arvu liitmine ja summa tagastamine return a + b; } // Ilma parameetrite ja tagastatava väärtuseta funktsioon void power_off(void) { } Funktsiooni kasutamiseks tuleb see välja kutsuda. Funktsioon peab programmikoodis olema deklareeritud enne välja kutsumise kohta. Näide liitmisfunktsiooni väljakutsumisest. int x; int y = 3; // Liitmisfunktsiooni väljakutsumine, kus parameetriteks on muutuja ja konstandi väärtus x = sum(y, 5); // Väljalülitamise funktsiooni väljakutsumine, parameetrid puuduvad power_off(); C-keele programmi täitmist alustatakse //main// nimelisest funktsioonist, mis teeb selle kohustuslikuks funktsiooniks. ===== Laused ===== ==== Tingimuslause ==== Tingimuslause võimaldab vastavalt sulgudes oleva avaldise tõesusele täita või mitte täita tingimusele järgnevat lauset või programmilõiku. Tingimuslause võtmesõna on //if//. Näide kasutamisest: // Avaldis on tõene ja lause täidetakse, // sest 2 + 1 on suurem kui 2 if ((2 + 1) > 2) x = 5; // Kui x on 5 ja y on 3, siis täidetakse järgnev programmilõik if ((x == 5) && (y == 3)) { // Suvaline tegevus y = 4; my_function(); } Tingimuslause võib olla pikem ja sisaldada ka lauset või programmilõiku, mis täidetakse avaldise mittetõesuse korral. Selleks tuleb //if// tingimuslause järel kasutada //else// võtmesõna. // Kas x on 5 ? if (x == 5) { // Suvaline tegevus z = 3; } // Kui x ei olnud 5, kas siis x on 6 ? else if (x == 6) { // Suvaline tegevus q = 3; } // Kui x ei olnud 5 ega 6... else { // Suvaline tegevus y = 0; } ==== Valikulause ==== Kui on vaja võrrelda avaldist mitme erineva väärtusega, on mõistlik kasutada valikulauset //switch// võtmesõnaga. Näide kasutamisest: int y; // Tingimuslause y võrdlemiseks switch (y) { // y on 1 ? case 1: // Suvaline tegevus function1(); break; // y on 2 ? case 2: // Suvaline tegevus function2(); break; // Kõik muud juhtumid default: // Suvaline tegevus functionX(); // break lauset pole vaja, // kuna võrdlemine lõpeb nagunii } ===== Tsüklid ===== Tsüklitega saab programmilõiku täita mitmeid kordi. ==== while ==== //while// võtmesõnaga tähistatud programmilõiku täidetakse seni, kuni sulgudes olev avaldis on tõene int x = 0; // Tsükkel kestab seni, kuni x on väiksem kui 5 while (x < 5) { // x suurendamine ühe võrra x++; } ==== for ==== //for// võtmesõnaga tsükkel sarnaneb //while// tsüklile, kuid lisaks on sulgudes ära määratud enne tsüklit täidetav lause ja iga tsükli ajal täidetav lause Näide: int i, x = 0; // Algul määratakse i nulliks. Tsüklit täidetaks seni, kuni // i on vähem kui 5. Iga tsükli lõpus suurendatakse i ühe võrra for (i = 0; i < 5; i++) { // x suurendamine 2 võrra x += 2; } // Siinkohal tuleb x väärtuseks 10 ==== Tsüklis liikumine ==== //while// ja //for// tsüklitest saab erandkorras väljuda //break// võtmesõnaga. //continue// võtmesõnaga saab alustada järgmist tsüklit ilma järgnevat koodi täitmata int x = 0, y = 0; // Lõputu tsükkel, kuna 1 on loogiline tõesus while (1) { // Tsüklist väljutakse, kui x on saavutanud väärtuse 100 if (x >= 100) break; // x suurendamine, et tsükkel kunagi lõppeks ka x++; // Kui x on 10 või vähem, siis alustatakse järgmist tsüklit if (x <= 10) continue; // y suurendamine y++; } // Siinkohal on y väärtus 90 ===== Tekstitöötlus ===== Tekstitöötlusfunktsioone on mikrokontrolleri puhul vaja eelkõige teksti kuvamiseks LCD ekraanile ja selle saatmiseks/vastuvõtmiseks kommunikatsiooniliideste kaudu. ==== sprintf ==== sprintf funktsioon toimib sarnaselt C-keeles üldlevinud printf funktsiooniga. Erinevuseks on funktsiooni tulemuse väljastamine puhvrisse (märgimassiivi), mitte standardväljundisse (mis on mikrokontrolleritel enamasti jadaliides, PC peal konsooli aken). Funktsioonil on muutuv arv argumente, mida vormindatakse vastavalt formaadile. Näide 1: #include char buffer[20]; int a = 1, b = 2; int len = sprintf(buffer, "%d pluss %d on %d", a, b, a + b); // Tulemuseks on tekstistring "1 pluss 2 on 3" Näiteks kirjutab sprintf funktsioon märgimassiivi //buffer// teksti, mis on koostatud kolmest argumendist ja on vormindatud vastavalt formaadi tekstistringile. Formaadi tekstistringis tähistab marker (inglise keeles //specifier//) %d täisarvu tüüpi argumenti, mis tuleb tekstis asendada. Koostatud teksti lõppu lisatakse automaatselt null-bait, mis tähistab tekstistringi lõppu. Kasutaja peab kindlustama, et koostatava teksti ja null-baidi pikkus ei ületaks märgimassiivi pikkust. Sprintf funktsioon lihtsustab fraaside ja lausete koostamist erinevat tüüpi muutujatest. Funktsioon tagastab märgimassiivi salvestatud teksti pikkuse (pikkus ei sisalda null-baiti). Vea korral tagastatakse negatiivne arv. Näide 2: #include char x[20]; sprintf(x, "%s on %d aastat vana", "Juku", 10); // Sama tulemuse saaksime ka konstantselt defineerida: char x[] = "Juku on 10 aastat vana"; Teise näite formaadi tekstistringis asendatakse marker %s esimese teksti-tüüpi argumendiga ja %d teise täisarv-tüüpi argumendiga. Niipalju, kui on markereid, peab olema ka argumente ning nende tüübid peavad klappima. Samuti peavad argumendid esitatud samas järjekorras nagu markerid. Erinevat tüüpi argumentide jaoks on olemas vastavad formaadi markerid: ^ Marker ^ Kirjeldus ^ Näide ^ | %c | Tähemärk | 'a' | | %i või %d | Täisarv | 123 | | %f | Murdarv | 3.14f | | %s | Tekst | "näide" | | %X | Heksadetsimaalarv| 0x3F | Standardteegi päisefailides stdio.h, stdlib.h ja string.h on veel terve hulk erinevaid funktsioone erinevate teksti-operatsioonide teostamiseks. Näiteks on võimalik nendega teksti muundada arvudeks, tekste liita ja võrrelda, tekstist märke otsida jpm. ===== Juhuarvud ===== Juhuarvude genereerimine ei olegi AVR kontrolleril väga lihtne. Esmalt tuleb juhunumbrigeneraator seemendada arvuga, mille alusel genereeritakse suvaliste numbrite jada. Sama numbri järgi genereeritakse alati sama jada, seega suurema juhuslikkuse saavutamiseks on kasulik seemendus teha arvuga, mis ei ole alati sama - näiteks kasutada selleks kellaaega või lahti ühendatud ADC sisendist väärtust. Näide, juhuarvu genereerimiseks vahemikus 1 kuni 16 (kaasa arvatud). #include srand(100); // Suvaline seemendusarv int x = 1 + (rand() % 16);