Skočiť na obsah


Programovanie v jazyku C a C#

Návody pre začiatočníkov a pokročilých



Fórum: Škôlka jazyka C
- - - - -
Fotografia

8. lekcia C (Viacrozmerné polia) základná škola


8 Viacrozmerné polia

Jazyk C umožňuje, aby pole malo viac dimenzií než len jednu. Najčastejšie sa používajú dvojdimenzionálne polia (tabuľky), ale troj a viacrozmerné polia sú taktiež dovolené. Pre viacrozmerné polia platí, že všetky indexy začínaju od 0 (nuly).


8.1 Základne definície a prístup k prvkom

Definícia viacrozmerného poľa je jednoduchá:

int x[2][3]
Pre definíciu viac úplne rovnakých polí sa používa definícia pomocou nového typu:
typedef int DVA[2][3];

DVA x, y, z;
Nový typ pre dvojrozmerné pole je možné taktiež vytvoriť pomocou už existujúceho jednorozmerného typu:
typedef int JEDEN[2];

typedef JEDEN DVA[3];
Prístup k prvkom viacrozmerného poľa pomocou indexov je úplne rovnaký, ako prístup do jednorozmerného poľa, ale pozor - každý index musí byť v samostatných zátvorkách, teda napr.:
int tabulka[5][10];

tabulka[1][6] = 4;

tabulka[4][9] = 0;

tabulka[4,9] = 0;					/* chyba */
Tri a viac rozmerné polia sú podobné dvojrozmerným, teda napr.:
int troj[5][6][7];

troj[0][5][0] = 10;
8.2 Uloženie viacrozmerných polí v pamäti

Pri prvých pokusoch s C nás príliš nezaujíma ako je čo uložené v pamäti a prečo. Keď však budeme chciet pracovať s viacrozmernými poľami efektívnejšie, je nutné vedieť, že dvojrozmerné pole je uložené v pamäti po riadkoch, teda definícia:

int x[2][3];
alokuje v pamäti 2*3 prvkov, napr. 2*3*2 (teda napr. sizeof(int)).
Predpokladajme, že počiatočná adresa poľa je 100, potom je obsadenie pamäti nasledovné:

Obrázok

Až sem to bolo pravdepodobne jasné. Nasledujúce riadky sú však oveľa menej zrozumiteľné, ale vyplaíi sa snaha o ich pochopenie.
Jazyk C je konzistentný (t. j. pracuje s poľami stále rovnako) v prístupe k poliam - dvojrozmerné pole v C je pri prvom pohlade vlastne jednorozmerné pole, ktoré má prvky pointery. To, že sú to pointery na ďalšie jednorozmerné pole, je vecou až druhého podrobnejšieho pohľadu. Obsahom prvého prvku x[0] jednorozmerného poľa, je pointer na prvý riadok dvojrozmerného poľa x. Inými slovami povedané - typ jednorozmerného poľa x je trojprvkové pole prvkov typu int a veľkosť tohoto typu je 6, teda 3 * sizeof(int), kde sizeof(int) predpokladáme rovné 2.

Je to zložité?
Asi áno, ale hádam vám pomôže nasledujúci modifikovaný obrazok:

Obrázok

Z obrázku je jasné, že:
*x	a	x[0]	predstavujú tú istú adresu (ale iné typy pointerov)

*x + 1	a	x[0] + 1	sú odlišné adresy (106 a 102)
Podobne je možné vypozorovať, že:
x						 /* je pointer na dvojrozmerne pole							 */

x[i]						/* je pointer na i-ty riadok									*/

*(x + 1) == x[1] = 106	 /* je adresa prveho riadku (inac povedane pointer na 1. riadok) */

x[i][j]					 /* je hodnota prvku [i][j] dvojrozmerneho pola				 */
Pretože:
x[i] == *(x + i)
je bázova adresa i-teho riadku,
potom:
  • adresa indexovanej premennej je:
&x[i][j] == x[i] + j == *(x + i) + j
  • hodnota indexovanej premennej je:
x[i][j] == *(x[i] + j) == *(*(x + i) + j)
Príklad:
Nasledujúci úsek programu ukazuje definíciu, inicializáciu a použitie dvojrozmerného poľa.





#include <stdio.h>



