Kapitola 9. Ukazatele, pole a řetězce

Obsah

9.1. Ukazatele
9.2. Pole
9.3. Aritmetika ukazatelů
9.4. Řetězce
9.5. Funkce pro práci s řetězci
9.6. Vícerozměrná pole
9.7. Ukazatele na funkce
9.8. Ukazatele na ukazatele a pole ukazatelů
9.9. Argumenty příkazového řádku
9.10. Opakování

Časová náročnost
Časová náročnost kapitoly: 1 hodina 50 minut

Nejprve trocha teorie. Program se skládá z instrukcí, také říkáme kódu, a dat. Instrukce musí procesor vykonávat, k datům pak bude přistupovat tehdy, když mu to instrukce nařídí. Teď musíme pouze určit, jak procesor pozná, kterou další instrukci má vykonat, kde najde potřebnou proměnnou, přesněji její hodnotu? Pozná to podle adresy. Ukazatel je název proměnné, která obsahuje adresu. Prostřednictvím této adresy ukazuje na data.

Adresa je klíčem pro procesor, kde má brát instrukce nebo data. Překladač vyššího programovacího jazyka, například jazyka C, za nás vykoná všechnu potřebnou práci, když se jedná o adresy instrukcí.

Potřebné znalosti
Potřebné znalosti
K zvládnutí obsahu této kapitoly je nutné mít znalosti z kapitoly 2 až 8 - rozumět dříve probírané problematice jazyka C, chápat činnosti preprocesoru, provádění podmíněného předkladu a psaní maker.

9.1. Ukazatele

Časová náročnost
Časová náročnost: 8 minut

O ukazatelích jsme se už zmínili v kapitole 3 konkrétně v části 3.10. Nyní se k problematice ukazatelů ještě vrátíme. Pro začátek trocha opakování.

Důležité

Ukazatel představuje adresu objektu. Současně s sebou ukazatel nese i informaci o datovém typu, který se na dané adrese nachází. Ukazatel vytváříme stejně, jako proměnnou daného typu (nesmíme zapomínat, že ukazatel je spojen s datovým typem), jen před jeho jméno uvedeme symbol * "hvězdička". Pak již stačí jen získat adresu proměnné (objektu), kterou chceme změnit nebo jen zjistit její hodnotu. K tomu použijeme adresový operátor &, uvedený opět před identifikátorem, tentokrát požadované proměnné.

A jak tedy změníme hodnotu ukazatele? K tomu vystačíme již se známou "hvězdičkou". Jí rozšířený ukazatel ovšem musí být vlevo od přiřazení.

Pár příkladů pro procvičení.

Jednoduchý příklad
Příklad:

int x, y, *px, *p2x;    /* *px, *p2x jsou pointery (ukazatele) na int */
	 
px = &x;         	/* px nyní ukazuje na x */
*px = 5;         	/* totéž co x = 5; */
y = *px + 1;     	/* y = x + 1;  */
*px += 1;        	/* x += 1; */
(*px)++;         	/* x++;  závorky jsou nutné */
p2x = px;        	/* p2x ukazuje na totéž, co px, tj. na x */
*p2x = *p2x + y; 	/* x = x + y;  */

Při deklaraci ukazatele můžeme použít i modifikátor const. Pak se jedná o konstantní ukazatel, ukazatel na konstantu, nebo obojí, tedy konstantní ukazatel na konstantu. Tyto konstrukce se zejména používají při tvorbě nebo použití knihovních funkcí, kdy je například zdůrazněno (a překladačem kontrolováno), že argument funkce nebude v žádném případě modifikován. Opět pár příkladů.

Jednoduchý příklad
Příklad:

int i;
int *pi;                  /* pi je neinicializovaný ukazatel na typ int */
int * const cp = &i;      /* konstantní ukazatel na int */
const int ci = 7;         /* celočíselná konstanta */
const int *pci;           /* neinicializovaný ukaz. na celoč. konst. */
const int * const cpc = &ci;   /* konst. uk. na konstantu  */

9.2. Pole

