Toto je starší verze dokumentu!


Cvičení 5: Řetězce, část 2.

Toto cvičenie má za úlohu precvičiť prácu s poliami a viacrozmernými poliami. S jednorozmernými poliami ste sa stretli už minulé cvičenie. Reťazec je jednorozmerné pole znakov zakončené koncovou nulou (nulovým bytom). Pomocou koncovej nuly sme schopný zistiť dĺžku reťazca. Pozor, toto pre polia vo všeobecnosti neplatí. V jazyku C existuje operátor sizeof, ktorý vám vráti dĺžku staticky alokovaného poľa krát veľkosť dátového typu. Vo všeobecnosti, však nie je možné veľkosť poľa zistiť a jazyk C v sebe nemá zakomponované mechanizmy na kontrolu pretečenia (zápis/čítanie mimo poľa). Je to z dôvodu rýchlosti a predpokladu, že programátor si to kontroluje sám. Staticky alokováné viacrozmerné polia sú v pamäti reprezentované ako súvislý, za sebou idúci blok, preto k nim je možné pristupovať aj ako k jednorozmerným poliam.

Kostra projektu: Download
int matrix[50][30]; // deklarácia matice o veľkosti 50 riadkov a 30 stĺpcov
int array[50*30]; // deklarácia poľa o veľkosti 1500 

Obe tieto volania spôsobia alokovanie 1500 * sizeof(int) súvislého pamäťového bloku.

Platí vzorec:

// L - počet stĺpcov
matrix[i][j] == *(matrix + i*L + j); // adresa prvku matrix[0][0], plus index riadku, krát počet stĺpcov, plus index prvku v stĺpci.
Spôsoby predávania viacrozmerných statických polí funkciám:
  • Ako ukazateľ na pamäť, kedže statické viacrozmerné polia sú v pamäti uložené ako jeden blok idúci za sebou, je možné predať funkcii ukazateľ na začiatok poľa.
  • Ako viacrozmerné pole s pevne danou dĺžkou.
     void funkcia(int matica[25][50]); 

    Nevýhoda takéhoto predania je, že funkcia akceptuje len matice veľkosti 25 * 50;

Úkol 1: Práca s maticami

V tejto úlohe si precvičíte prácu s 2 rozmerným poľom. Vašou úlohou bude implementovať niekoľko jednoduchých funkcii, na pochopenie, akým spôsobom k maticiam pristupovať.

Zadání úkolu 1

Naimplementujte funkciu matrixRowInfo , ktorá prejde všetky hodnoty uložené v matici celých čísel (int) a do 2. matice zapíše, pre každý riadok minimum a maximum.

Prvý argument je počet riadkov matice, druhý je počet stĺpcov, tretí je samotná matica, štvrtý je výsledná matica minmaxov. Minimum je prvý prvok a maximum druhý.

void matrixRowInfo(int mrows, int mcols, int matrix[mrows][mcols], int result[mrows][2]);

Naimplementujte funkciu matrixStringInfo , ktorá prejde pole reťazcov, čo je v podstate matica znakov a spočíta pre každý reťazec jeho dĺžku, tú uloží do poľa dĺžok a vráti ukazaťeľ na najdlhšie reťazec. Pre zjednodušenie budete rátať s tým, že najdlhší reťazec nebude mať viac ako 255 znakov.

char *matrixStringInfo(const int mrows, char matrix[mrows][256], int result[mrows]);

Úkol 2: Split

V druhej úlohe si vyskúšate implementovať funkciu, ktorá vám rozdelí reťazec na podreťazce a tie uloží do poľa. Delenie prebehne na základe deliaceho znaku.

Zadání úkolu 2

Naimplementujte funkciu stringSplit, ktorá rozdelí reťazec na základe deliaceho znaku na podreťazce. Naša funkcia bude obmedzená tým, že maximálna veľkosť podreťazca bude 255 znakov a maximálny počet podreťazcov bude 50.

Prvý argument funkcie je pôvodný reťazec, druhý argument je pole reťazcov, tretí argument udáva koľko podreťazcov sa v poli nachádza a štvrtý argument udáva znak, podľa ktorého dôjde k rozdeleniu pôvodného reťazca. Deliaci znak sa vo výslednej matici nachádzať nebude.

