Hlavní strana -> Programování v C -> 11. Pole, práce s řetězci -> příklady

11. Příklady na řetězce a pointery

Vím, pointery jsou probírány až v jednom z dalších dílů, ale k řetězcům úzce patří. Jdenoduše proto, že pointery se využívají při práci s poli a řetězec v jazyce C je polem znaků. Každý řetězec musí být ukončen nulou a funkce s tím při svých činnostech počítají. Neukončit řetězec nulou může vést k fatálním chybám. Toto cvičení bude spočívat v napsání si vlastních funkcí standardně používaných k práci s řetězci.

Začněme s funkcí, která nám vrátí délku řetězce. Standardní funkcí je pro tento účel funkce strlen(). Co tedy od ní očekáváme a co bude potřebovat? Chceme, aby vrátila délku řetězce, což nám bude reprezentovat celé číslo. Řekněme typ int. Předat jí musíme řetězec, jehož délku chceme zjistit. Nejlépe to uděláme předáním ukazatele na začátek řetězce a pokud ještě tento parametr nastavíme jako const, vůbec nic se nestane, naopak program bude lepší, protože použitím const zamezíme nechtěnému změnění původního řetězce. Naše funkce myStrlen() může vypadat takto:

int myStrlen(const char *str)
{
	int len=0;
	while(*str++) len++;
	return len;
}

Obsahuje jeden cyklus, který pracuje následovně. Podmínka cyklu se vyhodnotí. Zjistí se hodnota znaku, na který ukazuje str a ukazatel se posune na další znak, pokud není nula. Počet znaků uložený v len se zvětší o 1. Funguje to proto, že nejdříve se vyhodnotí podmínka a potom (ihned po vyhodnocení) se teprve ukazatel posune dál. Podmínka pro vykonání cyklu přestane být splněna, jakmile začne ukazovat ukazatel na nulový ukončovací znak. Kdybychom jej zapomněli, cyklus by se prováděl pořád dále, dokud by se nenarazilo na nějakou nulu v paměti. Dostali bychom se tak do oblastí, kde nemáme co dělat.

Další funkce kterou si sami vytvoříme je funkce pro porovnání dvou řetězců z lexikografického hlediska. Tedy který řetězec je "menší" a který "větší" se rozhodne na základě toho, kde by se nacházel ve slovníku. Originální funkce strcmp() by vrátila rozdíl znaků, ve kterých by se začly řetězce lišit. My si to zjednodušíme tak, že funkce vrátí -1, pokud je první menší, než druhý, +1 pokud je to naopak a 0, pokud jsou shodné. Nebudem si to komplikovat ani tím, že třeba velká písmena jsou "abecedně menší" než malá písmena z hlediska ASCII tabulky. Velká písmena totiž mají hodnoty A-Z 65-90, kdežto malá a-z 97-122. Z toho vyplývá, že "Zizala", je "menší" než "adam". Háčky a čárky také uvažovat nebudem.

int myStrcmp(const char *s1, const char *s2)
{
	while(*s1 && s2){
		if(*s1 < *s2) return -1;
		if(*s1 > *s2) return 1;
		s1++;
		s2++;
	}

	if(*s1) return 1;
	if(*s2) return -1;
	return 0;
}

Taková funkce obsahuje také jeden cyklus, ve kterém prochází současně oba dva řetězce. Cyklus má podmínku, která jej ukončí v případě, že první, druhý nebo oba ukazatele dorazily na konec řetězce. Uvnitř cyklu se ptáme, jestli je aktuální znak prvního řetězce menší než aktuální znak druhého řetězce, pak jsme našli rozdíl a vracíme -1. Nebo jestli je tomu naopak, vrátíme 1. Pokud se neshoda nenašla, v tomto místě se řetězce shodují, ukazatele se nastaví na další znak a vše se opakuje. Pokud je ale podmínka cyklu vyhodnocena jako false, je třeba rozhodnout, který řetězec skončil dříve. Stačí se na problém podívat obrácene a otestovat, který naopak neskončil. Pak je větší. Jestliže skončily oba, dojde se až na závěrečný return, který vrátí shodu řetězců.

Třetí funkcí, kterou napíšeme může být třeba funkce pro kopírování řetězců. Například takovou, která převezme dva ukazatele. Jeden na cílové místo, druhý na zdrojové. Můžeme ještě přidat třetí parametr, kolik znaků se může maximálně zkopírovat. Vracet taková funkce nemusí nic, ale my jí vrátíme, kolik znaků se skutečně zkopírovalo.

int myStrcpy(char *cil, const char *zdroj, int max)
{
	int zkopirovano=0;

	while(*zdroj && zkopirovano<max){
		*cil++ = *zdroj++;
		zkopirovano++;
	}
	*cil = '\0';
	return zkopirovano;
}

Naše funkce je proti standardní mystrcpy() také bezpečnější, protože dává možnost kontrolovat počet skutečně zkopírovaných znaků. Stačí jen pamatovat, že jí musíme předat číslo maximálně velikost_bufferu-1, aby se na poslední pozici mohla připsat ještě ukončovací nula. Navíc tím umožňuje zkopírovat jen požadovaný počet znaků ze začátku zdrojového řetězce. char *cil nesmí být const, protože by do něj nebylo možno zapisovat. Použitím const, se proměnná stává read-only.

Zpět

Programování v C | CZ 175/477 | Mapa stránek
Bc. Petr Klimánek, student Ostravské univerzity v Ostravě