Časová náročnost
Časová náročnost: 20 minut

V jazyku C je jednorozměrné pole seznamem proměnných, které jsou všechny stejného typu a přistupuje se k ním přes společné jméno. Jednotlivá proměnná v poli se nazývá prvek pole. Pole umožňuje pohodlně zpracovávat skupinu souvisejících dat.

Pro deklaraci pole se používá obecný formát:

Důležité
typ jméno-proměnné[velikost]

kde:

  • typ je platný datový typ jazyka C
  • jméno-proměnné je identifikátor pole
  • velikost je kladný počet prvků pole, tj. celočíselný konstantní výraz, který musí být vyčíslitelný za překladu. Typicky se jedná o makro - konstantu, nebo přímo zapsaný počet prvků pole. Poslední možnost však nevidíme příliš rádi.

pole.swf
Flashová animace
Kliknutím na ikonu spustíte test.

Klasický příklad deklarace celočíselného pole o 20 znaků.

Jednoduchý příklad
Příklad:

#define ROZSAH 20

int pole[ROZSAH];

Prvek pole se získává indexováním pole pomocí čísla prvku. V jazyku C začínají všechna pole nulovým indexem. To znamená, že chceme-li pracovat s prvním prvkem pole, zadáme jako index nulu. Pro indexování pole zadáme index požadovaného prvku do hranatých závorek.

Jednoduchý příklad
Příklad:

pole[0], pole[1], ... , pole[19]

Důležité Pamatujte, že pole začínají od nuly, takže index 1 označuje druhý prvek pole!!!

Paměťový prostor (velikost paměti) přidělený poli pole můžeme zjistit výpočtem, v němž počet prvků pole ROZSAH násobíme velikostí jednoho prvku, kterou zjistíme pomocí operátoru sizeof, jehož argumentem je požadovaný datový typ:

počet obsazených byte = ROZSAH * sizeof(int)

Nebo naopak, počet prvků pole:

pole = sizeof(pole) / sizeof(int)

Jazyk C ukládá pole do souvislé oblasti paměti. První prvek pole má nejnižší adresu. Po provedení této části programu:

Jednoduchý příklad
Příklad:

int i[5];
int j;

for(j=0; j<5; j++) i[j]=j;

Bude pole i vypadat takto:

i[0] hodnota 0
i[1] hodnota 1
i[2] hodnota 2
i[3] hodnota 3
i[4] hodnota 4

Jazyk C netestuje indexy polí, ale umožňuje současně s definicí pole provést i jeho inicializaci, tedy nastavení počátečních hodnot:

Jednoduchý příklad
Příklad:

double cisla[5] = {1.22, 0.4, -1.2, 12.3, 4.0};

Princip je jednoduchý. Před středník, jímž by definice končila, přidáme "rovnítko" a do složených závorek vložíme seznam hodnot, jimiž chceme inicializovat prvky pole. Hodnoty pochopitelně musí být odpovídajícího typu. Navíc jich nemusí být stejný počet jako je dimenze pole. Může jich být méně. Přiřazení totiž probíhá následovně: první hodnota ze seznamu je umístěna do prvního prvku pole, druhá hodnota do druhého prvku pole, ... Pokud je seznam vyčerpán dříve, než je přiřazena hodnota poslednímu prvku pole, zůstávají odpovídající (zbývající) prvky pole neinicializovány. Výjimkou jsou globální a statické proměnné, které jsou inicializovány nulou.

Při inicializaci popsané výše jsme museli uvádět dimenzi pole. Tuto práci ovšem můžeme přenechat překladači. Ten poli vymezí tolik místa, kolik odpovídá inicializaci.

Jednoduchý příklad
Příklad:

int pole[] = {21, 14, 55};

Důležité

Překladač nekontroluje rozsah použitého indexu. Pokud se o tuto skutečnost nepostaráme sami, můžeme číst nesmyslné hodnoty při odkazu na neexistující prvky pole.

Po probrané teorii si práci s poli vyzkoušíme na několika příkladech.

