nter++;              /* poschitat' simvol                   */
	return counter;                 /* skol'ko simvolov, otlichnyh ot '\0' */
}

	Tut nikakih ogranichenij net. Imenno etot podhod i byl izbran
	v yazyke Si, hotya v principe mozhno samomu pol'zovat'sya i drugimi.
	Na samom dele v yazyke est' takaya STANDARTNAYA funkciya strlen(s)
	(vam ne nado pisat' ee samomu, ee uzhe napisali za vas).
---------------------------------------------------------------------
INICIALIZACIYA GLOBALXNOGO MASSIVA
=================================
Massiv, zadannyj vne kakih-libo funkcij, mozhno proinicializirovat'
konstantnymi nachal'nymi znacheniyami:

int array[5]   = { 12, 23, 34, 45, 56 };

char string[7] = { 'P', 'r', 'i', 'v', 'e', 't', '\0' };

Esli razmer massiva ukazan BOLXSHE, chem my perechislim elementov,
to ostal'nye elementy zapolnyatsya nulyami (dlya int) ili '\0' dlya char.

int array[5] = { 12, 23, 34 };

Esli my perechislim bol'she elementov, chem pozvolyaet razmer massiva -
eto budet oshibkoj.


int a[5] = { 177, 255, 133 };

Operaciya indeksacii massiva a[] daet:

pri n           znachenie vyrazheniya a[n] est'
--------------------------------------------
-1              ne opredeleno (oshibka: "indeks za granicej massiva")
0               177
1               255
2               133
3               0
4               0
5               ne opredeleno (oshibka)
         * 13_FUNCS.txt * 
KAK PROISHODIT VYZOV FUNKCII
============================

Pust' u nas opisana funkciya, vozvrashchayushchaya celoe znachenie.

	/* OPREDELENIE FUNKCII func(). */
	/* Gde func - ee imya. Nazvat' my ee mozhem kak nam ugodno. */

	int func(int a, int b, int c){
		int x, y;

		...
		x = a + 7;
		...
		b = b + 4;
		...

		return(nekoe_znachenie);
	}

Zdes'
	a, b, c    - argumenty funkcii (parametry)
	x, y       - lokal'nye peremennye

Tochka vyzova - nahoditsya vnutri kakoj-to drugoj
funkcii, naprimer funkcii main()

main(){

	int zz, var;
	...
	var = 17;
	zz = func(33, 77, var + 3) + 44;
	...
}

Kogda vypolnenie programmy dohodit do stroki

	zz = func(33, 77, var + 3) + 44;

1) Proishodit VYZOV FUNKCII func()

	(a)     |tot punkt my uvidim nizhe.

	(b)     Sozdayutsya peremennye s imenami a, b, c, x, y;

	(c)     Peremennym-argumentam prisvaivayutsya nachal'nye znacheniya,
		kotorye berutsya iz tochki vyzova.
		V tochke vyzova perechislen spisok (cherez zapyatuyu) vyrazhenij (formul):

			func(vyrazhenie1, vyrazhenie2, vyrazhenie3)

		Vychislennye znacheniya etih vyrazhenij sootvetstvenno budut prisvoeny
		1-omu, 2-omu i 3-emu argumentam (parametram) iz opredeleniya funkcii:

			int func(a, b, c){      /* a = nomer 1, b = 2, c = 3 */

		Pervyj parametr:

			a = 33;

		Vtoroj parametr:

			b = 77;

		Tretij parametr:

			c = var + 3;

		to est', vychislyaya,

			c = 20;

		Lokal'nye peremennye x i y soderzhat neopredelennye znacheniya,
		to est' musor (my ne mozhem predskazat' ih znacheniya,
		poka ne prisvoim im yavnym obrazom kakoe-libo znachenie sami).

2) Vypolnyaetsya TELO funkcii, to est' vychisleniya, zapisannye vnutri { ... }
   v opredelenii funkcii. Naprimer:

			x = a + 7;

	I parametry, i lokal'nye peremennye - eto PEREMENNYE,
	to est' ih mozhno izmenyat'.

			b = b + 4;

	Pri etom nikakie peremennye VNE etoj funkcii ne izmenyayutsya.
	(Ob etom eshche raz pozzhe).


3) Proizvoditsya VOZVRAT iz funkcii.

		...
		return(nekoe_znachenie);
	}


	Naprimer, eto mozhet byt'

		...
		return(a + 2 * x);
	}

	Rassmotrim, chto pri etom proishodit v tochke vyzova:

		zz = func(33, 77, var + 3) + 44;

	(1) Vycherkivaem func(.....)

		zz = XXXXXXX + 44;

	(2) Vychislyaem znachenie "nekoe_znachenie" v operatore return,
	    i berem KOPIYU etogo znacheniya.
	    Pust' pri vychislenii tam poluchilos' 128.

	(3) Podstavlyaem eto znachenie na mesto vycherknutogo func(.....)
	    U nas poluchaetsya

		zz = 128 + 44;

	(4) AVTOMATICHESKI UNICHTOZHAYUTSYA lokal'nye peremennye i argumenty funkcii:

		a       - ubito
		b       - ubito
		c       - ubito
		x       - ubito
		y       - ubito

	Takih peremennyh (i ih znachenij) bol'she net v prirode.

	(5) Punkt, kotoryj my obsudim pozzhe.

	(6) Prodolzhaem vychislenie:

		zz = 128 + 44;

	Vychislyaetsya v

		zz = 172;       /* operator prisvaivaniya */

-------------------------------------------------------------------------

int func1(int x){
	printf("func1: x=%d\n", x);     /* 1 */
	x = 77;
	printf("func1: x=%d\n", x);     /* 2 */
	return x;
}

void main(){
	int var, y;

	var = 111;
	y = func1(var);                 /* @ */

	printf("main: var=%d\n", var);  /* 3 */
}

V dannom sluchae v tochke @ my peredaem v funkciyu func1()
ZNACHENIE peremennoj var, ravnoe 111.
|to znachit, chto pri vyzove funkcii budet sozdana peremennaya x
i ej budet prisvoeno nachal'noe znachenie 111

	x = 111;

Poetomu pervyj operator printf() napechataet 111.

Zatem my izmenyaem znachenie peremennoj x na 77.
My menyaem peremennuyu x, no ne peremennuyu var !!!
Ispol'zovav ZNACHENIE (ego kopiyu) iz peremennoj var dlya x,
my o peremennoj var zabyli - ona nas ne kasaetsya (a my - ee).

