C-keele stringide ja massiivide täielik juhend: põhitõed, funktsioonid ja mäluhaldus

1. Sissejuhatus

C-keelt kasutatakse endiselt laialdaselt süsteemiprogrammeerimise ja manustatud programmeerimise valdkonnas. Selles keeles on stringid ja massiivid olulised elemendid andmete haldamiseks. C-keelt õppides on vältimatu mõista selle keele ainulaadset omadust, mille järgi stringi käsitletakse kui “tähemärkide massiivi”.

Selles artiklis süveneme C-keele stringide ja massiivide põhikontseptsioonidesse, et lahendada algajatel ja kesktasemel programmeerijatel sageli tekkivad küsimused „stringide ja massiivide erinevuse ning seoste“ kohta.

Lisaks vaatleme reaalsete programmide näidete kaudu, kuidas deklareerida massiive ja stringe, millised on põhilised stringifunktsioonid ning millistele mäluhalduse aspektidele tuleb tähelepanu pöörata. Nii muutub stringide töötlemine C-keeles turvalisemaks ja tõhusamaks.

2. Massiivide põhitõed

Massiivide mõistmine C-keeles on aluseks stringide töötlemisele. Selles jaotises selgitame massiivi kontseptsiooni ja kasutamist.

Mis on massiiv?

Massiiv on struktuur, mis salvestab sama tüüpi andmed järjestikustesse mälukohtadesse. Näiteks int-tüüpi massiivi deklareerides saab korraga hallata mitut täisarvu. C-keeles deklareeritakse massiiv järgmiselt:

int numbers[5]; // Massiiv, mis talletab 5 täisarvu

See kood deklareerib täisarvumassiivi numbers ja eraldab mälu 5 täisarvu jaoks. Iga elemendi juurde pääseb, kasutades indeksit.

Massiivi deklareerimine ja initsialiseerimine

Lisaks deklareerimisele saab massiivi ka kohe initsialiseerida, st määrata algväärtused.

int numbers[5] = {1, 2, 3, 4, 5}; // Deklareerimine ja initsialiseerimine

Siin salvestatakse massiivi numbers täisarvud 1 kuni 5. Kui initsialiseerimist ei tehta, jäävad massiivi väärtused määramata (mälujääkandmed).

Mälu paigutus ja elementide kasutamine

C-keeles paiknevad massiivi elemendid mälus järjestikku. Näiteks int numbers[5] korral on numbers[0] kuni numbers[4] kõrvuti mälus.

Massiivi elementidele pääseb ligi indeksiga, mis algab nullist ja kehtib kuni massiivi suurus miinus üks.

printf("%d", numbers[0]); // Kuvab esimese elemendi

Massiivid võimaldavad hallata sama tüüpi andmeid ühe muutujaga ja neid tõhusalt töödelda.

3. Stringide põhitõed

C-keeles ei ole string lihtsalt tähemärkide jada, vaid seda käsitletakse erilise massiivina. Selles jaotises õpime tundma C-keele stringide ülesehitust ja töötlusmeetodeid.

Mis on string?

C-keeles esitatakse string tähemärgimassiivina, mille lõppu lisatakse nullmärk ('\0'). See nullmärk tähistab stringi lõppu ja on oluline stringide töötlemisel.

Näiteks saab stringi defineerida järgmiselt:

char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};

Siin talletatakse massiivi greeting viis tähemärki „Hello” ja seejärel nullmärk. C-keeles tuvastatakse stringi lõpp '\0' märgi abil.

Stringi deklareerimine ja initsialiseerimine

C-keeles saab stringi initsialiseerida otse tähemärgimassiivina. Tavaliselt kasutatakse selleks stringilitteraali:

char greeting[] = "Hello";

Selle kirjutamisviisi korral lisab kompilaator automaatselt lõppu nullmärgi. Seega on greeting-massiivi pikkus 6 (5 tähemärki + nullmärk). Kui nullmärgi olemasolu eirata, võib see põhjustada vale tulemuse.