Naším prvním úkolem bude naplnit pole sqr druhými mocninami čísel 1 až 10 a pak je vypsat.

src/aray_sqr.c
Příklad 9.1.
/******************************
*	aray_sqr.c
*	19.03.2002
******************************/

#include <stdio.h>

#define VELIKOST 10

int main(void)
{
 int sqr[VELIKOST];
 int i;
 
 for(i=0; i<11; i++)
    sqr[i] = i*i;
 
 for(i=0; i<11; i++)
    printf("Druha mocnina cisla %d je %d\n",i,sqr[i]);
	
 return 0;
} 

Naším dalším úkolem bude napsat program, který si bude v poli uchovávat zadávané teploty. Nakonec vypíše nejteplejší den (den s nejvyšší teplotou), nejstudenější den (den s nejnižší teplotou) a průměrnou měsíční teplotu.

src/aray_tem.c
Příklad 9.2.
/******************************
*	aray_tem.c
*	19.03.2002
******************************/

#include <stdio.h>

#define NEJVICE 31

int main(void)
{
 int temp[NEJVICE];
 int i, dny, min, max, prum = 0;
 
 /* zjisteni poctu dnu */
 printf("Kolik dnu ma tento mesic? ");
 scanf("%d",&dny);
 
 /* nacteni teplot v jednotlivych dnech */
 for(i=0; i<dny; i++)
 {
  printf("Zadejte teplotu pro den %d: ",i+1);
  scanf("%d",&temp[i]);
 }
 
 /* zjisteni prumerne teploty */ 
 for(i=0; i<dny; i++)
    prum = prum + temp[i];
 printf("\nPrumerna teplota: %f",(float)prum/dny);
 
 /* nalezeni minimalni a maximalni teploty */	
 min = max = temp[0];
 for(i=0; i<dny; i++)
 {
  if(min>temp[i]) min = temp[i];
  if(max<temp[i]) max = temp[i];
 }

 printf("\nMinimalni teplota: %d",min);
 printf("\nMaximalni teplota: %d",max);
 
 return 0;
}   


9.3. Aritmetika ukazatelů

Časová náročnost
Časová náročnost: 17 minut

Jak jsme si již uváděli, ukazatel ukazuje na hodnotu nějakého typu. Nese s sebou informaci o tomto typu. Tato informace pochopitelně představuje počet bytů nutný pro uchování hodnoty typu. A při aritmetice ukazatelů smysluplně používáme obě části zmíněné informace, adresu i velikost položky.

Důležité

Aritmetika ukazatelů je omezena na tři základní operace, sčítání, odčítání a porovnání. Nesmíme samozřejmě zapomenout na unární operace inkrementace a dekrementace, v daném kontextu a přehledněji řečeno jde o operace následovník a předchůdce. Podívejme se nejprve na první dvě operace.

Sčítání i odčítání jsou binární operace. Protože se zabýváme aritmetikou ukazatelů, bude jedním z operandů vždy ukazatel. Druhým operandem pak bude buď opět ukazatel (Týká se pouze operace odčítání dvou ukazatelů), nebo jím může být celé číslo. Pokud jsou oba operandy ukazatele, je výsledkem jejich rozdílu počet položek, které se mezi adresami, na něž ukazatele ukazují, nacházejí. Pokud k ukazateli přičítáme, respektive odčítáme celé číslo, je výsledkem ukazatel ukazující o příslušný počet prvků výše, respektive níže, představíme-li si prvky s vyšším indexem nad prvky s nižším indexem. Tak je ale pole v C definováno.

Jednoduchý příklad
Příklad:

int i, *pi, a[N];

adresa prvku a[0] je &a[0], což je totéž jako a + 0. Tento výraz již představuje součet ukazatele s celočíselnou hodnotou. V našem případě je celočíselná hodnota rovna 0. Z toho vyplývá že platí i:

Jednoduchý příklad
Příklad:

a + i <=> &amp;a[i]
*(a+i) <=> a[i]

Pozn:
Pozn:
znak <=> znamená ekvivalenci, což čteme jako "což je totéž".