Poetomu vtoroj operator printf() napechataet 77.
V peremennoj zhe var ostalos' znachenie 111,
chto i podtverdit nam tretij operator printf,
kotoryj napechataet 111.

-------------------------------------------------------------------------
VREMENNOE SOKRYTIE PEREMENNYH
=============================

int func1(int x){                       /* f.1 */
	printf("func1: x=%d\n", x);     /* f.2 */
	x = 77;                         /* f.3 */
	printf("func1: x=%d\n", x);     /* f.4 */
	return x;                       /* f.5 */
}

void main(){
	int x, y;                       /* 1 */

	x = 111;                        /* 2 */
	y = func1(x);                   /* 3 */

	printf("main: x=%d y=%d\n", x, y);  /* 4 */
}

A teper' my i peremennuyu vnutri main(), i argument funkcii
func1() nazvali odnim i tem zhe imenem. CHto budet?

Budet to zhe samoe, chto v predydushchem primere.

V moment vyzova funkcii func1() budet sozdana NOVAYA peremennaya
s imenem x, a staraya (prezhnyaya) peremennaya i ee znachenie budut
VREMENNO SPRYATANY (skryty).

Mozhno bylo by utochnit' eti peremennye imenami funkcij,
v kotoryh oni opredeleny:

	main::x

	   i

	func1::x

(no eto uzhe konstrukcii iz yazyka Si++, a ne Si).

Vypolnim programmu po operatoram:

|/* 1    */      Otvodyatsya peremennye main::x i main::y dlya celyh chisel;
|/* 2    */      main::x = 111;
|/* 3    */      Vyzyvaetsya func1(111);
|
+-------+
.       |/* f.1  */      Otvoditsya peremennaya func1::x so znacheniem 111;
.       |/* f.2  */      Pechataetsya 111 iz peremennoj func1::x;
.       |
.       |/* f.3  */      func1::x = 77; (eto ne main::x, a drugaya peremennaya,
.       |                                LOKALXNAYA dlya funkcii func1.
.       |                                Peremennuyu main::x my sejchas ne vidim -
.       |                                ona "zaslonena" imenem nashej lokal'noj
.       |                                peremennoj.
.       |                                Poetomu my ne mozhem ee izmenit').
.       |
.       |/* f.4  */      Pechataet 77 iz func1::x;
.       |/* f.5  */      Vozvrashchaet znachenie func1::x , to est' 77.
.       |                Peremennaya func1::x unichtozhaetsya.
.       |
.       |                Teper' my snova vozvrashchaemsya v funkciyu main(),
.       |                gde imya x oboznachaet peremennuyu main::x
.       |                a ne func1::x
+-------+
|
|/* 3    */      y = 77;
|/* 4    */      Pechataet znacheniya main::x i main::y, to est'
|                111 i 77.


|tot mehanizm sokrytiya imen pozvolyaet pisat' funkcii main() i func1()
raznym programmistam, pozvolyaya im NE ZABOTITXSYA o tom, chtoby imena
lokal'nyh peremennyh v funkciyah NE SOVPADALI. Pust' sovpadayut - huzhe ne
budet, mehanizm upryatyvaniya imen razreshit konflikt.
Zato programmist mozhet ispol'zovat' lyuboe ponravivsheesya emu imya
v lyuboj funkcii - hotya by i x, ili i.

-------------------------------------------------------------------------
To zhe samoe proishodit s lokal'nymi peremennymi,
a ne s argumentami funkcii.

int func1(int arg){     /* lokal'naya peremennaya-parametr func1::arg */
	int x;          /* lokal'naya peremennaya          func1::x   */

	x = arg;
	printf("func1: x=%d\n", x);
	x = 77;
	printf("func1: x=%d\n", x);
	return x;
}

void main(){
	int x, y;       /* peremennye main::x i main::y */

	x = 111;
	y = func1(x);

	printf("main: x=%d y=%d\n", x, y);
}

Dejstvuet tot zhe samyj mehanizm vremennogo sokrytiya imeni x.
Voobshche zhe, argumenty funkcii i ee lokal'nye peremennye
otlichayutsya tol'ko odnim:
	argumentam avtomaticheski prisvaivayutsya
	nachal'nye znacheniya, ravnye znacheniyam sootvetstvuyushchih vyrazhenij
	v spiske

		imya_funkcii(..., ..., ....)
			    arg1 arg2 arg3

	v meste vyzova funkcii.

To est'

OPISANIE FUNKCII:

int f(int arg1, int arg2, int arg3){
	int perem1, perem2;
	...
	/* prodolzhenie */
}

VYZOV:

	.... f(vyrazhenie1, vyrazhenie2, vyrazhenie3) ...

TO V TELE FUNKCII VYPOLNITSYA (v moment ee vyzova):

	arg1 = vyrazhenie1;
	arg2 = vyrazhenie2;
	arg3 = vyrazhenie3;

	perem1 = MUSOR;
	perem2 = MUSOR;

	...
	/* prodolzhenie */

-------------------------------------------------------------------------
GLOBALXNYE PEREMENNYE
=====================

Nakonec, sushchestvuyut peremennye, kotorye ob®yavlyayutsya VNE VSEH FUNKCIJ,
i sushchestvuyushchie vse vremya vypolneniya programmy
(a ne tol'ko to vremya, kogda aktivna funkciya, v kotoroj oni sozdany).

Lokal'nye peremennye i argumenty UNICHTOZHAYUTSYA pri vyhode
iz funkcii. Global'nye peremennye - net.

int x = 12;             /* ::x - ej mozhno zaranee prisvoit' konstantu */
int globvar;            /* ::globvar                                  */

int f1(){
	int x;          /* f1::x */

	x = 77;
	printf("x=%d\n", x);            /* 4 */
	return x;
}

int f2(){
	printf("x=%d\n", x);            /* 5 */
	return 0;
}

void main(){
	int x, y;       /* main::x */

	x = 111;                         /* 1 */
	printf("x=%d\n", x);             /* 2 */
	printf("glob=%d\n", globvar);    /* 3 */

	y = f1();
	y = f2();
}

V dannom primere my vidim:
- vo-pervyh my vidim FUNKCII BEZ PARAMETROV. |to normal'naya situaciya.
- vo-vtoryh tut ispol'zuyutsya TRI peremennye s imenem "x".

Kak vypolnyaetsya programma?

/* 1 */   main::x = 111;
	  |to lokal'nyj x, a ne global'nyj.
	  Global'nyj x poprezhnemu soderzhit 12.

/* 2 */   Napechataet znachenie peremennoj main::x, to est' 111.
	  Vnutri funkcii main global'naya peremennaya ::x
	  zaslonena svoej sobstvennoj peremennoj x.
	  V dannom sluchae NET SPOSOBA dobrat'sya iz main k global'noj
	  peremennoj x, eto vozmozhno tol'ko v yazyke Si++ po imeni ::x

	  K peremennoj zhe globvar u nas dostup est'.

/* 3 */   Pechataet ::globvar. My obnaruzhivaem, chto ee znachenie 0.
	  V otlichie ot global'nyh peremennyh,
	  kotorye iznachal'no soderzhat MUSOR,
	  global'nye peremennye iznachal'no soderzhat znachenie 0.

	  V ramochku, podcherknut'.

/* 4 */   Pri vyzove f1()
	  peremennaya f1::x
		zaslonyaet soboj kak
		     main::x
		tak i
		     ::x

	  V dannom sluchae napechataetsya 77,
	  no ni ::x ni main::x ne budut izmeneny operatorom x = 77.
	  |to izmenyalas' f1::x

/* 5 */   Pri vyzove f2() istoriya interesnee.
	  Tut net svoej sobstvennoj peremennoj x.
	  No kakaya peremennaya pechataetsya tut -
		::x     ili
		main::x   ?

	  Otvet: ::x
		 to est' 12.

	  Peremennye nazvany lokal'nymi eshche i potomu,
	  chto oni NEVIDIMY V VYZYVAEMYH FUNKCIYAH.

	  |to OPREDELENIE lokal'nyh peremennyh.
	  (Poetomu ne sprashivajte "pochemu?" Po opredeleniyu)

	  To est', esli my imeem

		funca(){
			int vara;
			...
			...funcb();...  /* vyzov */
			...
		}

	  to iz funkcii funcb() my NE IMEEM DOSTUPA K PEREMENNOJ vara.

		funcb(){
			int z;

			z = vara + 1;   /* oshibka,
					   vara neizvestna vnutri funcb() */
		}

	  Esli, v svoyu ochered', funcb() vyzyvaet funcc(),
	  to i iz funcc() peremennaya vara nevidima.

	  Ostanovites' i osoznajte.
	  |to pravilo sluzhit vse toj zhe celi - raznye funkcii
	  mogut byt' napisany raznymi programmistami, kotorye mogut
	  ispol'zovat' odni i te zhe imena dlya RAZNYH peremennyh,
	  ne boyas' ih vzaimoperesecheniya.
	  Mnozhestva imen, ispol'zovannyh v raznyh funkciyah, nezavisimy
	  drug ot druga. Imena iz odnoj funkcii NIKAK ne otnosyatsya
	  k peremennym s temi zhe imenami IZ DRUGOJ funkcii.

	  Vernemsya k paragrafu KAK PROISHODIT VYZOV FUNKCII
	  i rassmotrim punkt (a). Teper' on mozhet byt' opisan kak

	(a) Lokal'nye peremennye i argumenty vyzyvayushchej funkcii delayutsya nevidimymi.
					     ~~~~~~~~~~
	  A pri vozvrate iz funkcii:

	(5) Lokal'nye peremennye i argumenty vyzyvayushchej funkcii snova delayutsya vidimymi.

	  ODNAKO global'nye peremennye vidimy iz LYUBOJ funkcii,
	  isklyuchaya sluchaj, kogda global'naya peremennaya zaslonena
	  odnoimennoj lokal'noj peremennoj dannoj funkcii.

-------------------------------------------------------------------------
PROCEDURY
=========
Byvayut funkcii, kotorye ne vozvrashchayut nikakogo znacheniya.
Takie funkcii oboznachayutsya void ("pustyshka").
Takie funkcii nazyvayut eshche PROCEDURAMI.

	void func(){
		printf("Privetik!\n");
		return;  /* vernut'sya v vyzyvayushchuyu funkciyu */
	}

Takie funkcii vyzyvayutsya radi "pobochnyh effektov",
naprimer pechati strochki na ekran ili izmeneniya global'nyh (i tol'ko)
peremennyh.

	int glob;

	void func(int a){
		glob += a;
	}

Operator return tut neobyazatelen, on avtomaticheski vypolnyaetsya
pered poslednej skobkoj }