Stringilitteraali ja tähemärgimassiivi erinevus

Stringilitteraal ja tähemärgimassiiv tunduvad sarnased, kuid C-keeles on need erinevad. Stringilitteraal deklareeritakse const char* tüübina ja seda ei saa muuta.

const char *greeting = "Hello"; // Stringilitteraal

Tähemärgimassiivi sisu on aga muudetav. Näiteks, kui see on deklareeritud char greeting[] = "Hello";, saab üksikuid tähemärke muuta.

greeting[0] = 'h'; // Muudab "Hello" -> "hello"

Stringilitteraali ja tähemärgimassiivi erinevuse mõistmine aitab vältida mäluvigade ja vigade teket.

4. Stringide ja massiivide seos

C-keeles on string tegelikult „tähemärkide massiiv“. Selles jaotises selgitame, mida see tähendab ja kuidas stringe deklareerida ning initsialiseerida.

String on tähemärgimassiiv

C-keeles hallatakse stringe char-tüüpi massiivide abil. Näiteks saab massiivi deklareerida ja sinna stringi salvestada järgmiselt:

char name[10] = "Alice";

Selle koodi puhul sisaldab name-massiiv sõna „Alice” ning ülejäänud elemendid täidetakse automaatselt nullmärkidega ('\0').

{'A', 'l', 'i', 'c', 'e', '\0', '\0', '\0', '\0', '\0'}

Stringi deklareerimise tähelepanekud

Stringi massiivina deklareerimisel tuleb määrata piisav suurus. Kui suurus on väiksem kui stringi pikkus + nullmärk, tekib viga või ootamatu käitumine.

char name[3] = "Alice"; // Viga - liiga väike massiiv

„Alice“ vajab 6 baiti (5 tähemärki + nullmärk), kuid massiivis on ainult 3 baiti.

Stringi muutmine ja kopeerimine

C-keeles ei saa kogu massiivile väärtust otse omistada. Stringi muutmiseks tuleb muuta üksikuid elemente:

name[0] = 'B'; // Alice -> Blice

Kogu stringi kopeerimiseks kasutatakse standardfunktsiooni strcpy:

strcpy(name, "Bob"); // Asendab sisu sõnaga "Bob"

5. Põhilised stringifunktsioonid

C-keeles on standardses teegis mugavad funktsioonid stringide tõhusaks töötlemiseks. Selles jaotises käsitleme põhilisi funktsioone, mis kopeerivad, ühendavad, mõõdavad pikkust ja võrdlevad stringe.

Stringi kopeerimise funktsioon strcpy

strcpy kopeerib ühe stringi teise. See säästab vaeva, et mitte kopeerida tähemärke ükshaaval käsitsi.

#include <string.h>

char source[] = "Hello";
char destination[10];

strcpy(destination, source); // Kopeerib source -> destination

Selles näites kopeeritakse source sisu destination-isse, mille tulemusena sisaldab see stringi “Hello”. Tuleb tagada, et destination oleks piisavalt suur, et vältida puhvrivigu.

Stringide ühendamine funktsiooniga strcat

strcat lisab teise stringi esimese lõppu.

#include <string.h>

char greeting[20] = "Hello";
char name[] = " World";

strcat(greeting, name); // greeting -> "Hello World"

Tuleb tagada, et esimene string mahutaks ühendamise järel kogu tulemuse.

Stringi pikkuse leidmine strlen

strlen tagastab stringi pikkuse, jättes nullmärgi arvestamata.

#include <string.h>

char greeting[] = "Hello";
int length = strlen(greeting); // length = 5

Märkus: saadud pikkus ei pruugi kattuda massiivi kogusuurusega.

Stringide võrdlemine strcmp