void stringSplit(const char *string, char result[50][256], int *size, char delim);
Všimnite si, že maximálna dĺžka reťazca je 255 znakov a v matici deklarujeme 256. Je to kôli znaku koncovej nuly.

Bonus 1: String map

Niekedy sa určite stretnete s tým, že chcete nejakým spôsobom spracovať každý prvok poľa. Následne takto spracovaný prvok uložiť do poľa nového na to isté miesto ako pôvodný prvok. Príkladom môže byť prevedenie reťazca na reťazec s veľkými písmenami. V tejto úlohe si vyskúšate implementovať univerzálnu funkciu string map.

Zadání úkolu Bonus 1

Funkcii stringMap bude ako prvý argument predané pole nad ktorým sa bude aplikovať. Ako druhý argument vezme void * ukazaťeľ, do ktorého bude vložený výsledok. Tretím argumentom bude transformačná funkcia.

void stringMap(const char *string, void *result, void (*func)(void *, int, const char));

Práca funkcie je nasledovná. Funkcia prechádza pole znakov a každý znak s jeho príslušným indexom predá transformačnej funkcii.

Transformačná funkcia vezme ako svoj prvý argument výsledok (result), ako druhý vezme pozíciu (index), kde na ktorej sa predávaný znak nachádza a tretí argument bude konkrétny znak.

Príklad transformačnej funkcie:

/*
 * Funkcia prevedie reťazec na veľké písmená.
 */
void transformUp(void *out, int i, const char ch)
{
    char * result = (char *) out;
    result[i] = toupper(ch);
}
 
 
/* 
 * Funkcia spočíta koľko malých alebo veľkých písmen A sa v zadanom reťazci nachádza.
 * Povšimnite si, že index (i), sme nepoužili, v niektorých prípadoch to ne je treba.
 */
void countLetterA(void *out, int i, const char ch)
{
   if(tolower(ch) == 'a')
   {
      *((int*) out) += 1;
   }
 
}

Skúste si implementovať vlastnú transformačnú funkciu, ktorá spočíta počet písmen v reťazci.

Bonus 2: Better split

V tejto úlohe budete znova implementovať funkciu Split, avšak tento krát nebudete výsledok ukladať do matice pevnej veľkosti. Vstupom bude reťazec, ktorý budete deliť na podreťazce a znak, podľa ktorého budete deliť. Výstupom bude jednorozmerné pole znakov, ktoré sa bude správať ako buffer. Druhým výstupom bude pole ukazateľov na reťazce, ktoré budú ukazovať do tohoto bufferu.

Zadání bonusu 2

Naimplementujte funkciu my_strsplit2, ktorá ako svoj prvý argument zoberie reťazec, ako svoj druhý argument zoberie buffer, ktorý by mal mať rovnakú veľkosť ako pôvodný reťazec. Tretím argumentom bude deliaci znak, ako 4. argument funkcia vezme pole, ktoré musí byť veľké minimálne počet podreťazcov + 1. Počet podreťazcov môžete zistiť tak, že si spočítate, koľko krát sa deliaci znak nachádza v pôvodnom reťazci + 1. Pre zjednodušenie, ale predpokladajte, že nebudete mať viac ako 25 podreťazcov. Pole ukazateľov bude za posledným platným podreťazcom ukončené ukazateľom naNULL. Pomocou ktorého bude možné zistiť výslednú veľkosť poľa.

void stringSplit2(const char *orig, char *buffer, char delim, char *result[26]);

Funkcia bude fungovať nasledovne aby sme zabránili pozmeneniu pôvodného reťazca, zkopírujete obsah pôvodného reťazca do bufferu. Do výsledného poľa ukazateľov uložíte vždy adresu znaku, ktorý nasleduje za deliacim znakom. Ako posledný prvok poľa ukazateľov nadstavíte NULL. Všetky výskyty deliaceho znaku v buffery nahradíte nulovým znakom.

Vzorové řešení

Úlohu připravil Peter Stanko – stanko@mail.muni.cz.

QR Code
QR Code public:pb071:cviceni05 (generated for current page)