Vyzov takih funkcij ne mozhet byt' ispol'zovan
v operatore prisvaivaniya:

main(){
	int z;

	z = func(7);     /* oshibka, a chto my prisvaivaem ??? */
}

Korrektnyj vyzov takov:

main(){
	func(7);
}

Prosto vyzov i vse.

        ZACHEM FUNKCII?

CHtoby vyzyvat' ih s raznymi argumentami!

	int res1, res2;
		...

	res1 = func(12 * x * x + 177, 865,     'x');
	res2 = func(432 * y + x, 123 * y - 12, 'z');

Kstati, vy zametili, chto spisok fakticheskih parametrov
sleduet cherez zapyatuyu;
i vyrazhenij rovno stol'ko, skol'ko parametrov u funkcii?

Funkciya opisyvaet POSLEDOVATEXNOSTX DEJSTVIJ,
kotoruyu mozhno vypolnit' mnogo raz,
no s raznymi ishodnymi dannymi (argumentami).
V zavisimosti ot dannyh ona budet vydavat' raznye rezul'taty,
no vypolnyaya odni i te zhe dejstviya.

V tom to i sostoit ee prelest':
my ne dubliruem odin kusok programmy mnogo raz,
a prosto "vyzyvaem" ego.

Funkciya - abstrakciya ALGORITMA, to est' posledovatel'nosti dejstvij.
Ee konkretizaciya - vyzov funkcii s uzhe opredelennymi parametrami.
-------------------------------------------------------------------------
Operator return mozhet nahodit'sya ne tol'ko v konce funkcii,
no i v ee seredine.
On vyzyvaet nemedlennoe prekrashchenie tela funkcii i vozvrat znacheniya
v tochku vyzova.

	int f(int x){
		int y;

		y = x + 4;
		if(y > 10) return (x - 1);
		y *= 2;
		return (x + y);
	}

        REKURSIVNYE FUNKCII. STEK

