- 0.1 1. Sissejuhatus
- 0.2 2. Massiivide põhimõisted
- 0.3 3. Massiivide deklareerimine ja initsialiseerimine
- 0.4 4. Massiivi pikkuse (elementide arvu) määramine
- 0.5 5. Sõnemassiivi pikkuse määramine
- 0.6 6. Muutuvpikkusega massiivide (VLA) kasutamine
- 0.7 7. Massiivi pikkusega seotud ettevaatusabinõud
- 0.8 8. Kokkuvõte
- 1 Korduma kippuvad küsimused (KKK)
- 1.1 K1: Miks ei tööta sizeof funktsiooni sees massiivi pikkuse leidmisel õigesti?
- 1.2 K2: Kumb on parem sõne pikkuse leidmiseks, sizeof või strlen?
- 1.3 K3: Kumb valida, muutuvpikkusega massiiv (VLA) või malloc-iga dünaamiline massiiv?
- 1.4 K4: Mis juhtub, kui unustan malloc-iga eraldatud mälu vabastada?
- 1.5 K5: Kuidas vältida puhvri ületäitumist?
- 1.6 Kokkuvõte
1. Sissejuhatus
Programmeerimiskeel C on oma lihtsuse ja suure jõudluse tõttu laialdaselt kasutusel süsteemiarenduses ja manussüsteemides. Nende hulgas on massiivid oluline andmestruktuur, mis võimaldab andmeid korraga hallata ning mida kasutatakse sageli paljudes programmides.
Selles artiklis selgitame üksikasjalikult, kuidas C-keeles massiivi pikkust (elementide arvu) määrata. Keskendume eelkõige algajatele raskusi valmistavatele punktidele ning anname samm-sammulised juhised alates põhitõdedest kuni rakenduseni, et omandada oskus massiivi pikkust täpselt hinnata.
2. Massiivide põhimõisted
Mis on massiiv?
Massiiv on andmestruktuur, mis võimaldab hallata sama andmetüüpi väärtusi üheskoos. See on kasulik, kui on vaja töödelda korraga mitut andmeühikut, ning eraldab sama tüüpi andmetele järjestikuse mäluruumi.
Massiivide kasutusviisid
- Andmete hulgitöötlus – sobib juhtudel, kui on vaja hallata sama tüüpi andmeid, näiteks õpilaste hindeid või andureidelt saadud mõõtmisi.
- Korduvate protsesside teostamine – tänu järjestikusele juurdepääsule tsüklites on võimalik sama töötlust efektiivselt korrata.
- Mälu haldamine – mäluruumi järjestikune kasutamine võimaldab kiiremat ja tõhusamat juurdepääsu.
Massiivi tööpõhimõte
Massiivi elementidele pääseb ligi indeksite abil. C-keeles algab indeks 0-st ning viimane element on juurdepääsetav väärtusega massiivi suurus - 1
.
Näide:
int numbers[5] = {10, 20, 30, 40, 50};
printf("%d\n", numbers[0]); // kuvab 10
printf("%d\n", numbers[4]); // kuvab 50
Selles näites sisaldab massiiv numbers
viis täisarvu ning igale elemendile saab ligi vastava indeksi abil.
3. Massiivide deklareerimine ja initsialiseerimine
Kuidas massiivi deklareerida
C-keeles deklareeritakse massiiv järgmiselt:
tüüp massiivi_nimi[suurus];
Konkreetne näide:
int scores[10]; // täisarvutüüpi massiiv 10 elemendiga
Selles näites luuakse täisarvudest (int
) koosnev massiiv scores
ning eraldatakse mälu 10 elemendi jaoks.
Massiivide initsialiseerimine
Massiivi saab initsialiseerida kohe deklareerimise hetkel.
- Näide selgest initsialiseerimisest
int values[5] = {1, 2, 3, 4, 5};
- Osaline initsialiseerimine
int data[5] = {10, 20}; // ülejäänud elemendid saavad väärtuseks 0
- Suuruse väljajätmine initsialiseerimisel
int numbers[] = {10, 20, 30}; // elementide arv arvutatakse automaatselt (3)
Mitteinitsialiseeritud massiivid
Kui massiivi ei initsialiseerita, võivad selles olla määramata väärtused (nn “prügiandmed”). Seetõttu on soovitatav vajadusel massiiv alati initsialiseerida.
4. Massiivi pikkuse (elementide arvu) määramine
C-keeles on massiivi täpne pikkus oluline, eriti tsüklite ja andmete haldamisel. Selles jaotises selgitame täpselt, kuidas pikkust määrata ja millele tähelepanu pöörata.
4.1 Pikkuse määramine sizeof
operaatori abil
Levinum viis massiivi pikkuse määramiseks on kasutada sizeof
operaatorit, mis tagastab muutuja või tüübi suuruse baitides.
Põhinäide:
#include <stdio.h>
int main() {
int array[5] = {10, 20, 30, 40, 50};
int length = sizeof(array) / sizeof(array[0]); // arvutab elementide arvu
printf("Massiivi pikkus: %d\n", length); // väljund: 5
return 0;
}
Olulised punktid:
sizeof(array)
– kogu massiivi suurus baitides.sizeof(array[0])
– ühe elemendi suurus baitides.- Kogu suurus jagatakse ühe elemendi suurusega, et saada elementide arv.
4.2 Massiivi edastamine funktsioonile
Kui massiiv edastatakse funktsioonile, teisendatakse see pointeriks. Sel juhul tagastab sizeof
pointeri suuruse (tavaliselt 4 või 8 baiti) ega anna õiget pikkust.
Probleemne näide:
#include <stdio.h>
void printArrayLength(int arr[]) {
printf("Suurus: %ld\n", sizeof(arr) / sizeof(arr[0])); // ei tööta õigesti
}
int main() {
int array[5] = {1, 2, 3, 4, 5};
printArrayLength(array);
return 0;
}
Lahendus: edastada pikkus eraldi argumendina.
#include <stdio.h>
void printArrayLength(int arr[], int length) {
printf("Massiivi pikkus: %d\n", length);
}
int main() {
int array[5] = {1, 2, 3, 4, 5};
int length = sizeof(array) / sizeof(array[0]);
printArrayLength(array, length);
return 0;
}
5. Sõnemassiivi pikkuse määramine
C-keeles käsitletakse sõnesid samuti massiividena. Kuid erinevalt tavalistest täisarvu massiividest on sõned char
-tüüpi massiivid, mille lõppu lisatakse erimärk '\0'
(nullmärk). Selles jaotises selgitame, kuidas määrata sõnemassiivi pikkust ja millele tähelepanu pöörata.
5.1 Sõne ja massiivi seos
Sõne on char
-tüüpi märkide kogum. Näide:
char str[] = "Hello";
See kood salvestab massiivi str
mällu järgmiselt:
H | e | l | l | o | ‘\0’ |
---|
Olulised punktid:
'\0'
tähistab sõne lõppu.- Massiivi suurus on 6 baiti, kuna nullmärk on kaasatud.
5.2 Pikkuse määramine funktsiooniga strlen
Sõne tegeliku pikkuse (märkide arvu) määramiseks kasutatakse standardteegi funktsiooni strlen
.
Näide:
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "Hello";
printf("Sõne pikkus: %ld\n", strlen(str)); // väljund: 5
return 0;
}
Märkus:
strlen
loendab ainult märgid, jättes nullmärgi arvestamata.
5.3 Erinevus sizeof
ja strlen
vahel
Pikkust saab määrata ka sizeof
-iga, kuid tulemused erinevad.
Näide:
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "Hello";
printf("sizeof: %ld\n", sizeof(str)); // väljund: 6 (sh '\0')
printf("strlen: %ld\n", strlen(str)); // väljund: 5
return 0;
}
Peamised erinevused:
sizeof
tagastab kogu massiivi suuruse baitides (sh nullmärk).strlen
tagastab ainult märkide arvu (nullmärki arvestamata).
6. Muutuvpikkusega massiivide (VLA) kasutamine
C99 standardist alates on võimalik kasutada muutuvpikkusega massiive (VLA – Variable Length Array). See võimaldab massiivi suuruse määrata programmi käivitamise ajal, mitte ainult kompileerimise ajal. Siin selgitame VLA omadusi, kasutust ja ettevaatusabinõusid.
6.1 Mis on muutuvpikkusega massiiv?
Tavaline massiiv on staatiline – selle suurus määratakse kompileerimise ajal. VLA on dünaamiline massiiv, mille suurus saab sõltuda kasutaja sisendist või arvutustulemusest.
Staatiline massiiv:
int arr[10]; // suurus määratud kompileerimise ajal
VLA näide:
int size;
scanf("%d", &size); // suurus määratakse käitusajal
int arr[size]; // massiiv luuakse jooksvalt
6.2 VLA põhiline kasutamine
Näide:
#include <stdio.h>
int main() {
int n;
printf("Sisesta massiivi suurus: ");
scanf("%d", &n);
int arr[n]; // VLA deklaratsioon
for (int i = 0; i < n; i++) {
arr[i] = i + 1;
}
printf("Massiivi elemendid: ");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
Väljundinäide:
Sisesta massiivi suurus: 5
Massiivi elemendid: 1 2 3 4 5
- VLA võimaldab määrata suuruse alles käitusajal.
- See teeb andmete haldamise paindlikumaks.
7. Massiivi pikkusega seotud ettevaatusabinõud
C-keeles on massiivide puhul suuruse (pikkuse) korrektne haldamine äärmiselt oluline. Kui pikkus määratakse valesti, võib see põhjustada programmi vigast käitumist või tõsiseid turvariske. Selles jaotises käsitleme massiivi pikkusega seotud ohte ja ohutuma koodi kirjutamise soovitusi.
7.1 Massiivi piiridest väljapoole pääsemise vältimine
Kuna massiiv on fikseeritud suurusega, põhjustab sellele ettenähtud piiridest väljapoole juurdepääs ettearvamatuid tulemusi või programmi krahhi.
Vale näide:
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
for (int i = 0; i <= 5; i++) { // tingimus on vale
printf("%d\n", arr[i]); // väljaspool piire
}
return 0;
}
Lahendus:
- Korrektne tsükli tingimus
for (int i = 0; i < 5; i++) { // õige tingimus
printf("%d\n", arr[i]);
}
- Pikkuse dünaamiline arvutamine
int length = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < length; i++) {
printf("%d\n", arr[i]);
}
7.2 Puhvri ületäitumise (Buffer Overflow) oht
Üks levinumaid probleeme massiividega on puhvri ületäitumine, mis tekib siis, kui kirjutatakse andmeid massiivi piiridest väljapoole ja kirjutatakse üle teisi mälupiirkondi.
Ohtlik näide:
#include <stdio.h>
#include <string.h>
int main() {
char buffer[10]; // 10 baiti
strcpy(buffer, "This string is too long!"); // ületab massiivi piiri
printf("%s\n", buffer);
return 0;
}
Lahendus:
- Turvalisemad funktsioonid
strncpy(buffer, "This string is too long!", sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0'; // tagab nullmärgi lõpus
8. Kokkuvõte
Selles artiklis käsitlesime C-keele massiivide pikkusega seotud teemasid alates põhitõdedest kuni praktiliste näideteni. Massiiv on võimas andmestruktuur, kuid vale kasutamine võib põhjustada programmeerimisvigu ja turvaauke. Järgime artikli peamisi punkte:
8.1 Artikli kokkuvõte
- Massiivi põhimõisted ja deklareerimine/initsialiseerimine
- Massiiv salvestab sama tüüpi elemente järjestikku ja selle suurus määratakse deklareerimisel.
- Initsialiseerimisel võib suuruse jätta märkimata, kui väärtused on teada.
- Pikkuse määramine
- Staatiliste massiivide puhul kasutada
sizeof
-i. - Funktsioonile edastamisel lisada pikkus eraldi argumendina.
- Sõnemassiivid
- Kasutada
strlen
-i tegeliku pikkuse jaoks ja mõista erinevustsizeof
-iga.
- Muutuvpikkusega massiivid (VLA)
- Võimaldavad suurust määrata käitusajal, kuid vajavad tähelepanu mälukasutuse ja ühilduvuse osas.
- Turvaline programmeerimine
- Vältida piiridest väljumist ja puhvriohtusid, kasutada turvalisi funktsioone.
8.2 Edasised sammud
- Koodi praktiline testimine
- Kompileeri ja käivita artiklis toodud näited.
- Edasijõudnute harjutused
- Katseta mitmemõõtmelisi massiive ja pointeritega seotud lahendusi.
- Turvalise koodi harjumus
- Planeeri koodi kirjutades alati ka turvalisus ja vigade käsitlemine.
8.3 Lõppsõna
Massiiv on C-keele aluspõhimõiste, mis on samaaegselt nii lihtne kui ka mitmekülgne. Kui järgid artiklis toodud juhiseid, saad kirjutada turvalist ja efektiivset koodi.
Korduma kippuvad küsimused (KKK)
K1: Miks ei tööta sizeof
funktsiooni sees massiivi pikkuse leidmisel õigesti?
V:sizeof
tagastab massiivi suuruse ainult siis, kui massiiv on deklareeritud samas ulatuses (scope). Kui massiiv edastatakse funktsioonile, teisendatakse see viidaks (pointer). Pointer sisaldab ainult massiivi algusaadressi, mistõttu sizeof
tagastab pointeri suuruse (tavaliselt 4 või 8 baiti), mitte tegeliku elementide arvu.
Lahendus:
Edasta massiivi pikkus eraldi argumendina funktsioonile.
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d\n", arr[i]);
}
}
int main() {
int array[5] = {1, 2, 3, 4, 5};
printArray(array, sizeof(array) / sizeof(array[0])); // pikkus edastatakse
return 0;
}
K2: Kumb on parem sõne pikkuse leidmiseks, sizeof
või strlen
?
V:
Sõltub eesmärgist.
sizeof
: tagastab kogu massiivi suuruse baitides (sh'\0'
). Sobib puhvri suuruse kontrollimiseks.strlen
: tagastab tegeliku märgijada pikkuse (ilma'\0'
-ta). Sobib sõne pikkuse määramiseks.
Näide:
char str[] = "Hello";
printf("%ld\n", sizeof(str)); // väljund: 6 ('\0' kaasas)
printf("%ld\n", strlen(str)); // väljund: 5
K3: Kumb valida, muutuvpikkusega massiiv (VLA) või malloc
-iga dünaamiline massiiv?
V:
Olenevalt olukorrast.
- VLA eelised:
- Lihtne kasutada, suurus määratakse käitusajal.
- Kasutab virnamälu (stack), mis võib olla piiratud suurusega.
- Mõnes keskkonnas (nt C11 ja uuemad) võib VLA tugi puududa.
malloc
eelised:- Kasutab hunnikumälu (heap), mis sobib suurte andmehulkade jaoks.
- Nõuab mäluhaldusega (mälu vabastamine
free()
-ga) tegelemist, kuid pakub suuremat paindlikkust ja ühilduvust.
K4: Mis juhtub, kui unustan malloc
-iga eraldatud mälu vabastada?
V:
See põhjustab mäluleke (memory leak). Mälulekke kuhjumine võib aeglustada süsteemi või põhjustada programmi krahhi.
Lahendus:
Pärast kasutamist vabasta alati mälu free()
-ga.
int *arr = malloc(10 * sizeof(int));
if (arr == NULL) {
printf("Mälu eraldamine ebaõnnestus\n");
return 1;
}
// ... kasuta massiivi ...
free(arr); // vabastab mälu
K5: Kuidas vältida puhvri ületäitumist?
V:
Puhvri ületäitumine tekib siis, kui kirjutatakse massiivi piiridest väljapoole. See võib põhjustada turvariske ja programmi krahhi. Ennetamiseks:
- Kontrolli alati massiivi suurust sisendi töötlemisel.
- Kasuta turvalisi funktsioone – näiteks
strncpy
strcpy
asemel,snprintf
sprintf
asemel. - Jäta puhvris alati ruumi nullmärgile.
- Testi koodi äärmuslike sisenditega.
Kokkuvõte
See KKK aitas vastata levinud küsimustele massiivide pikkuse leidmise ja ohutu kasutamise kohta. Koos artikli põhiosaga moodustab see tervikliku juhendi turvalise ja tõhusa C-keele massiivide käsitlemise kohta.