Výrazy uvedené níže jsou po tomto přiřazení zaměnitelné (mají stejný význam, představují hodnotu prvku pole a s indexem i).

Jednoduchý příklad
Příklad:

a[i] <=> *(a+i) <=> pi[i] <=> *(pi+i)

Chceme-li se například dostat na prostřední prvek u pole majícího 20 znaků, použijeme tento zápis s pomocí ukazatele pi:

Jednoduchý příklad
Příklad:

<screen>
pi = a + 9;
</screen> 

S tímto ukazatelem se pak můžeme posunout na následující prvek pole třeba pomocí inkrementace, neboť i u ní překladač ví, o kolik bajtů má posunout (zvětšit) adresu, aby ukazoval na následující prvek: ++pi nebo pi++, ale ne ++a ani a++, neboť a je konstantní ukazatel (je pevně spojen se začátkem pole).

A na závěr si opět ukážeme příklad. Funkce printf() umožňuje zobrazit adresu paměti obsaženou v ukazateli pomoci specifikátoru %p. Tuto schopnost můžeme použít pro předvedení ukazatelové aritmetiky. Ukážeme si, jak ukazatelová aritmetika závisí na základním typu ukazatele.

src/poin_ari.c
Příklad 9.3.
/******************************
*	poin_ari.c
*	19.03.2002
******************************/

#include <stdio.h>

int main(void)
{
 char *cp, c;
 int *ip, i;
 float *fp, f;
 double *dp, d;
 
 cp = &c;
 ip = &i;
 fp = &f;
 dp = &d;

 /* vytiskneme aktualni hodnoty */
 printf("%p %p %p %p\n", cp, ip, fp, dp);
 
 /* inkrementace */
 cp++;
 ip++;
 fp++;
 dp++;
 
 /* vytiskneme zmenene hodnoty */
 printf("%p %p %p %p\n", cp, ip, fp, dp);
 
 return 0;
} 

Hodnoty obsažené v jednotlivých proměnných se mohou lišit v závislosti na překladači, ale přesto uvidíme, že adresa ukazující na c bude zvýšena o jeden byte. Ostatní budou inkrementovány o počet bytů jejich základních typů. V 16 bitovém prostředí to bude typicky 2 pro int, 4 pro float a 8 pro double.

9.4. Řetězce

Časová náročnost
Časová náročnost: 10 minut

Důležité Nejčastějším použitím jednorozměrného pole v jazyku C je řetězec. Na rozdíl od ostatních počítačových jazyků nemá jazyk C zabudován datový typ řetězec. Místo toho je řetězec definován jako pole znaků ukončené nulovým znakem (null) . V jazyku C má hodnotu nula. Skutečnost, že pole musí být ukončeno nulovým znakem znamená, že musíme nadefinovat pole, do něhož se vejde řetězec o jeden byte delší, než je požadovaný řetězec, tedy aby zbylo místo pro nulový znak. Řetězcová konstanta je ukončena nulovým znakem automaticky překladačem.

Příklad definice pole:

Jednoduchý příklad
Příklad:

char retezec[SIZE];

Na identifikátor retezec můžeme nahlížet takto:

  1. Jako na proměnnou typu řetězec (pole znaků doplněné o zarážku, kterou přidáváme někdy sami) délky SIZE, jehož jednotlivé znaky jsou přístupné pomocí indexů retezec[0] až retezec[SIZE-1].
  2. Nebo jako na konstantní ukazatel na znak, tj. na první prvek pole retezec, retezec[0].

Řetězcové konstanty píšeme mezi dvojici uvozovek, uvozovky v řetězcové konstantě musíme uvodit speciálním znakem \, nazývaným "zpětné lomítko".

Jednoduchý příklad
Příklad:
  • "abc" - je konstanta typu řetězec délky 3+1 znak (zarážka), tedy tři znaky, čtyři bajty paměti (konstantní pole čtyř znaků)
  • "a" - je řetězcovou konstantou, má délku 1+1 znak
  • 'a' - je znaková konstanta !!!!