int main(void) {

	int i, j, x[5][5]; // dvojrozmerne pole o 25 prvkoch



	for (i = 0; i < 5; i++) { // pre kazdy riadok

		for (j = 0; j < 5; j++) // pre kazdy stlpec

			x[i][j] = 1 + j + (i * 5); // vkladanie cisiel od 1 do 25 po riadkoch a stlpcoch

	}

	for (i = 0; i < 5; i++) { // vypis cisla riadku

		printf("mapa pamati pre %d. riadok: \n",i);

		for(j = 0; j < 5; j++) // vypis cisla stlpca, adresy umiestnenia a hodnoty prvkov

			printf("\t stlpec %d \t adresa %p \t hodnota %d \n", j, (x[i] + j), x[i][j]);

		putchar('\n');

}



	return 0;

}

8.3 Rôzne spôsoby definície dvojrozmerných polí

Ak potrebujeme pracovať s dvojrozmerným poľom, ktoré je "obdĺžnikové" a je ho možné definovať staticky, potom to už teraz vieme a ako vieme, nie sú v tom žiadne zrady.
Problémy ale nastávajú s viacrozmerným dynamickým poľom. Našťastie sú to problémy iba pri definícii pointeru na toto pole a pri alokácii pamäti pre pole.
Akonáhle sa nám podarí tieto dva kroky zvládnuť, môžeme zase pracovať s dynamickým poľom úplne rovnako ako so statickým.
Pre vytvorenie dvojrozmerného poľa sú k dispozícii 4 hlavné spôsoby, z ktorých ten prvý už poznáme - je to statické pole.
Ďalšie tri umožňujú vytvárať dvojrozmerné dynamické pole, z ktorých každé je trochu iné.
V nasledujúcom výklade budeme predpokladať znalosť vzťahov z predchádzajúcej časti 8.2.

Poznámky:
  • Vo všetkých štyroch prípadoch budeme vytvárať dvojrozmerné pole s prvkami typu int a predpokladáme sizeof(int) == 2.
  • Pri prideľovaní dynamickej pamäti funkciou malloc() nebudeme z dôvodov jednoduchosti zápisu testovať uspesnosť pridelenia. V praxi by však bol tento test "životne" nutný, zvlášť keby sa jednalo o rozsiahle pole.

8.3.1 Statické dvojrozmerné pole

Tento spôsob už dôverne poznáme, takže len pre úplnosť - definícia je:

int xa[2][3];
Pole je alokované pri preklade ako súvislý blok 6-tich prvkov a je uložené po riadkoch v dátovej oblasti pamäti.
xa je konštantný pointer, ktorého hodnota nemôže byť zmenená - nie je to l-hodnota.


8.3.2 Pole pointerov

Pretože už vieme, že jazyk C sa pozerá na dvojrozmerné polia dvojúrovňovým pohľadom, je možné tohto faktu využit a definovať pole iba pre "prvý pohľad". Samozrejme je potom nutné, aby sme sami zaistili aj ten "druhý pohľad", ktorý za nás pri definícii statického poľa robí prekladač.

Definícia jednorozmerného poľa pointerov je:
int *xb[2];
xb nie je dvojrozmerné pole, ale jednorozmerné pole dvoch pointerov na typ int a tieto pointery potom ďalej využijeme ako ukazovatele na jednotlivé riadky poľa. Tieto riadky ale zatiaľ neexistujú a preto musíme pre každý riadok jednotlivo alokovať pamät pomocou funkcie malloc(), teda:

xb[0] = (int *) malloc(3 * sizeof(int));

xb[1] = (int *) malloc(3 * sizeof(int));
Po tejto alokácii, je potom možné teraz už dvojrozmerné dynamické pole xb normalne použivat napr.:
x[0][2] = 5;
Poznámky:
  • Tento druh poľa sa veľmi často používa.
  • Vždy je ale nutné si uvedomiť, že druhý riadok poľa xb nemusí v pamäti ležať bezprostredne za prvým riadkom - obidva riadky spolu v pamäti nijako nesúvisia. Inak povedané - keby sme u statického poľa použili chybný príkaz: xa[0][3] = 8; potom by sa hodnota 8 zápisala do prvku xa[1][0], pretože pretiekla medza prvého riadku poľa a my sme sa dostali na prvý prvok druhého riadku. Pokiaľ ten istý príkaz použijeme pre dynamické pole xb, teda: xb[0][3] = 8; potom s najväčšou pravdepodobnosťou nezmeníme prvok xb[1][0], pretože ten leži v pamäti úplne inde, lebo patrí k druhému riadku poľa xb.
