Praktikumi materjal
- Sel nädalal tegeleme eelkõige teadmiste kinnistamise ja harjutamisega
- Tutvu algoritmide ülesannetega – selliseid näed ka kontrolltöös
- Sissejuhatus siluri kasutamisesse
- Allpool on toodud lühikesed teooriakillud
Teooriakild 1: Teksti edastamine funktsiooni
Sageli võib tekkida olukord, kus funktsioon ise oleks justkui kenasti korduvkasutatav, kuid kasutajale kuvatav tekst ei sobi konkreetse kasutusjuhuga. See on tüüpiline üldiste sisend- ja väljundfunktsioonide puhul.
Üks võimalus selle lahendamiseks on tekst varem välja trükkida funktsioonis, mis teist funktsiooni välja kutsub. Täpselt nii oleme teinud viimase kahe nädala jooksul: väljastasime teksti funktsioonis main() ja seejärel kutsusime välja teise funktsiooni. Üldiselt jääb see ka edaspidi soovituslikuks lähenemiseks. Sedasi on funktsioon lihtsam ja kergemini korduvkasutatav.
Alternatiivse lahendusena jälgime põhimõtet, kus erinevusi käsitletakse funktsiooni parameetrite abil – antud juhul on erinevuseks väljastatav tekst. Pea meeles: kui kopeerid ja kleebid koodi ainult väikese erinevuse pärast, proovi leida viis selle erinevuse lahendamiseks funktsiooni parameetrite abil. Seega anname erineva päiseteksti käsitlemiseks teksti funktsioonile parameetrina.
See töötab üsna hästi näiteks siis, kui tahame väärtuse (näiteks täisarvu) sisestamisel kuvada kasutajale juhise. Samuti võib see hästi sobida massiivi väljastamiseks nii, et selle ees kuvatakse kirjeldav tekst.
NB! Kaks olulist mõtet veel! Mõlemast neist räägime süviti semestri teises pooles.
- Tekst on massiiv, mis koosneb üksikutest tähemärkidest, mida saab vajadusel indekseerida (hilisem teema)
- Erinevalt arvumassiividest ei pea me tekstimassiivi väljastamiseks selle pikkust funktsioonile kaasa andma, sest kasutatakse nullmärgiga lõpetatud sõnesid (zero-terminated strings), kus viimase kuvatava märgi järel asub spetsiaalne nullmärk. Ka see on hilisem teema.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#include <stdio.h> #define DEFINED_STRING "Passing by using a macro" void PrintPassedText(char text[]); int main(void) { PrintPassedText("Passing by using a constant"); PrintPassedText(DEFINED_STRING); char inputString[] = "Passing by using a variable"; PrintPassedText(inputString); } void PrintPassedText(char text[]) { /* To print double quotes, they need to be escaped first! */ printf("Text passed to this function: \"%s\"\n", text); } |
Teooriakild 2: Massiivi vs massiivi liikme edastamine
Sellega tutvusime küll eelmises tunnis, kuid tuletame meelde. Kõigepealt mõned põhipunktid
- (Staatilise) massiivi deklaratsioonil on fikseeritud suurus, mis peab olema määratud juba deklareerimise ajal. Massiivi suurust ei saa programmi töö ajal muuta (nt suurendada)
- Massiiv edastatakse alati viitena originaal asukohale
- Massiivi sisu saab muuta alamfunktsioonides, muudatus kehtib kõikjal
- Funktsioonile edastatud massiivi pikkust ei saa funktsiooni sees automaatselt kindlaks teha, seega peame alati edastama ka massiivi pikkuse
- Funktsiooni sees lokaalselt deklareeritud massiivi ei saa funktsioonist tagastada
See tähendab, et praegu deklareerime massiivid peaaegu alati main() funktsioonis ja anname need teistele funktsioonidele parameetritena edasi.
Teiseks tuletame meelde, kuidas kantsulud töötavad
- Kui massiivi ees on andmetüüp, tähistab see massiivi deklareerimist ja massiivi suurus tuleb määrata (nt int array[10]; )
- Kui andmetüüp puudub, tähistab see massiivi konkreetse elemendi indekseerimist (nt array[3] )
- Ühemõõtmelise massiivi puhul võib suuruse ära jätta ainult siis, kui
- massiiv initsialiseeritakse samal ajal (nt int array[] = {1, 2, 3, 2 ,1}; )
- massiivi kasutatakse funktsiooni parameetrina (nt int Foo(int arr[], int n); )
See viib meid järgmise näiteni, kus võrdleme, kas funktsioonile antakse edasi terve massiivi või ainult üks massiivi element.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#include <stdio.h> #define ARR_LEN 5 void PrintNum(int num); void PrintIntArray(int arr[], int len); int main(void) { int nums[ARR_LEN] = {9, 24, -2, 3, 25}; /* Passing an entire array */ PrintIntArray(nums, ARR_LEN); /* Passing the second number in the array */ PrintNum(nums[1]); /* Passing the fourth number in the array */ PrintNum(nums[3]); } void PrintNum(int num) { printf("Passed number: %d\n\n", num); } void PrintIntArray(int arr[], int len) { printf("Numbers are: "); for (int i = 0; i < len; i++) { printf("%d ", arr[i]); } printf("\n\n"); } |
Teooriakild 3: (varajane) tagastamine void-tüüpi funktsioonist
Kuigi oleme öelnud, et void tüüpi funktsioonid ei tagasta väärtust, ei tähenda see, et nende täitmist ei saaks varem lõpetada. void tüüpi funktsioonist väljumiseks saab kasutada käsku returnilma ühegi väärtuseta.
Seda kasutatakse sageli sisendparameetrite kontrollimisel, et teha kindlaks, kas funktsioon saab oma tööd jätkata. Näiteks vaatame funktsiooni, mis väljastab massiivi. Kui midagi ei ole väljastada (nt massiiv on tühi), tuleks funktsiooni töö lõpetada enne tsükli käivitamist.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void PrintArray(int nums[], int n) { printf("Array: "); if (n <= 0) { printf("empty\n"); return; } for (int i = 0; i < n; i++) { printf("%d ", nums[i]); } printf("\n"); } |
Sel nädalal on sellest teadmisest kasu peamiselt lisaülesande lahendamisel. Tulevikus on seda teadmist vaja ka põhiülesannete lahendamisel.
Esitamisele kuuluvad ülesanded
Selles tunnis tuleb lahendada kaks ülesannet. Esimest ülesannet laiendab kaks lisaülesannet.
Ülesanne 1 [W06-1]: Uute massiivide koostamine
Antud ülesanne põhineb suuresti algoritmiülesandel. Vastav ülesanne on püstitatud siin: https://blue.pri.ee/ttu/programmeerimine-i/algoritmide-ulesanded/#Algoritm_3_Negatiivsete_ja_positiivsete_arvude_umberjarjestus
Selle algoritmi lahendus on sulle ette antud. Ülesandele on lisatud algoritmis puuduvaid täiendusi.
Algoritm
Pane tähele, et tulemusmassiivi loomine toimub kahe järjestikuse iteratsiooni vältel, et säilitada algset esinemisjärjekorda. Esitatud joonistel on täitmata lahtrid tähistatud küsimärgiga. Selle eesmärk on ühtlasi meenutada, et algväärtustamata massiivi liikmed on juhuslikud väärtused. Pärast esimest iteratsiooni peaks seis olema järgnev:
Oluline: Pööra tähelepanu mõlema massiivi indekseerimisele!!
Teise iteratsiooni lõpuks täidetakse kogu vastusmassiiv väärtustega.
Nõuded
- Programm peab põhinema etteantud algoritmil
- Kasutajalt loetakse 6 arvu ja salvestatakse need massiivi
- Programmi vältel koostatakse teine samapikk massiiv põhimõttel negatiivsed arvud ettepoole, positiivsed nende järel. Arvu 0 loetakse positiivseks arvuks
- Programmi vältel koostatakse ka kolmas massiiv, kuhu kuuluvad vaid nullist suuremad arvud. Massiivi pikkus võib osutuda lühemaks algsest massiivist
- Pärast massiivide koostamist väljastatakse järjest kõik 3 massiivi
- Programm peab endas sisaldama kokku nelja funktsiooni – arvude lugemiseks kasutajalt, arvude ekraanile väljastamiseks, nullist suurematest arvudest koosneva massiivi koostamiseks ning etteantud algoritmi alusel järjestatud massiivi koostamiseks.
- Kõik enda loodud funktsioonid peavad olema välja kutsutud main() funktsioonist.
Soovitusi ja vihjeid
Alusta programmi koostamist sisestusest ja massiivi ekraanile kuvamisest. Seda oled juba eelmine kord teinud. Loo vajalik massiiv ja korduvkasuta varasemalt tehtud funktsioone. Kui kopeerid funktsioone eelneva nädala ülesandest, ära unusta ka nendele eelnevat kommentaari kopeerida!
Nüüd lähme ümberjärjestamise juurde. Loo uus massiiv (lisaks olemasolevale), kuhu salvestad ümberjärjestatud arvujada. Uue massiivi pikkus on sama nagu esmase massiivi pikkus. Meenuta, et funktsioonist massiivi tagastada ei ole võimalik, seega massiiv tuleb deklareerida main() funktsioonis ja edastada funktsioonile. Ümberjärjestamise ülesande lahendamiseks jälgi etteantud algoritmi, mis oli esitatud veidi ülevalpool.
Seejärel kutsu uuesti välja massiivi väljastamise funktsioon, sel korral ümberjärjestatud massiivi väljastamiseks. St kood võiks funktsiooni väljakutsete mõttes näha välja umbes sarnane (nimed ei ole olulised):
|
1 2 3 4 5 6 7 8 |
ReadIntArray(inputNumbers, NUM_CNT); CreateRearrangedArray(inputNumbers, rearrangedNumbers, NUM_CNT); printf("Printing the original array: "); PrintIntArray(inputNumbers, NUM_CNT); printf("Printing the modified array: "); PrintIntArray(rearrangedNumbers, NUM_CNT); |
Siinkohal kui soovid oma main() funktsiooni puhtamaks saada, siis tasuks massiivi väljastuse ette minev tekst parameetriga kaasa anda. Selleks vaata esimest teooriakildu!
Nüüd liigu viimase funktsiooni juurde, mis peaks ainult nullist suurematest arvudest massiivi koostama. Struktuurilt on koostamine üsna sarnane eelnevalt tehtud ümberjärjestusele, kuid mõned erinevused siiski eksisteerivad. Kui ümberjärjestatud massiiv oli liikmete arvult samapikk algse massiiviga, siis nullist suuremate arvude massiiv tuleb suure tõenäosusega lühem. Mõtle, kuidas saada koostatud massiivi pikkus alamfunktsioonist välja, main() funktsiooni! Kui pikkus käes, kutsu jälle main() funktsioonis välja massiivi väljatrükk, et ka kolmas massiiv ekraanile väljastada.
Testimine
Esimese testina proovime tavapärast juhtu, kui sisestuses on nii positiivseid kui negatiivseid arve.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Enter number 1 / 6: 6 Enter number 2 / 6: -9 Enter number 3 / 6: 3 Enter number 4 / 6: -12 Enter number 5 / 6: 0 Enter number 6 / 6: 4 The original array 6 -9 3 -12 0 4 The rearranged array -9 -12 6 3 0 4 The greater than 0 array 6 3 4 |
Arvesta, et antud olukorras võivad ka nullist suuremad arvud puududa. Väljastus peaks jääma jätkuvalt arusaadavaks.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Enter number 1 / 6: -1 Enter number 2 / 6: -4 Enter number 3 / 6: -66 Enter number 4 / 6: -97 Enter number 5 / 6: -3 Enter number 6 / 6: -5 The original array -1 -4 -66 -97 -3 -5 The rearranged array -1 -4 -66 -97 -3 -5 The greater than 0 array Empty array |
Ülesanne 2 [W06-2]: kahe arvu võrdlus
Teises ülesandes praktiseerime massiivi indekseerimist, pöördudes vaid soovitud massiivi indeksite poole ning teostame lihtsaid operatsioone leitud väärtustega.
Nõuded
- Loe klaviatuurilt 5 täisarvu
- Küsi kasutajalt kahe soovitud arvu positsioon (järjekorranumber)
- Kontrolli, et need oleksid sobilikus vahemikus enne jätkamist! Vajadusel küsi uuesti
- Numeratsioon peab olema sama nagu sisendit küsides
- Soovitus: kontrollimaks kas leidsid õiged arvud, trüki need välja
- Leia sisestatud positsioonide põhjal nendele vastavad arvud algsest massiivist. Kasutades neid arve tee järgnevat:
- Võrdle neid kahte arvu ning kuva nende omavaheline suhe (väiksem, suurem, võrdne)
- Koosta jagamistehe ning kuva selle vastus. Tehe tuleb koostada sedasi, et suurem arv on jagatav ning väiksem arv on jagaja.
- Vastus anna ühe komakohaga
- Ülesandes on vaja luua neli funktsiooni. Funktsioonid on sulle vähemal või rohkemal määral kirjeldatud järgnevas peatükis.
Loodavad funktsioonid
1. Massiivi lugemine. Selleks korduvkasuta koodi varasematest programmidest. Kopeeri funktsioon koos sellele eelneva kommentaariga.
2. Indeksi lugemine. Selleks tuleks luua uus funktsioon. Loome korduvkasutatava funktsiooni, mida saad tulevikus kasutada igal korral, kui on vaja kasutajalt lugeda täisarv kindlas vahemikus (nt 0 … 5, -10 … 10 jne). Kui tegid eelmine nädal lisaülesande ära, on sul see funktsioon suure tõenäosusega juba olemas.
Pakun välja kaks funktsiooni varianti, vali kumb meelepärasem tundub ja realiseeri see.
Esimene versioon eeldab, et kasutajale esitatav küsimus küsitakse väljaspool seda funktsiooni.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/** * Description: Asks the user for an integer in between the given limits. * Repeats until requirements are met and returns the number. * * Parameters: min - lower bound for the user input (inclusive) * max - upper bound for the user input (inclusive) * * Return: number within the specified limits */ int GetIntInRange(int min, int max) { } |
Teine versioon eeldab, et küsimus saadetakse funktsiooni kaasa ja väljastatakse selle sees. Vaata esimest teooriakildu.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/** * Description: Asks the user for an integer in between the given limits. * Repeats until requirements are met and returns the number. * * Parameters: min - lower bound for the user input (inclusive) * max - upper bound for the user input (inclusive) * prompt - prompt for user input, printed before entry * * Return: number within the specified limits */ int GetIntInRange(int min, int max, char prompt[]) { } |
3. Arvude võrdlemine. Siia funktsiooni tuleks edastada 2 arvu, mida võrreldakse omavahel, ning väljastatakse kumb neist oli suurem (või olid nad võrdsed). Väljastus toimub funktsiooni sees. Väljastus on kujul “a < b”, “a > b” või “a = b”
NB! Arvude edastamiseks siia funktsiooni vaata teist infokildu!
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/** * Description: Compares the given values (val1 and val2) and prints * results of the comparison (which is greater; or equal) * * Parameters: val1 - first value being compared * val2 - second value being compared * * Return: - */ void CompareValues(int val1, int val2) { } |
4. Jagatise teostamine. Siia funktsiooni edastatakse samamoodi kaks arvu. Taaskord toimub arvude võrdlemine nagu ka eelmine kord, kuid sel korral määrab võrdluse tulemus jagamistehte operandide järjekorra.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/** * Description: Compares the given values (val1 and val2). Performs the * division by dividing greater number with the smaller number. * Both division operation and result are printed. * * Parameters: val1 - first value * val2 - second value * * Return: - */ void DivideValues(int val1, int val2) { } |
Testimine
NB! Näites on nähtavale jäetud silumiseks kasutatavad read (algusega DEBUG), mida soovitan ka sinul alguses teha. Need saad hiljem kiirelt välja kommenteerida.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Enter number 1 / 5: 5 Enter number 2 / 5: -5 Enter number 3 / 5: 24 Enter number 4 / 5: 9 Enter number 5 / 5: 1 Enter first index: -5 Error! Index must be within 1 and 5! Enter first index: -7 Error! Index must be within 1 and 5! Enter first index: 0 Error! Index must be within 1 and 5! Enter first index: 4 DEBUG: Corresponding value: 9 Enter second index: 3 DEBUG: Corresponding value: 24 9 < 24 24 / 9 = 2.7 |
Testid
Järgnevalt pakun välja osalise loetelu testidest, mida proovida
- Proovi koodi sisenditega, mis peaksid andma tulemuse ilma ühegi veata
- Proovi indekseid, mis on massiivi piiridest väljas. Katseta seda nii esimese kui teise sisendiga.
- Proovi massiivi piiridest välja jäävaid indekseid korduvalt, et programm ei läheks edasi enne, kui sobilik sisend on saadud.
- Proovi piirjuhtusid
- Kui loendasid 0 – 4, proovi -1, 0, 4, 5
- Kui loendasid 1 – 5, proovi 0, 1, 5, 6
- Proovi sisendeid, mis testiksid läbi kõik 3 võrdlusjuhtu (<, >, ==)
- Proovi nulliga jagamist. NB! Nulliga jagamine on defineerimata operatsioon. Nulliga jagamine võib jooksutada programmi või isegi kogu arvuti kokku (mikrokontrollerite puhul on tavapärane, et terve seade taaskäivitab end nulliga jagamise tulemusel). Nulliga jagamistehet ei tohi sooritada!
Lisaülesanne 1 [W06-3]: Ühekordsed arvud
Ülesanne on tunnitöö #1 laiendus.
Nõuded
- Ülesanne peab olema tehtud laiendusena baasülesandele. Baasülesande funktsionaalsus peab säilima täies ulatuses.
- Loo uus massiiv olemasolevate arvude põhjal. Uus massiiv tuleb luua põhimõttel, et kõik numbrid tohivad esineda vaid ühekordselt.
Näide lahendusest
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
Enter number 1 / 6: -5 Enter number 2 / 6: 3 Enter number 3 / 6: 9 Enter number 4 / 6: 3 Enter number 5 / 6: -5 Enter number 6 / 6: 5 The original array: -5 3 9 3 -5 5 The rearranged array: -5 -5 3 9 3 5 The greater than 0 array: 3 9 3 5 The nonrecurrent array: -5 3 9 5 |
Lisaülesanne 2 [W06-4]: Vastandarvude paarid
Ülesanne on tunnitöö #1 laiendus.
Nõuded
- Ülesanne peab olema tehtud laiendusena baasülesandele. Baasülesande funktsionaalsus peab säilima täies ulatuses.
- Leia ja kuva massiivis olevate arvude ja nende vastandarvude paarid.
- Samu arve tohib paarina kuvada ühekordselt, olenemata arvude järjestusest või korduste arvust.
Testimine
Testimisel veendu, et paarid ei korduks.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Enter number 1 / 6: 1 Enter number 2 / 6: 5 Enter number 3 / 6: -1 Enter number 4 / 6: -5 Enter number 5 / 6: 5 Enter number 6 / 6: 224 The original array 1 5 -1 -5 5 224 The rearranged array -1 -5 1 5 5 224 The greater than 0 array 1 5 5 224 The nonrecurrent array 1 5 -1 -5 224 Pairs: (-1, 1) (-5, 5) |
Pärast tundi peaksid
- Oskama sõnesid (teksti) funktsioonidesse edastada
- Oskama void-tüüpi funktsioonidest väljuda varakult return lausega
- Oskama funktsiooni edastada nii terviklikku massiivi kui ka üksikut massiivi liiget
- Oskama koostada ning kasutada massiive, mille kõik liikmed (positsioonid) ei ole kasutusel
- Tundma end mugavamalt massiivi käsitledes ka juhtudel, kus läbikäik pole esimesest elemendist viimaseni
- Oskama kopeerida ühest massiivist arve teise massiivi, kusjuures kasutades kahte erinevat indeksit ühes samas tsüklis erinevate massiivide indekseerimiseks.
- Tundma ennast juba natuke kodusemalt funktsioone kasutades