Platí tedy, že následující zápisy jsou ekvivalentí:

Jednoduchý příklad
Příklad:

char pozdrav[] = "ahoj";

char pozdrav[] = {'a','h','o','j',0};

Mějme nyní tuto definici:

Jednoduchý příklad
Příklad:

char *ps = "retezec";

Jde o velice podobnou definici, ale přesto se podstatně liší. ps je ukazatel na znak, jemuž je přiřazena adresa řetězcové konstanty retezec. Tato konstanta je tedy umístěna v paměti, ale proměnná ps na ni ukazuje jen do okamžiku, než její hodnotu třeba změníme. Pak ovšem ztratíme adresu konstantního řetězce "retezec".

Následující příklad demonstruje práci s řetězci.

src/poin_chr.c
Příklad 9.4.
/******************************
*	poin_chr.c
*	19.03.2002
******************************/

#include <stdio.h>

int main()
{
 char text[] = "world", *new1 = text, *new2 = text;
 printf("%s\t%s\t%s\n", text, new1, new2);
 /* menime hodnotu ukazatele, nedochazi ke kopirovani retezcu */
 new1 = "hello";  
 printf("%s\t%s\t%s\n", text, new1, new2);
 printf("%s\n", "hello world" + 2);	/* posun retezce */
 
 return 0;
}
world   world   world
world   hello   world
llo world

Hodnota řetězcové konstanty je stejná jako hodnota jakéhokoliv ukazatele na řetězec (a nejen na řetězec) - ukazatel na první položku.

Při práci s řetězci nesmíme zapomínat na skutečnost, že jsou reprezentovány ukazateli. Pouhou změnou či přiřazením ukazatele se samotný řetězec nezmění.

9.5. Funkce pro práci s řetězci

Časová náročnost
Časová náročnost: 7 minut

Řetězce zřejmě budeme v našich programech používat velmi často. Naštěstí jsou již funkce pro práci s řetězci napsány a nemusíme si je psát sami. Jsou součástí standardní knihovny funkcí a jejich prototypy jsou obsaženy v hlavičkovém souboru string.h.

Syntaxe:

Důležité
  • int strcmp(const char *s1, const char *s2); - lexikograficky porovnává řetězce a vrací hodnotu:
    <0 je-li s1 < s2
    =0 je-li s1 = s2
    >0 je-li s1 > s2
    	
  • int strncmp(const char *s1, const char *s2, unsigned int n); - totéž jako předchozí s tím, že porovnává nejvýše n znaků
  • unsigned int strlen(const char *s); - vrátí počet významných znaků řetězce (bez zarážky)
  • char *strcpy(char *dest, const char *src); - kopíruje src do dest
  • char *strncpy(char *dest, const char *src,unsigned int n); - totéž jako předchozí, ale nejvýše n znaků (je-li jich právě n, nepřidá zarážku)
  • char *strcat(char *s1, const char *s2); - s2 přikopíruje za s1
  • char *strncat(char *s1, const char *s2, unsignedint n); - totéž jako předchozí, ale nejvýše n znaků, n se týká délky s2, ne s1
  • char *strchr(const char *s, int c); - vyhledá první výskyt (zleva) znaku c v řetězci s
  • char *strrchr(const char *s, int c); - vyhledá první výskyt (zprava) znaku c v řetězci s
  • char *strstr(const char *str, const char *substr); - vyhledá první výskyt (zleva) podřetězce substr v řetězci str

9.6. Vícerozměrná pole

Časová náročnost
Časová náročnost: 7 minut

Jazyk C umožňuje deklarovat pole pouze jednorozměrné. Jeho prvky ovšem mohou být libovolného typu. Mohou tedy být opět (například) jednorozměrnými poli. To však již dostáváme vektor vektorů, tedy matici. Budeme-li uvedeným způsobem postupovat dále, vytvoříme datovou strukturu prakticky libovolné dimenze. Pak již je třeba mít jen dostatek paměti. A operační systém který nám ji umí poskytnout.

Příklad definici matice:

Jednoduchý příklad
Příklad:

type jmeno[4][5];

Kde:

  • typ - určuje datový typ položek pole
  • jmeno - představuje identifikátor pole
  • [4][5] - určuje rozsah jednotlivých vektorů na čtyři řádky a pět sloupců

Grafické zobrazení takového dvourozměrného pole včetně indexů jednotlivých prvků je následující:

S vícerozměrným polem můžeme pracovat stejně jako s jednorozměrným. To se přirozeně týká i možnosti inicializace. Jen musíme jednotlivé "prvky" vektory, uzavírat do složených závorek. Tento princip je ovšem stejný jako pro vektor.

Důležité Poslední index se mění nejrychleji (tak je vícerozměrné pole umístěno v paměti).

Nyní si na příkladu ukážeme jak vypsat dvou rozměrné pole 4x5, které bude vyplněno součiny indexů.

src/aray_4x5.c
Příklad 9.5.
/******************************
*	aray_4x5.c
*	19.03.2002
******************************/

#include <stdio.h>

int main()
{
 int pole2[4][5];
 int i,j;
 
 for(i=0; i<4; i++)
 {
    for(j=0; j<5; j++)
	{
	   pole2[i][j] = i*j;
	 }
 }
 	   
 for(i=0; i<4; i++)
 {
    for(j=0; j<5; j++)
	{
	   printf("%d ", pole2[i][j]);
	}
	printf("\n");
 }
 
 return 0;
} 

9.7. Ukazatele na funkce

Časová náročnost
Časová náročnost: 10 minut

Jedná se často o ukazatele nebo o pole ukazatelů. Funkce ovšem nejen vracejí hodnotu jistého typu, ale mohou mít i různý počet argumentů různého typu.

Definujme si například ukazatel na funkci, která nemá argumenty a vrací hodnotu zvoleného datového typu:

Jednoduchý příklad
Příklad:

typ (*jmeno)();

Závorky okolo identifikátoru jmeno a úvodní hvězdičky jsou nutné, neboť zápis

Důležité
typ *jmeno();

představuje funkci vracející ukazatel na typ.

Příkladem použití ukazatelů na funkce může být knihovní funkce qsort().

Syntaxe:

void qsort(void *base, size_t nelem, size_t width, 
int (*fcmp)(const void *, const void *));

Kde:

  • base - je začátek pole, které chceme setřídit
  • nelem - je počet prvků, které chceme setřídit (rozsah pole)
  • width - je počet bajtů, který zabírá jeden prvek
  • fcmp - je ukazatel na funkci, provádějící porovnání, ta má jako argumenty dva konstantní ukazatele na právě porovnávané prvky (nutno přetypovat).

Pozn:
Pozn:
Prototyp funkce qsort() je umístěn v hlavičkovém souboru stdlib.h.

Funkce qsort() je napsána obecně a tak nemá žádné konkrétní informace o typech hodnot, které třídí. Tuto část řeší naše uživatelská funkce, která správně porovnává hodnoty.

src/poin_fct.c
Příklad 9.6.
/******************************
*	poin_fct.c
*	19.03.2002
******************************/

#include <time.h>
#include <stdio.h>
#include <stdlib.h>

#define	POCET 10000
#define	RND_START 1234

int float_sort (const float *a, const float *b)
{
 return (*a - *b);	
} 

void test (float *p, unsigned int pocet)
/* otestuje zda dane pole je setrideno vzestupne */
{
 int chyba = 0;
 for (; !chyba && --pocet > 0; p++)
   if (*p > *(p+1))
     chyba=1;
 puts ((chyba) ? "\npole neni setrideno\n" : "\npole je setrideno\n");
} 

void vypln(float *p, int pocet)
/* vypni pole dane adresou a pocte prvku, nahodnymi cisly pomoci generatoru nahodnych cisel */
{
 srand(RND_START);
 while (pocet-- > 0)
    *p++ = (float) rand();
} 