strcmp võrdleb kahte stringi ja tagastab 0, kui need on identsed, või positiivse/negatiivse väärtuse, kui need erinevad.

#include <string.h>

char str1[] = "Hello";
char str2[] = "Hello";
char str3[] = "World";

int result1 = strcmp(str1, str2); // 0 (samad)
int result2 = strcmp(str1, str3); // != 0 (erinevad)

6. Stringide massiiv (kahemõõtmeline massiiv)

Mitme stringi käsitlemiseks kasutatakse tihti kahemõõtmelist massiivi. Nii saab mitu stringi ühes massiivis hallata.

Kahemõõtmelise massiivi põhitõed

Kahemõõtmeline massiiv on massiiv massiivist, mida saab käsitleda nagu tabelit.

char names[3][10] = {
    "Alice",
    "Bob",
    "Carol"
};

Siin on 3 rida ja iga rida mahutab kuni 10 tähemärki.

Elementidele juurdepääs

char first_char = names[0][0]; // 'A' massiivist "Alice"

Kõigi stringide väljastamiseks:

for (int i = 0; i < 3; i++) {
    printf("%s\n", names[i]);
}

Initsialiseerimise ja suuruse tähelepanekud

Vaja on määrata piisavad mõõtmed. Kui string on pikem kui ridade veerupikkus, tekib viga.

char colors[3][10] = {"Red", "Green", "Blue"};

Näide kahemõõtmelise massiivi kasutamisest

#include <stdio.h>
#include <string.h>

int main() {
    char names[3][10] = {"Bob", "Alice", "Carol"};
    char temp[10];

    for (int i = 0; i < 2; i++) {
        for (int j = i + 1; j < 3; j++) {
            if (strcmp(names[i], names[j]) > 0) {
                strcpy(temp, names[i]);
                strcpy(names[i], names[j]);
                strcpy(names[j], temp);
            }
        }
    }

    for (int i = 0; i < 3; i++) {
        printf("%s\n", names[i]);
    }

    return 0;
}

7. Mäluhaldus ja ettevaatusabinõud

C-keeles on stringide ja massiividega töötamisel mäluhaldus äärmiselt oluline. Vigane mäluhaldus võib põhjustada puhvrivigu ja mälulekkeid.

Mis on puhvriviga?

Puhvriviga tekib, kui kirjutatakse andmeid väljapoole massiivi eraldatud mäluala. C-keeles seda automaatselt ei kontrollita.

char buffer[10];
strcpy(buffer, "This is a very long string"); // Puhvriviga

Turvaline kopeerimine: strncpy

char buffer[10];
strncpy(buffer, "This is a very long string", sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0'; // Lõputähis käsitsi

Dünaamiline mäluhaldus

#include <stdlib.h>
#include <string.h>

char *str = (char *)malloc(20 * sizeof(char));
strcpy(str, "Dynamic allocation");

free(str);

Mälulekke vältimine

char *name = (char *)malloc(50 * sizeof(char));
strcpy(name, "John Doe");
free(name);

8. Kokkuvõte

Selles artiklis õppisime tundma C-keele stringe ja massiive, standardseid stringifunktsioone, kahemõõtmeliste massiivide kasutamist ning mäluhalduse põhimõtteid.

Massiivide ja stringide põhitõed

String on C-keeles „tähemärkide massiiv“ ja selle lõpus on nullmärk ('\0').

Põhilised stringifunktsioonid

Olulised funktsioonid: strcpy, strcat, strlen, strcmp.

Kahemõõtmelised massiivid

Kasulikud mitme stringi haldamiseks ühe muutuja abil.

Mäluhaldus

Puhvrivigade ja mäluleke vältimine, strncpy ja dünaamilise mälu kasutamine.

Lõppsõna

C-keele stringide ja massiivide töötlemine võib tunduda alguses keeruline, kuid põhitõdesid ja mäluhaldust järgides saab seda teha turvaliselt ja tõhusalt.