8.3.3 Pointer na pole

Nasledujúci spôsob definície dynamického poľa nie je príliš často používaný, ale v niektorých špeciálnych prípadoch má svoje opodstatnenie.
Využíva sa skutočnosť, že jazyk C umožňuje taktiež viacúrovňový pohľad na dvojrozmerné pole "z nižšej úrovne", teda od stĺpcov. Je to teda zhruba opačný spôsob, než bol predchádzajúci.
Definícia je:

int (*xc)[3];
xc nie je pole, ale len jeden pointer na pole troch int.
Ak alokujeme pomocou malloc() "dostatok pamäti", môžeme s ňou pomocou xc pracovať ako s dvojrozmerným poľom, pretože prekladač pozná veľkosť riadku - 3 prvky typu int.
Vyraz "dostatok pamäti" znamená celé dvojrozmerné pole alokované jedným príkazom, teda:

xc = (int (*)[3] malloc(2 * 3 * sizeof(int));
xc teraz ukazuje na blok 6-tich int združených vďaka definícii xc po trojiciach, ktoré ležia v pamäti za sebou. Je to teda prakticky obdoba statického poľa, len s tým rozdielom, že je celé uložené v dynamickej pamäti.

Prístup k prvkom je opäť zhodný, napr.:

xc[0][1] = 6;

xc[1][2] = 9;
Poznámka:
Ak použijeme chybný príkaz:
xc[0][3] = 8;
potom sa hodnota 8 zapíše do prvku xc[1][0], rovnakým spôsobom, ako u statického poľa.


8.3.4 Pointer na pointer

Tento spôsob je vôbec najkomplikovanejší a vychádza vlastne z "nultého pohľadu" na pole, kde sa pozeráme na jednorozmerné pole ako na pointer.

Definícia je:
int **xd;
a potom platí:
xd		 /* je pointer na pointer na typ int */

*xd		 /* je pointer na typ int			*/

**xd		/* je prvok typu int				*/
Na vytvorenie dvojrozmerného poľa je nutné urobiť dva kroky:
  • Alokovať dva pointery na riadky:
    xd = (int **) malloc(2 * sizeof(int *));
    teda teraz je možné používať dva prvky poľa xd[0] a xd[1]
  • Alokovať pamät pre tri prvky typu int na riadku (čo je rovnaké, ako u poľa xb):
    xd[0] = (int *) malloc(3 * sizeof(int));
    xd[1] = (int *) malloc(3 * sizeof(int));
Po týchto dvoch akciách platia pre pole xd rovnaké pravidlá, ako pre pole xb.


8.3.5 Výhody a nevýhody predchádzajúcich štyroch spôsobov

Vyššie popísane štyri spôsoby alokácie dvojrozmerného poľa je možné hodnotiť z rôznych hľadísk:

Typ poľa:
  • Definícia xa predstavuje statické pole.
  • Definície xb, xc a xd predstavujú alokáciu dynamického poľa.
Pamäťové naroky: (Je nutne poznamenať, že každa alokácia dynamickej pamäti potrebuje naviac určité miesto pre "administratívu")
  • Definícia xa je pamäťovo najvýhodnejšía, pretože vyžaduje iba pamät pre prvky poľa a žiadne pomocné pointery. Taktiež prístup k jednotlivým položkám poľa bude pravdepodobne najefektívnejší.
  • Definícia xb vyžaduje naviac pamät pre 2 pointery na typ int.
  • Definícia xc vyžaduje naviac pamät pre 1 pointer na typ int. Prístup do poľa bude pravdepodobne takmer rovnako rýchly ako u poľa xa.
  • Definícia xd vyžaduje naviac pamät pre 3 pointery. Dá sa predpokladať, že prístup do poľa xd bude pravdepodobne najpomalší zo všetkých predchádzajúcich variant.
Charakter poľa:
  • Definícia xa vytvorí iba "pravoúhle" pole - každý riadok má rovnaký počet stĺpcov. Počet riadkov aj počet stĺpcov je pevne určený pri preklade a nie je možné ho v priebehu výpočtu meniť.
  • Definícia xb umožňuje vytvoriť pole s pevným počtom riadkov (určeným pri preklade), ale každý riadok môze mať inú veľkosť - iný počet stĺpcov. Pole xb teda môze byť zubaté.
  • Definícia xc umožňuje vytvoriť pole s pevným počtom stĺpcov (daným pri preklade), ale s ľubovolným počtom riadkov. Pole xc je opät "pravoúhle".
  • Definícia xd umožňuje vytvoriť kompletné dynamické pole s ľubovolným, za behu programu voliteľným počtom riadkov a každý riadok môze mať ľubovolný počet stĺpcov.
Príklad:
Ak potrebujeme vytvárať dvojrozmerné pole s rôznou dĺžkou riadkov, napr. pre polovicu matice pod diagonálou, potom použijeme definíciu podľa spôsobu poľa xb alebo xd - v našom prípade xb.
int *xx[3];



for (i = 0; i < 3; i++)

	xx[i] = (int *) malloc((i + 1) * sizeof(int));
Tieto príkazy vytvoria v pamäti:

Obrázok

Z dvojrozmerného poľa xx sú potom dostupné prvky:
xx[0][0] xx[1][0] xx[1][1] xx[2][0] xx[2][1] x[2][2]
Poznámka:
Prvok xx[0][1] je syntakticky správny, ale odkazuje do neznámej oblasti pamäti - nie teda na xx[1][0]!

Priklad:
Nasledujúca funkcia umožňuje vytvoriť dynamické dvojrozmerné obdĺžnikové pole prvkov int ľubovolných rozmerov. Pre jednoduchosť sa netestuje úspešnosť pridelenia pamäti:

int **vytvor_pole(int riadky, int stlpce)

{

	int **p_p_x, i;



	p_p_x = (int **) malloc(riadky * sizeof(int *));

	for (i = 0; i < riadky; i++)

		p_p_x[i] = (int *) malloc(stlpce * sizeof(int));



	return(p_p_x);

}



void uvolni_pole(int **p_p_x, int riadky)

{

	int i;

	

	for (i = 0; i < riadky; i++)

		free((void *) p_p_x[i]);



	free((void *) p_p_x);

}

Funkcie vytvor_pole() a uvolni_pole() by sa použili napr.:

int **a, **b;



a = vytvor_pole(3, 11);

b = vytvor_pole(8, 4);

uvolni_pole(a, 3);

uvolni_pole(b, 8);

Pozor:
Ak máme definície:
int **x, yy[5][6];
potom je síce možné napísať:
xx = (int **) yy;
ale premennú xx nie je možné aj napriek tomu používať pre prístup k prvkom poľa yy. Premenná xx nie je správne inicializovaná pre použitie ako pointer pre prístup k dvojrozmernému poľu - nenesie si v sebe informáciu o veľkosťi riadkov. Pokiaľ vás zarazilo, že to v predchádzajúcom prípade u funkcie vytvor_pole() išlo, potom si uvedomte, že tam sa jednalo o pole typu xd, ktoré je dynamické a je zložené z dvoch častí, kdežto tu sa jedná o statické pole.


8.3.6 Dvojrozmerné pole ako parameter funkcie

Dvojrozmerné pole ako skutočný parameter funkcie je úplne rovnaké, ako keď je skutočným parametrom pole jednorozmerné - uvádza sa iba názov poľa. Malá odlišnosť nastáva u formálnych parametrov, kde sa veľkosť prvej dimenzie vynecháva rovnako ako u jednorozmerného poľa (prazdne []), ale druhá dimenzia musí byť uvedená ako konštanta. Z toho, co už o poliach vieme, môžeme usúdiť, že sa odovzdáva pointer na riadok poľa určitého typu a určitej dĺžky.

Z toho vyplýva:
  • Počet riadkov je opät nutné odovzdať ako ďalší parameter funkcie, ale občas sa naviac odovzdáva aj počet stĺpcov.
  • Skutočný parameter môze byť len statické pole typu xa - alebo dynamické pole typu xc, pretože obidve sú "pravouhlé" polia.
Ak máme pole definované ako:
double x[3][4];
potom formálny parameter bude:
double x[][4]
alebo
double (*x)[4]
Pozor:
častou chybou je formálny parameter:
double *x[4]
čo ako už vieme znamená: "x je pole štyroch pointerov na typ double" - a to je niečo iné než požadované: "x je pointer na pole štyroch prvkou typu double".

Príklad:
Nasledujúca funkcia vráti najväčší prvok z dvojrozmerného poľa, ktorého každý riadok má 4 prvky typu double. Všimnite si, že sa na prístup k jednotlivým prvkom používajú len indexy. Prístup pomocou pointerov je samozrejme tiež možný, ale oveľa menej prehľadný.
double maxim(double pole[][4], int riadky) {

double pom = pole[0][0];

int i, j;



for (i = 0; i < riadky; i++) {

	 for (j = 0; j < 4; j++) {

		 if (pole[i][j] > pom)

			 pom = pole[i][j];

	 }

}



return (pom);

}
8.4 Inicializácia polí všetkých rozmerov

Inicializácia polí sa uskutočňuje najčastejšie u reťazcov, ale je samozrejme možné podobne inicializovať aj pole iného typu.
Inicializačné hodnoty sú uzatvorené do zložených zátvoriek, napr:

Jednorozmerné pole:

double f[3] = {1.5, 3.0, 7.6};
Ak nie je uvedený počet prvkov poľa, kompilátor si ho určí sám podľa počtu inicializačných hodnôt.
double f[] = {1.5, 3.0, 7.6};
Ak je uvedený počet prvkov poľa a inicializačných hodnôt je menej, potom zostávajúce prvky budú mať nulovú hodnotu:
double f[3] = {1.5, 3.0};
f[2] bude mat hodnotu 0.0 .

Nie je možné uviesť viac inicializácií, než je prvkov poľa:

double f[3] = {1.5, 3.0, 7.6, 9.2}; /* chybne */
Dvojrozmerné pole sa inicializuje takto:

double f[][2] = {
{1.5, 3.0},
{7.6 ,9.2},
{2.4, 8.7}
};

pričom počet stĺpcov musí byť uvedený a počet riadkov môze byť uvedený.

Príklad:
Niekdy môže byť výhodné zistiť, aké veľké je pole, ktorého veľkosť bola určená inicializáciou. Pozor ale na to, že uvedený postup sa nedá použiť na zistenie veľkosti poľa, ktoré sa odovzdáva ako parameter do funkcie.

#include <stdio.h>

int globalne[] = { 1, 2, 2, 4, 5 {;

int n_glob = sizeof(globalne) / sizeof(int);



int main(void)

{

	char *lokalne[] = { "jeden", "dva", "tri", "styri" };

	int n_lok = sizeof(lokalne) / sizeof(char *);

	int i;



	printf("globalne = %d: ", n_glob);

	for (i = 0; i < n_glob; i++)

		printf(%d, ", globalne[i]);



	printf("\nlokalne = %d: ", n_lok);

	for (i = 0; i < n_lok; i++)

		printf(%d, ", lokalne[i]);

	return 0;

}

8.5 Pole reťazcov

Pole reťazcov je asi najčastejšie využívané dvojrozmerné pole s rôznou dĺžkov jednotlivých riadkov. Takmer každý program pracujúci s textom využíva pole reťazcov. Stojí teda za námahu preniknúť trochu viac do spôsobu práce s poľami reťazcov, pretože je dosť pravdepodobné, že sa s nimi vo svojej praxi stretneme.

Definícia premennej p_text ako poľa štyroch pointerov na reťazce (čo je nám už známe pole typu xb):

char *p_text[4];
Tomuto poľu pointerov môžeme priradiť hodnoty - adresy reťazcov - napr:
p_text[0] = "prvy";

p_text[1] = "druhy";

p_text[2] = (char *) malloc(6);

strcpy(p_text[2],"treti");

p_text[3] = "stvrty";
Po takomto priradení bude situácia v pamäti vyzerať takto:
Obrázok

Jednotlivé znaky z prvého reťazca sa budú čítať z adries:
p_text[0][0] p_text[0][1] p_text[0][2]	atd.
Tlač prvého reťazca (text "prvy") po znakoch pomocou pointeru:
char *p_pom = p_text[0];



while (*p_pom != '\0')

	putchar(*p_pom++);
Tlač druhého reťazca (text "druhy") pomocou printf(), teda naraz celý reťazec:
printf("%s \n",p_text[1]);
Tlač tretieho reťazca (text "treti") pomocou puts(), teda opät naraz celý reťazec:
puts(p_text[2]);
Jednoducho povedané, ak použijeme jeden index, pracujeme naraz s celým riadkom, predstavujúci jeden reťazec. To je pri reťazcoch výhodné, reťazec si vďaka existencii ukončovacieho znaku '\0' vlastne nesie so sebou informáciu o svojej dĺžke.
Ak použijeme obidva indexy, pracujeme s jednotlivými znakmi.

Je teda vidieť, že sa s poľom reťazcov nepracuje žiadnym zvláštnym spôsobom. Veľký pozor je však nutné dávať v prípade, že skúšame využit "neštandardné" postupy pomocou ďalších pointerov, napr.:
char **p_pom = p_text;

puts(++*p_pom);
Tento príkaz vytlačí text "rvy" namiesto očakávaného textu "druhy", pretože *p_pom je pointer na nultý prvok poľa p_text a príkaz:
++*p_pom
zväčšil hodnotu na tejto adrese o 1 * sizeof(char)

Situácia v pamäti teda bude nasledujúca:
Obrázok
p_text[0] teraz ukazuje na podreťazec "rvy" a táto zmena je trvalá!

Ak však použijeme rovnako definovaný pointer, ale iný príkaz, dostaneme očakávané výsledky, pretože tu sa neinkrementuje obsah na adrese *p_pom ale samotný pointer p_pom, napr.:
char **p_pom = p_text;

for (i = 0; i < 4; i++)

	puts(*p_pom++);
V tomto prípade sa vytlačí:
prvy

druhy

treti

stvrty
Poznámka:
Pole reťazcov používané v predchádzajúcom prípade by sme v praxi definovali a inicializovali najskôr takto:

char *p_pole[] = {"prvy", "druhx", "treti", "stvrty"};

kde by všetky položky tohto poľa reťazcov boli statické.


8.6 Parametre funkcie main()

Doposiaľ sme používali funkciu main() ako funkciu bez parametrov a s implicitnou návratovou hodnotou typu int.
Pomocou návratovej hodnoty je možné odovzdať volajúcemu - čo je v prípade funkcie main() operačný systém, ktorý program spustil - výsledok práce programu. Napríklad v MS-DOSe sa takto odovzdaná hodnota zapísala do systémovej premennej ERRORLEVEL, odkiaľ môze byť prečítana napr. v dávkovom súbore. Je však nutné poznamenať, že spôsob využitia návratovej hodnoty ANSI C nijako nedefinuje a záleží vždy na konkrétnom operačnom systéme.

Okrem návratovej hodnoty môže mať funkcia main() tiež formálne parametre. Ich účel je odovzdať programu argumenty zo vstupného príkazového riadku, teda parametre, s ktorými bol spustený program. Tie sa potom stávajú skutočnými parametrami main().
Zvláštnosťou main() oproti iným funkciám je to, že môže mať formálny parameter jeden, dva alebo žiaden (čo sme doteraz používali).
Ak má funkcia main() parametre, sú vždy dva a z historických dôvodov sú pomenované vždy ako argc a argv.

Programy bežne pracujú tak, že umožňujú zadať údaje potrebné na svoje ovládanie (napr. meno súboru s ktorým bude program pracovať) už z príkazového riadku. To má potom výhodu, že program môže byť spustený aj z dávkového súboru (skriptu) a po spustení sa už nepýta na ďalšie informácie. Pokiaľ užívateľ programu nezadá pri spustení programu parametre, vypíše sa buď návod alebo je umožnené zadanie týchto parametrov štandardne z klávesnice.

Ak je program text.exe spustený príkazom:
text param1 param2
a funkcia main() ma hlavičku:
main(int argc, char *argv[])
(tiež sa používa:
main(int argc, char **argv)
)

potom má parameter argc hodnotu 3, pretože udáva počet reťazcov na vstupnom riadku - teda "text" "param1" "param2"
a pole pointerov na reťazce argv ukazuje takto:
argv[0]		 /* na retazec: text */

argv[1]		 /* na retazec: param1 */

argv[2]		 /* na retazec: param2 */
Argument príkazového riadku, ktorý je uzatvorený do úvodzoviek, sa počíta za jeden reťazec. Napríklad po príkaze:

test "ahoj, ako" sa "stale mas"

bude situácia nasledovná:
argc == 4

argv[0] == text

argv[1] == ahoj, ako

argv[2] == sa

argv[3] == stale mas
Poznámka:
Pretože jednotlivé parametre vstupného riadku sú normálne reťazce, je možné pre prácu s nimi využit všetkých funkcii, ktoré s reťazcami pracujú.

Pozor:
Parametre zásadne len čítame. Ak by sme ich chceli meniť, je potrebné si ich ako reťazce prekopírovať (napr. pomocou strcpy()) do reťazcov definovaných v našom programe a stými ďalej pracovať. Napríklad:

char pom_prvy[100];

strcpy(pom_prvy, argv[1]);

Priklad:
Program vypíše počet argumentov príkazoveho riadku vrátane názvu programu. Tieto parametre opíše na nové riadky.

#include <stdio.h>



main(int argc, char *argv[])

{

	int i;



	printf("Prikazovy riadok ma %d parametrov \n", argc);



	for (i = 0; i < argc; i++)

		printf("%s \n", argv[i]);

}

Poznámka:
Ak je parametrom príkazového riadku číslo, je tiež odovzdávané ako reťazec a preto je nutné ho pred použitím previesť na číslo niektorou z funkcií atoi(), atof() alebo sscanf(). Napríklad:
int pocet = atoi(argv[1]);
8.7 Externé pole všetkých rozmerov

Pole môze byť špecifikované ako extern, čo znamená deklaráciu poľa, keď je toto pole definované napr. v inom súbore.
Opäť platí, že prvá dimenzia poľa je uvádzaná dobrovolne, ale vrelo sa odporúča ju pri externých deklaráciach uvádzať, pretože to zvyšuje čitateľnosť programu.

Poznámka:
Filozofickou otázkou však je, či má byť vôbec pole externé, tzn. zdielané viacerými modulmi. Tomuto stavu sa snažíme vyhýbať, pretože pri zdielaní poľa je oveľa väčšia pravdepodobnosť nevhodného zásahu do neho (stačí len fakt, že nie sú kontrolované medze poľa), než pri zdielaní jednoduchej premennej.

Priklad:
Súbor A.C - definícia

int x[10];

float y[2][3];
Súbor B.C - deklarácia
extern int x[10];		 /* alebo horsie		extern int x[];

extern float y[2][3];	 /* alebo horsie		extern float y[][3];
Čo je dobré si uvedomiť:
  • Polia sú v pamäti ukladané po riadkoch.
  • Ak je definícia: int x[2][3]; potom:

		 - typ [i][b]x[/b][/i] je pointer na riadok troch [i][b]int[/b][/i]

		 - typ [i][b]*x[/b][/i] je pointer na [i][b]int[/b][/i]

		 - vo vyraze [i][b]x + i[/b][/i] je[i][b] i[/b][/i] násobené počtom stĺpcov (3)

		 - *x == x[0]

		 - **x == *(x[0]) == x[0][0]

		 - x[i] == *(x + i) == *x + i * počet stĺpcov

Ak je x dvojrozmerné pole, potom ako formálny parameter funkcie musí mať uvedený druhý rozmer pomocou konštanty:
int x[][5]
alebo
int *x[5]
Časté chyby:
f(double b[][])
nie je uvedený druhý rozmer poľa b


Úlohy:

1. Napíš program, ktorý naplní maticu 10 x 10 int číslami 0 - 99 a pomocou pointerov vytlačí jej obsah prehľadne na obrazovku. Pri nastavovaní hodnôt prvkov pristupuj do matice pomocou indexov.
Spoiler

2. Definuj statické pole 5 x 5 int a vypíš adresu začiatku poľa, nultého a prvého riadku, prvku [0][0], [1][0] a posledného prvku. Pomocou sizeof() zisti veľkosť poľa a porovnaj toto číslo s bázovou adresou poľa a adresou jeho posledného prvku. Adresy vypisuj formátom %p alebo %x.
Spoiler

3. Napíš program, ktorý prečíta súbor a pomocou poľa pointerov na char ho uloží do
dynamickej pamäti po riadkoch. Ukladaj len skutočné dĺžky riadkov. Čítaný súbor nebude mať viac
ako 1000 riadkov. Do druhého súboru zapíš prečítaný súbor opačne, teda najskôr posledný riadok
a nakoniec prvý riadok. Pre čítanie celého riadku použi funkciu fgets().
Spoiler

4. Uprav predchádzajúci program tak, že bude na obrazovku vypisovať len riadky, v ktorých sa objaví slovo, ktoré bude zadané z klávesnice. Na hľadanie reťazca využi funkciu srtstr().
Spoiler

5. Uprav predchádzajúci program tak, že vytvorí súbor ODKAZY.TXT, kde ku každému slovu zo súboru SLOVA.TXT pripíše čísla riadkov, na ktorých sa príslušné slovo nachádzalo.
Spoiler

6. Definuj v main() lokálne automatické pole 10 x 10 prvkov typu int a vytvor funkcie napln() a vytlac(). Prvá funkcia toto pole naplní číslami 0 - 99 a druhá ho vytlačí na obrazovku. Pole odovzdávaj do obidvoch funkcií ako skutočný parameter.
Spoiler

7. Napíš program, ktorý vytvorí dynamické pole a dolnú trojuholníkovú maticu. Túto maticu naplň číslami xij, kde xij = i * 10 + j a vytlač ju.
Spoiler

8. Napíš program, ktorý prečíta parametre príkazového riadku a vypíše ich veľkými písmenami.
Spoiler

9. Napíš program, ktorý vypíše na obrazovku súbor, ktorého meno je prvý parameter príkazového riadku. Pokiaľ bude uvedený aj druhý parameter, považuj ho za meno súboru, do ktorého sa bude čítaný súbor kopírovať namiesto výpisu na obrazovku. Ak nebude uvedený žiadny parameter, vypíš prehľadný návod k použitiu.
Spoiler

10. Uprav predchádzajúci program tak, že návod k použitiu sa vypíše, ak bude prvý parameter -h alebo -H (help). Ak nebude zadaný žiadny parameter, program bude vyžadovať zadanie názvu čítaného súboru z klávesnice.
Spoiler

11. Napíš program, ktorý bude v súbore, ktorého názov bude uvedený ako 1. parameter príkazového riadku, hľadať reťazec, ktorý bude zadaný ako 2. parameter. Riadky obsahujúce tento reťazec sa vypíšu na obrazovku. Program bude reagovať aj na nasledujúce znaky, ktoré sa môžu objaviť v ľubovolnom poradí ako 3. parameter príkazového riadku:
c (case sensitive) rozlišuje malé a veľké písmená, inakšie ich považuje za rovnaké
n (numbering) každý vypísaný riadok bude očíslovaný, inakšie sa číslovať nebude

Príklady zadania z príkazového riadku:
11 dopis.txt pointer
11 dopis.txt pointer c
11 dopis.txt pointer n
11 dopis.txt pointer cn // alebo tretí parameter bude nc, výstup bude rovnaký
Spoiler
  • 0

Popis: Viacrozmerné polia

- Základne definície a prístup k prvkom
- Uloženie viacrozmerných polí v pamäti
- Rôzne spôsoby definície dvojrozmerných polí
- * Statické dvojrozmerné pole
- * Pole pointerov
- * Pointer na pole
- * Pointer na pointer
- * Výhody a nevýhody predchádzajúcich štyroch spôsobov
- * Dvojrozmerné pole ako parameter funkcie
- Inicializácia polí všetkých rozmerov
- Pole reťazcov
- Parametre funkcie main()
- Externé pole všetkých rozmerov
- Úlohy k lekcii
Poznámky:
Odkazy:


0 Komentárov


Najlepšie lekcie


Najnovšie pridané lekcie


Najnovšie komentáre


Najviac komentované lekcie


Najviac zobrazené lekcie


Náhodné lekcie


Na tejto stránke bolo užívateľ(ov) za posledných 30 minút

členov, návštevníkov