int main (void)
{
 static float pole [POCET];

 vypln (pole, POCET);
 clock(); /* zacatek pocitani casu */
 qsort(pole, POCET, sizeof(*pole), (int (*) (const void *, const void *)) float_sort);
 /* uplynuly cas v ticich preveden na sekundy */
 printf ("Trideni qsort trvalo %.2fs\n", ((float) clock()) / CLOCKS_PER_SEC);  
 test (pole, POCET);

 return 0;
}

Možný výstup:

Trideni qsort trvalo 0.26s

pole je setrideno

9.8. Ukazatele na ukazatele a pole ukazatelů

Časová náročnost
Časová náročnost: 12 minut

Vzhledem k tomu, že ukazatel je proměnná jako každá jiná, můžeme na ni ukazovat nějakým jiným (dalším) ukazatelem. Ve vícerozměrném poli provádíme v podstatě totéž, jen jednotlivé vektory nejsou pojmenované. Přistupujeme k nim pomocí bázové adresy základního pole a indexu. Z tohoto pohledu jsou opravdu nepojmenované. Lze říci, že pole ukazatelů je v jazyce C nejen velmi oblíbená, ale i velmi často používaná konstrukce.

Nejlépe si ukazatele procvičíme na příkladu. Tentokrát půjde o program, který lexikograficky setřídí jména měsíců v roce. Pro vlastní třídění jsme použili funkci qsort().

src/poin2poi.c
Příklad 9.7.
/******************************
*	poin2poi.c
*	19.03.2002
******************************/

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

#define N 13

int qsort_stringlist(const void *e1, const void *e2)
{
 return strcmp(*(char **)e1, *(char **)e2);
}

int main()
{
 char *name[N] =
  {
   "chyba", "leden", "unor", "brezen", "duben",
   "kveten", "cerven", "cervenec", "srpen",
   "zari", "rijen", "listopad", "prosinec"
  };
 int i;
 puts("jmena mesicu v roce (podle poradi):");
 for (i = 0; i < N; i++)
	printf("%2d. mesic je : %s\n", i, name[i]);
 puts("\nsetrideno:");
 qsort(name, N, sizeof(char *), qsort_stringlist);

 for (i = 0; i < N; i++)
   printf("%2d. %s\n", i, name[i]);
 return 0;
}

Výstup:

setrideno:
 0. brezen
 1. cerven
 2. cervenec
 3. chyba
 4. duben
 5. kveten
 6. leden
 7. listopad
 8. prosinec
 9. rijen
10. srpen
11. unor
12. zari

Následující program používá dvojrozměrné pole ukazatelů pro vytvoření tabulky řetězců, která sdružuje odrůdy jablek s jejich barvami. Pro použití programu zadejte název odrůdy a program vypíše její barvu.

src/poi2aray.c
Příklad 9.8.
/******************************
*	poi2aray.c
*	19.03.2002
******************************/

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

char *p[][2] = {
"Red Delicious", "cervene",
"Golden Delicious", "zlute",
"Winesap", "cervene",
"Gala", "oranzove",
"Lodi", "zelene",
"Mutsu", "zlute",
"Cortland", "cervene",
"Jonathan", "cervene",
"", "" /* ukonceni tabulky nulovymi retezci */
};

int main(void)
{
 int i;
 char jablko[80];
 
 printf("Zadejte jmeno odrudy jablka: ");
 gets(jablko);
 
 for(i=0; *p[i][0]; i++)
 {
  if(!strcmp(jablko, p[i][0]))
     printf("%s je %s\n", jablko, p[i][1]);
 }
 
 return 0;
}

Prohlédněte si pozorně podmínku řídící cyklus for. Výraz *p[i][0] nabývá hodnoty prvního bytu i-tého řetězce. Jelikož je seznam ukončen nulovým řetězcem, bude tato hodnota při dosazení konce tabulky nulová (nepravdivá). Ve všech ostatních případech bude nenulová a cyklus se bude opakovat.

9.9. Argumenty příkazového řádku

Mnoho programů umožňuje zadávat při jejich spuštění argumenty na příkazovém řádku. Argument příkazového řádku je informace, která následuje za jménem programu na příkazovém řádku operačního systému.