Rekursivnoj nazyvaetsya funkciya, vyzyvayushchaya sama sebya.

	int factorial(int arg){
		if(arg == 1)
			return 1;                               /* a */
		else
			return arg * factorial(arg - 1);        /* b */
	}

|ta funkciya pri vyzove factorial(n) vychislit proizvedenie

	n * (n-1) * ... * 3 * 2 * 1

nazyvaemoe "faktorial chisla n".
V dannoj funkcii peremennaya arg budet otvedena (a posle i unichtozhena) MNOGO RAZ.

Tak chto peremennaya factorial::arg dolzhna poluchit' eshche i NOMER vyzova funkcii:

	factorial::arg[uroven'_vyzova]

I na kazhdom novom urovne novaya peremennaya skryvaet vse predydushchie.
Tak dlya factorial(4) budet


		+----------------------------------------+
     |          | factorial::arg[ 0_oj_raz ]  est' 4     |
     |          | factorial::arg[ 1_yj_raz ]  est' 3     |
     |          | factorial::arg[ 2_oj_raz ]  est' 2     |
     |          | factorial::arg[ 3_ij_raz ]  est' 1     |
     V  --------+                                        +---------

Zatem pojdut vozvraty iz funkcij:

		+----------------------------------------+
     A          | /* b */ return 4 * 6 = 24              |
     |          | /* b */ return 3 * 2 = 6               |
     |          | /* b */ return 2 * 1 = 2               |
     |          | /* a */ return 1;                      |
     |  --------+                                        +---------

Takaya konstrukciya nazyvaetsya STEK (stack).

	--------+               +------------
		|               |                       pustoj stek
		+---------------+

	Polozhim v stek znachenie a
			|
	--------+       |       +------------
		|       V       |
		+---------------+
		|       a       | <--- vershina steka
		+---------------+

	Polozhim v stek znachenie b

	--------+               +------------
		|               |
		+---------------+
		|       b       | <--- vershina steka
		+---------------+
		|       a       |
		+---------------+

	Polozhim v stek znachenie c

	--------+               +------------
		|               |
		+---------------+
		|       c       | <--- vershina steka
		+---------------+
		|       b       |
		+---------------+
		|       a       |
		+---------------+

	Analogichno, znacheniya "snimayutsya so steka" v obratnom poryadke: c, b, a.

	V kazhdyj moment vremeni u steka dostupno dlya chteniya (kopirovaniya) ili
	vybrasyvaniya tol'ko dannoe, nahodyashcheesya na VERSHINE steka.

	Tak i v nashej rekursivnoj funkcii peremennaya factorial::arg
	vedet sebya imenno kak stek (etot yashchik-stek imeet imya arg) -
	ona imeet ODNO imya, no raznye znacheniya v raznyh sluchayah.
	Peremennye, kotorye AVTOMATICHESKI vedut sebya kak stek,
	vstrechayutsya tol'ko v (rekursivnyh) funkciyah.

	Stek - eto chasto vstrechayushchayasya v programmirovanii konstrukciya.
	Esli podobnoe povedenie nuzhno programmistu, on dolzhen promodelirovat'
	stek pri pomoshchi massiva:

	int stack[10];
	int in_stack = 0;       /* skol'ko elementov v steke */

	/* Zanesenie znacheniya v stek */
	void push(int x){
		stack[in_stack] = x;
		in_stack++;
	}

	/* Snyat' znachenie so steka */
	int pop(){
		if(in_stack == 0){
			printf("Stek pust, oshibka.\n");
			return (-1);
		}
		in_stack--;
		return stack[in_stack];
	}

	Obratite v nimanie, chto net nuzhdy STIRATX (naprimer obnulyat')
	znacheniya elementov massiva, vykinutyh s vershiny steka.
	Oni prosto bol'she ne ispol'zuyutsya, libo zatirayutsya novym znacheniem pri
	pomeshchenii na stek novogo znacheniya.

	void main(){
		push(1);
		push(2);
		push(3);

		while(in_stack > 0){
			printf("top=%d\n", pop());
		}
	}

        STEK I FUNKCII

Budem rassmatrivat' kazhdyj VYZOV funkcii kak pomeshchenie v special'nyj stek
bol'shogo "bloka informacii", vklyuchayushchego v chastnosti
ARGUMENTY I LOKALXNYE PEREMENNYE vyzyvaemoj funkcii.

Operator return iz vyzvannoj funkcii vytalkivaet so steka VESX takoj blok.

V kachestve primera rassmotrim takuyu programmu:

int x = 7;      /* global'naya */
int v = 333;    /* global'naya */

int factorial(int n){
	int w;  /* lishnyaya peremennaya, tol'ko dlya demonstracionnyh celej */

	w = n;

	if(n == 1)      return 1;                       /* #a */
	else            return n * factorial(n-1);      /* #b */
}
void func(){
	int x;          /* func::x */

	x = 777;        /* #c */
	printf("Vyzvana funkciya func()\n");
}
void main(){
	int y = 12;     /* main::y */
	int z;

				/* A */
	z = factorial(3);       /* B */
	printf("z=%d\n", z);

	func();                 /* C */
}
-------------------------------------------------------------------------
Vypolnenie programmy nachnetsya s vyzova funkcii main().
V tochke /* A */

				|    v  z  g  l  ya  d   |
				V                       V

	--------+                       +--------
		|=======================|
		|       z = musor       |
		|       y = 12          |           +------+---------+
		|main()                 |           |x = 7 | v = 333 |
		+-----------------------+-----------+------+---------+-----
		 STEK VYZOVOV FUNKCIJ                GLOBALXNYE PEREMENNYE


V kazhdyj dannyj moment vidimy peremennye, kotorye nahodyatsya
a) na vershine (i tol'ko) steka vyzovov funkcij.
b) i nezaslonennye imi global'nye peremennye.
V dannom sluchae my smotrim "sverhu" i vidim:

	main::z,  main::y,  ::x,  ::v

-------------------------------------------------------------------------
V tochke /* B */ my vyzyvaem factorial(3).


	--------+                       +--------
		|=======================|
		|       w = musor       |
		|       n = 3           |
		|factorial(3)           |
		|=======================|
		|                     +-|---> tekushchij operator z = factorial(3);
		|       z = musor       |
		|       y = 12          |           +------+---------+
		|main()                 |           |x = 7 | v = 333 |
		+-----------------------+-----------+------+---------+-----
		 STEK VYZOVOV FUNKCIJ                GLOBALXNYE PEREMENNYE

Pri novom vzglyade vidimy:
	factorial(3)::w,  factorial(3)::n,  ::x,  ::v

Stali nevidimy:
	main::z,  main::y

Stroka "tekushchij operator ..." ukazyvaet mesto, s kotorogo nado vozobnovit'
vypolnenie funkcii, kogda my vernemsya v nee.
-------------------------------------------------------------------------
Kogda vypolnenie programmy v funkcii factorial(3) dojdet do tochki
/* #b */ budet vypolnyat'sya return 3 * factorial(2).
V itoge budet vyzvana funkciya factorial(2).

	--------+                       +--------
		|=======================|
		|       w = musor       |
		|       n = 2           |
		|factorial(2)           |
		|=======================|
		|                     +-|---> tekushchij operator return 3 * factorial(2);
		|       w = 3           |
		|       n = 3           |
		|factorial(3)           |
		|=======================|
		|                     +-|---> tekushchij operator z = factorial(3);
		|       z = musor       |
		|       y = 12          |           +------+---------+
		|main()                 |           |x = 7 | v = 333 |
		+-----------------------+-----------+------+---------+-----
		 STEK VYZOVOV FUNKCIJ                GLOBALXNYE PEREMENNYE


-------------------------------------------------------------------------
Kogda vypolnenie programmy v funkcii factorial(2) dojdet do tochki
/* #b */ budet vypolnyat'sya return 2 * factorial(1).
V itoge budet vyzvana funkciya factorial(1).

	--------+                       +--------
		|=======================|
		|       w = musor       |
		|       n = 1           |
		|factorial(1)           |
		|=======================|
		|                     +-|---> tekushchij operator return 2 * factorial(1);
		|       w = 2           |
		|       n = 2           |
		|factorial(2)           |
		|=======================|
		|                     +-|---> tekushchij operator return 3 * factorial(2);
		|       w = 3           |
		|       n = 3           |
		|factorial(3)           |
		|=======================|
		|                     +-|---> tekushchij operator z = factorial(3);
		|       z = musor       |
		|       y = 12          |           +------+---------+
		|main()                 |           |x = 7 | v = 333 |
		+-----------------------+-----------+------+---------+-----
		 STEK VYZOVOV FUNKCIJ                GLOBALXNYE PEREMENNYE

-------------------------------------------------------------------------
Zatem v factorial(1) vypolnenie programmy dojdet do tochki /* #a */
i budet proizvodit'sya return 1.

Pri return vycherkivaetsya ODIN blok informacii so steka vyzovov funkcij,
i vozobnovlyaetsya vypolnenie "tekushchego operatora" v funkcii,
stavshej NOVOJ vershinoj steka vyzovov.
Zamet'te, chto v dannoj situacii vyzvannye funkcii factorial(m) eshche ne zavershilis'.
V nih eshche ESTX chto sdelat': vychislit' vyrazhenie v return,
i sobstvenno vypolnit' sam return. Vychisleniya budut prodolzheny.

	--------+                       +--------
		|=======================|
		|                     +-|---> tekushchij operator return 1;
		|       w = 1           |
		|       n = 1           |
		|factorial(1)           |
		|=======================|
		|                     +-|---> tekushchij operator return 2 * factorial(1);
		|       w = 2           |
		|       n = 2           |
		|factorial(2)           |
		|=======================|
		|                     +-|---> tekushchij operator return 3 * factorial(2);
		|       w = 3           |
		|       n = 3           |
		|factorial(3)           |
		|=======================|
		|                     +-|---> tekushchij operator z = factorial(3);
		|       z = musor       |
		|       y = 12          |           +------+---------+
		|main()                 |           |x = 7 | v = 333 |
		+-----------------------+-----------+------+---------+-----
		 STEK VYZOVOV FUNKCIJ                GLOBALXNYE PEREMENNYE

-------------------------------------------------------------------------
Nachinaetsya vytalkivanie funkcij so steka i vypolnenie operatorov return;

	--------+                       +--------
		|=======================|
		|                     +-|---> tekushchij operator return 2 * 1;
		|       w = 2           |
		|       n = 2           |
		|factorial(2)           |
		|=======================|
		|                     +-|---> tekushchij operator return 3 * factorial(2);
		|       w = 3           |
		|       n = 3           |
		|factorial(3)           |
		|=======================|
		|                     +-|---> tekushchij operator z = factorial(3);
		|       z = musor       |
		|       y = 12          |           +------+---------+
		|main()                 |           |x = 7 | v = 333 |
		+-----------------------+-----------+------+---------+-----
		 STEK VYZOVOV FUNKCIJ                GLOBALXNYE PEREMENNYE


	--------+                       +--------
		|=======================|
		|                     +-|---> tekushchij operator return 3 * 2;
		|       w = 3           |
		|       n = 3           |
		|factorial(3)           |
		|=======================|
		|                     +-|---> tekushchij operator z = factorial(3);
		|       z = musor       |
		|       y = 12          |           +------+---------+
		|main()                 |           |x = 7 | v = 333 |
		+-----------------------+-----------+------+---------+-----
		 STEK VYZOVOV FUNKCIJ                GLOBALXNYE PEREMENNYE


	--------+                       +--------
		|=======================|
		|                     +-|---> tekushchij operator z = 6;
		|       z = musor       |
		|       y = 12          |           +------+---------+
		|main()                 |           |x = 7 | v = 333 |
		+-----------------------+-----------+------+---------+-----
		 STEK VYZOVOV FUNKCIJ                GLOBALXNYE PEREMENNYE


	--------+                       +--------
		|=======================|
		|       z = 6           |
		|       y = 12          |           +------+---------+
		|main()                 |           |x = 7 | v = 333 |
		+-----------------------+-----------+------+---------+-----
		 STEK VYZOVOV FUNKCIJ                GLOBALXNYE PEREMENNYE

-------------------------------------------------------------------------
Nakonec, v tochke /* C */ budet vyzvana funkciya func().
Rassmotrim tochku /* #c */ v nej.

	--------+                       +--------
		|=======================|
		|       x = 777;        |
		|func();                |
		|=======================|
		|                     +-|---> tekushchij operator func();
		|       z = 6           |
		|       y = 12          |           +------+---------+
		|main()                 |           |x = 7 | v = 333 |
		+-----------------------+-----------+------+---------+-----
		 STEK VYZOVOV FUNKCIJ                GLOBALXNYE PEREMENNYE


V dannom meste nas interesuet - kakie peremennye vidimy?
Vidimy:
		func::x = 777
		::v     = 333

I vse.
		::x                zaslonen lokal'noj peremennoj.
		main::y,  main::z  nevidimy, tak kak nahodyatsya
				   ne na vershine steka vyzovov funkcij
-------------------------------------------------------------------------
Mnogie funkcii bolee estestvenno vyrazhayutsya cherez rekursiyu.
Hotya, chasto eto privodit k izlishnim vychisleniyam po sravneniyu s iteraciyami
(to est' ciklami). Vot primer - chisla Fibonachchi.

int fibonacci(int n){
	if(n==1 || n==2) return 1;

	/* else */

	return fibonacci(n-1) + fibonacci(n-2);
}
void main(){
	printf("20oe chislo Fibonachchi ravno %d\n", fibonacci(20));
}

Poskol'ku tut otsutstvuet massiv dlya zapominaniya promezhutochnyh
rezul'tatov, to etot massiv na samom dele neyavno modeliruetsya
v vide lokal'nyh peremennyh vnutri steka vyzovov funkcij.
Odnako etot sposob ploh - v nem slishkom mnogo povtoryayushchihsya
dejstvij. Dobavim operator pechati - i poschitaem, skol'ko raz
byla vyzvana fibonacci(1) ?

int called = 0;

int fibonacci(int n){
	if(n==1){
		called++;
		return 1;

	} else  if(n==2)
		return 1;

	return fibonacci(n-1) + fibonacci(n-2);
}
void main(){
	printf("20oe chislo Fibonachchi ravno %d\n", fibonacci(20));
	printf("fibonacci(1) byla vyzvana %d raz\n", called);
}

Ona byla vyzvana... 2584 raza!
        14.c
/* Risuem hitruyu geometricheskuyu figuru */
#include <stdio.h>

const int LINES = 15;

void draw(int nspaces, int nstars, char symbol){
	int i;

	for(i=0; i < nspaces; i++)
		putchar(' ');
	for(i=0; i < nstars; i++)
		putchar(symbol);
}

void main(){
	int nline, nsym;
	char symbols[3];        /* Massiv iz treh bukv */

	symbols[0] = '\\';
	symbols[1] = 'o';
	symbols[2] = '*';

	for(nline=0; nline < LINES; nline++){
		for(nsym = 0; nsym < 3; nsym++)
			draw(nline, nline, symbols[nsym]);

		/* Perehod na novuyu stroku vynesen
		   iz funkcii v glavnyj cikl */
		putchar('\n');
	}
}
        15.c
/* Zadacha:
	narisovat' tablicu vida

	kot     kot     kot     koshka   koshka   kot
	kot     kot     koshka   koshka   kot     ...

   Gde idet posledovatel'nost'
	kot, kot, kot, koshka, koshka...
   povtoryayushchayasya mnogo raz i raspolagaemaya po 6 zverej v stroku.
*/

#include <stdio.h>      /* magicheskaya stroka */

/* Ob®yavlenie global'nyh peremennyh.
   V dannom sluchae - konstant.
 */

int TOMCATS     = 3;            /* tri kota  */
int CATS        = 2;            /* dve koshki */
int ANIMALS_PER_LINE    = 6;    /* shest' zverej v kazhdoj stroke */
int LINES       = 25;           /* chislo vyvodimyh strok */

/* i nam ponadobitsya eshche odna peremennaya - obshchee chislo zverej.
   Ee my vychislim cherez uzhe zadannye, poetomu tut my ee ob®yavim...
   no vychislit' chto-libo mozhno tol'ko vnutri funkcii.
   V nashem sluchae - v funkcii main().
*/
int ANIMALS;                    /* obshchee chislo zverej */

int nth_in_line = 0;            /* nomer zverya v tekushchej stroke */
/* |ta peremennaya ne mozhet byt' lokal'noj v funkcii, tak kak
 * togda ona unichtozhitsya pri vyhode iz funkcii. Nam zhe neobhodimo,
 * chtoby ee znachenie sohranyalos'. Poetomu peremennaya - global'naya.
 */

/* Funkciya, kotoraya schitaet chislo zverej v odnoj stroke
   i libo perevodit stroku, libo perevodit pechat' v
   sleduyushchuyu kolonku (tabulyaciej).
*/
void checkIfWeHaveToBreakLine(){
	nth_in_line++;  /* tekushchij nomer zverya v stroke (s edinicy) */

	if(nth_in_line == ANIMALS_PER_LINE){
	/* Esli stroka zapolnena zver'mi... */
		putchar('\n');     /* novaya stroka */
		nth_in_line = 0;   /* v novoj stroke net zverej */
	} else {
		putchar('\t');     /* v sleduyushchuyu kolonku */
	}
}

void main(){
	int nanimal;    /* nomer zverya */
	int i;          /* schetchik     */

	ANIMALS = ANIMALS_PER_LINE * LINES;
	nanimal = 0;

	while(nanimal < ANIMALS){

		for(i=0; i < TOMCATS; i++){
			/* Operator printf() vyvodit STROKU SIMVOLOV.
			   STROKA zaklyuchaetsya v dvojnye kavychki
			   (ne putat' s odinochnymi dlya putchar().
			 */
			printf("kot");
			nanimal++;      /* poschitat' eshche odnogo zverya */

			/* i proverit' - ne nado li perejti na novuyu stroku ? */
			checkIfWeHaveToBreakLine();
		}
		for(i=0; i < CATS; i++){
			printf("koshka");
			nanimal++;      /* poschitat' eshche odnogo zverya */

			/* i proverit' - ne nado li perejti na novuyu stroku ? */
			checkIfWeHaveToBreakLine();
		}
	}
	/* putchar('\n'); */
}
        16.c
/*
	Ta zhe zadacha, no eshche nado pechatat' nomer kazhdogo zverya.
	Ogranichimsya pyat'yu strokami.
*/

#include <stdio.h>      /* magicheskaya stroka */

int TOMCATS     = 3;            /* tri kota  */
int CATS        = 2;            /* dve koshki */
int ANIMALS_PER_LINE    = 6;    /* shest' zverej v kazhdoj stroke */
int LINES       = 5;            /* chislo vyvodimyh strok */
int ANIMALS;                    /* obshchee chislo zverej */

int nth_in_line = 0;            /* nomer zverya v tekushchej stroke */

void checkIfWeHaveToBreakLine(){
	nth_in_line++;

	if(nth_in_line == ANIMALS_PER_LINE){
		putchar('\n');
		nth_in_line = 0;
	} else
		printf("\t\t");         /* @ */

	/* Odinokij operator mozhet obojtis' bez {...} vokrug nego */
}

void main(){
	int nanimal;
	int i;

	ANIMALS = ANIMALS_PER_LINE * LINES;
	nanimal = 0;

	while(nanimal < ANIMALS){

		for(i=0; i < TOMCATS; i++){
		/* Format %d vyvodit znachenie peremennoj tipa int
		   v vide tekstovoj stroki.
		   Sama peremennaya dolzhna byt' v spiske posle formata
		   (spisok - eto perechislenie peremennyh cherez zapyatuyu).
		   Peremennyh ILI vyrazhenij (formul).

		   Davajte vyvodit' po DVE tabulyacii --
		   eto mesto otmecheno v funkcii checkIfWeHaveToBreakLine()
		   kak @.

		   Eshche raz vnimanie - odin simvol my vyvodim kak
			putchar('a');
		   Neskol'ko simvolov - kak
			printf("abcdef");

		   Odinochnye kavychki - dlya odnoj bukvy.
		   Dvojnye kavychki   - dlya neskol'kih.
		*/

			printf("kot%d", nanimal);
			nanimal++;

			checkIfWeHaveToBreakLine();
		}
		for(i=0; i < CATS; i++){
			printf("koshka%d", nanimal);
			nanimal++;

			checkIfWeHaveToBreakLine();
		}
	}
}
        17.c
/* Zadacha: napechatat' korni iz chisel ot 1 do 100.

   Novaya informaciya:
	Nam ponadobitsya novyj tip dannyh - DEJSTVITELXNYE CHISLA.
	|to chisla, imeyushchie drobnuyu chast' (posle tochki).
	Kak my uzhe znaem, celye - eto   int.
			  bukva - eto   char.
			  dejstvitel'noe chislo - eto double.
	(est' eshche slovo float, no im pol'zovat'sya ne rekomenduetsya).

   Dlya vychisleniya kornya ispol'zuetsya iteracionnyj algoritm Gerona.

		q = koren' iz x;

		q[0]   := x;
		q[n+1] := 1/2 * ( q[n] + x/q[n] );

   Glavnoe tut ne vpast' v oshibku, ne klyunut' na q[n] i ne
   zavesti massiv. Nam ne nuzhny rezul'taty kazhdoj iteracii,
   nam nuzhen tol'ko konechnyj otvet. Poetomu nam budet vpolne
   dostatochno ODNOJ, no izmenyayushchejsya v cikle, yachejki q.
*/

#include <stdio.h>

/* Eshche odno novoe klyuchevoe slovo - const. Oboznachaet konstanty.
   V otlichie ot peremennyh, takie imena nel'zya izmenyat'.
   To est', esli gde-to potom poprobovat' napisat' epsilon = ... ;
   to eto budet oshibkoj.
 */
const double epsilon = 0.0000001;       /* tochnost' vychislenij */

/* Funkciya vychisleniya modulya chisla */
double doubleabs(double x){
	if(x < 0) return -x;
	else      return x;
}

/* Funkciya vychisleniya kvadratnogo kornya */
double sqrt(double x){

	double sq = x;

	/* Takaya konstrukciya est' prosto sklejka dvuh strok:
		double sq;
		sq = x;
	   Nazyvaetsya eto "ob®yavlenie peremennoj s inicializaciej".
	 */

	while(doubleabs(sq*sq - x) >= epsilon){
		sq = 0.5 * (sq + x/sq);
	}
	return sq;
}

void main() {
	int n;

	for(n=1; n <= 100; n++)
		printf("sqrt(%d)=%lf\n",
			     n,  sqrt((double) n)
		      );

}

/*
   Zdes' v operatore printf() my pechataem DVA vyrazheniya.
	FORMAT          ZNACHENIE
	------          --------
	%d      --      n
	%lf     --      sqrt((double) n)

	Po formatu %d pechatayutsya znacheniya tipa int.
	Po formatu %c pechatayutsya znacheniya tipa char.
	Po formatu %lf (ili %g) pechatayutsya znacheniya tipa double.

   CHto znachit "napechatat' znachenie vyrazheniya sqrt(xxx)" ?
   |to znachit:
	- vyzvat' funkciyu sqrt() s argumentom, ravnym xxx;
	- vychislit' ee;
	- vozvrashchennoe eyu znachenie napechatat' po formatu %lf,
	  to est' kak dejstvitel'noe chislo.

   Zamet'te, chto tut vozvrashchaemoe znachenie NE prisvaivaetsya
   nikakoj peremennoj, my ne sobiraemsya ego hranit'.

   Tochno tak zhe, kak v operatore x = 12 + 34;
   12 i 34 ne hranyatsya ni v kakih peremennyh,
   a operator

	printf("%d\n", 12);

   pechataet CHISLO 12, a ne peremennuyu.

   Tochno tak zhe, kak mozhno pisat'

	double z;

	z = sqrt(12) + sqrt(23);

   gde znachenie, vychislennoe kazhdoj funkciej, NE hranitsya
   v svoej sobstvennoj peremennoj (takaya peremennaya na samom
   dele sushchestvuet v komp'yutere, no programmistu ona ne
   nuzhna i nedostupna).
   Ili

	z = sqrt( sqrt(81));

	(koren' iz kornya iz 81 --> dast 3)

   Dalee, chto oznachaet konstrukciya (double) n   ?
   Funkciya sqrt() trebuet argumenta tipa double.
   My zhe predlagaem ej celyj argument

	int n;

   Celye i dejstvitel'nye chisla predstavleny v pamyati
   mashiny PO-RAZNOMU,
   poetomu chisla

	12   i   12.0  hranyatsya v pamyati PO-RAZNOMU.

   Mashina umeet preobrazovyvat' celye chisla v dejstvitel'nye
   i naoborot, nado tol'ko skazat' ej ob etom.
   Operator (double) x
   nazyvaetsya "privedenie tipa k double".

   Zametim, chto chasto preobrazovanie tipa
   vypolnyaetsya avtomaticheski.

   Tak, naprimer, pri slozhenii int i double
   int avtomaticheski privoditsya k double, i rezul'tat
   imeet tip double.

	int    var1;
	double var2, var3;

	var1 = 2;
	var2 = 2.0;
	var3 = var1 + var2;

   chto oznachaet na samom dele

	var3 = (double) var1 + var2;

   var3 stanet ravno 4.0

   Bolee togo, k primeru tip char - eto tozhe CELYE CHISLA iz intervala
   0...255. Kazhdaya bukva imeet kod ot 0 do 255.

*/
         * 18_POINTERS.txt * 
UKAZATELI
=========
void f(int x){
	x = 7;
}

main(){
	int y = 17;
	f(y);
	printf("y=%d\n", y);       /* pechataet: y=17 */
}

V argumente x pereda£tsya KOPIYA znacheniya y,
poetomu x=7; ne izmenyaet znacheniya u.
Kak vse zhe sdelat', chtoby vyzyvaemaya funkciya
mogla izmenyat' znachenie peremennoj?

Otbrosim dva sposoba:

	- ob®yavlenie y kak global'noj
	  (mnogo global'nyh peremennyh - plohoj stil'),

	- y=f(y);
	  (a chto esli nado izmenit' MNOGO peremennyh?
	  return, k neschast'yu, mozhet vernut' lish' odno znachenie).

Ispol'zuetsya novaya dlya nas konstrukciya: UKAZATELX.
--------------------------------------------------
Primer (@)

void f(int *ptr){       /* #2 */
	*ptr = 7;       /* #3 */
}

main (){
	int y=17;

	f(&y);          /* #1 */
	printf("y=%d\n", y);       /* pechataet: y=7 */
}

Nu kak, nashli tri otlichiya ot ishodnogo teksta?
----------------------------------------------------------------------
My vvodim dve novye konstrukcii:

	&y              "ukazatel' na peremennuyu y" ili
			"adres peremennoj y"

	*ptr            oznachaet "razymenovanie ukazatelya ptr"
			(podrobnee - pozzhe)

	int *ptr;       oznachaet ob®yavlenie peremennoj ptr,
			kotoraya mozhet soderzhat' v sebe
			ukazatel' na peremennuyu,
			hranyashchuyu int-chislo.


Dlya nachala opredelim, chto takoe ukazatel'.

	int var1, var2, z;      /* celochislennye peremennye */
	int *pointer;           /* ukazatel' na celochislennuyu peremennuyu */

	var1    = 12;
	var2    = 43;
	pointer = &var1;

My budem izobrazhat' ukazatel' v vide STRELKI;
eto horoshij priem i pri prakticheskom programmirovanii.
	________
       /pointer/
     _/_______/_
     | znachenie|        Peremennaya, hranyashchaya ukazatel'
     |   est'  |        (adres drugoj peremennoj)
     |         |
     |  &var1  |
     |         |
     |_______|_|
	     |
	     |&var1     - sam ukazatel' na var1 -
	     |            "strelka, ukazyvayushchaya na peremennuyu var1".
	     V            Ona oboznachaetsya &var1
	________
       / var1  /
     _/_______/_
     | znachenie|
     |   est'  |
     |    12   |
     |_________|

Takim obrazom, UKAZATELX - eto "strelka, ukazyvayushchaya na nekij yashchik-peremennuyu".
Nachalo etoj strelki mozhno (v svoyu ochered') hranit' v kakoj-nibud' peremennoj.

Pri etom, esli strelka ukazyvaet na peremennuyu tipa int,
to tip peremennoj, hranyashchej nachalo strelki, est' int *

Esli tipa char, to tip - char *

ADRES (ukazatel' na) mozhno vzyat' tol'ko ot peremennoj ili elementa massiva,
no ne ot vyrazheniya.

	int x;
	int arr[5];

	Zakonno     &x          strelka na yashchik "x"
		    & arr[3]    strelka na yashchik "arr[3]"

	Nezakonno   &(2+2)      tut net imenovannogo "yashchika",
				na kotoryj ukazyvaet strelka,
				da i voobshche yashchika net.

        ISPOLXZOVANIE UKAZATELEJ

Ukazateli neskol'ko razlichno vedut sebya SLEVA i SPRAVA
ot operatora prisvaivaniya.
Nas interesuet novaya operaciya, primenyaemaya tol'ko k ukazatelyam:

	*pointer

--------------------------------------------------------------------
SPRAVA ot prisvaivanij i v formulah
===================================
*pointer        oznachaet
		"vzyat' znachenie peremennoj (lezhashchee v yashchike),
		na kotoruyu ukazyvaet ukazatel',
		hranyashchijsya v peremennoj pointer".

V nashem primere - eto chislo 12.

To est' *pointer oznachaet "projti po strelke i vzyat' ukazyvaemoe eyu ZNACHENIE".

	printf("%d\n", *pointer);

Pechataet 12;

	z = *pointer;        /* ravnocenno z = 12;        */
	z = *pointer + 66;   /* ravnocenno z = 12 + 66;   */

Zastavim teper' ukazatel' ukazyvat' na druguyu peremennuyu
(inache govorya, "prisvoim ukazatelyu adres drugoj peremennoj")

	pointer = &var2;

	________
       /pointer/
     _/_______/_
     |         |
     |  &var2  |
     |         |
     |_______|_|
	     |
	     |&var2
	     |
	     V
	________
       / var2  /
     _/_______/_
     |         |
     |   43    |
     |         |
     |_________|

Posle etogo   z = *pointer;
oznachaet      z = 43;

--------------------------------------------------------------------
Takim obrazom, konstrukciya

	z = *pointer;

	    oznachaet

	z = *(&var2);

	    oznachaet

	z = var2;

To est' * i & vzaimno STIRAYUTSYA.

        SLEVA ot prisvaivaniya...

	*pointer = 123;

Oznachaet        "polozhit' znachenie pravoj chasti (t.e. 123)
		 v peremennuyu (yashchik), na kotoryj