Samozřejmě i programy napsané v jazyce C mohou využívat argumenty příkazového řádku. Ty se předávají do programu pomocí dvou argumentů funkce main(). Tyto argumenty se nazývají argc a argv a jak jste již mohli odhadnout jsou tyto argumenty nepovinné.

Syntaxe:

Důležité
int main(int argc, char *argv[])

Kde:

  • argc - první argument funkce main(), tj. typicky int argc, udává počet argumentů příkazové řádky (jeden = jen jméno programu, dva = jméno programu + jeden argument, ...). Je celočíselného typu.
  • argv - druhý argument funkce main(), tj. typicky char *argv[], představuje hodnoty argumentů příkazového řádku. Jeho typ je pochopitelně pole ukazatelů na řetězce, neboť jimi argumenty příkazového řádku skutečně jsou.

Pozn:
Pozn:
Je dobrým zvykem popsané argumenty funkce main() pojmenovat argc a argv, kde arg znamená argument, přípona c znamená count a přípona v znamená value.

Jazyk C nespecifikuje, co tvoří argument příkazového řádku, protože jednotlivé operační systémy se v tomto bodě od sebe značně liší. Nejčastější konvence zní: Každý argument příkazového řádku musí být oddělen mezerou nebo tabulátorem. Čárky, středníky a podobně nejsou považovány za oddělovač.

Toto je test

Je tvořen třemi řetězci, ale

Toto,je,test

je jeden řetězec.

Potřebujeme-li předat argument příkazového řádku který obsahuje mezery, musíme jej vložit do uvozovek, jak ukazuje následující příklad.

"Toto je test"

Nyní si napišme příklad, který vypíše své argumenty příkazového řádku.

src/argcargv.c
Příklad 9.9.
/******************************
*	argcargv.c
*	19.03.2002
******************************/

#include <stdio.h>

int main(int argc, char *argv[])
{
 int i;
 for (i = 0; i < argc; i++)
   printf("%d\t%s\n", i, argv[i]);
   
 return 0;
}

9.10. Opakování

Cvičení
Cvičení

Úkol k textu

Zadání 1)

Napište program s cyklem for, který počítá od 0 do 9 a vypisuje čísla na obrazovku. Čísla vypisujte pomocí ukazatele.
Řešení

Úkol k textu

Zadání 2)

Ukažte jak deklarovat ukazatel na double
Řešení

Úkol k textu

Zadání 3)

Napište program, který přiřazuje hodnotu proměnné nepřímo, pomocí ukazatele na tuto proměnnou.
Řešení

Úkol k textu

Zadání 4)

Co je špatně na tomto úseku programu?
#include <stdio.h>

int main(void)
{
 int i, count[10];
 
 for(i=0; i<100; i++)
 {
  printf("Zadejte cislo: ");
  scanf("%d",&count[i]);
 }
.
.
.
}
Řešení

Úkol k textu

Zadání 5)

Napište program, který načte deset čísel zadaných uživatelem a ohlásí zda jsou některá z nich shodná.
Řešení

Úkol k textu

Zadání 6)

Co je špatně na tomto úseku programu?
int *p, i;
p = &i;
p = p * 8;
Řešení

Úkol k textu

Zadání 7)

Můžete k ukazateli přičíst neceločíselnou hodnotu?
Řešení

Úkol k textu

Zadání 8)

Napište program, který přečte řetězec a pak jej vypíše na obrazovku pozpátku.
Řešení

Úkol k textu

Zadání 9)

Co je špatně na tomto programu?
#include <stdio.h>
#include <string.h>

int main(void)
{
 char str[5];
 
 strcpy(str, "tento text");
 printf(str);
 
 return 0;
}
Řešení

Úkol k textu

Zadání 10)

Napište program, který přečte řetězec a pak jej vypíše na obrazovku pozpátku.
Řešení

Úkol k textu

Zadání 11)

Napište program, který přebírá dva argumenty z příkazového řádku a zobrazte jejich součet.
Řešení
test9.swf
Test
Kliknutím na ikonu spustíte test.