ukazyvaet ukazatel', hranyashchijsya v peremennoj pointer". Projti po strelke i polozhit' znachenie v ukazyvaemuyu peremennuyu. V dannom sluchae *pointer oboznachaet ne ZNACHENIE ukazyvaemoj peremennoj, a SAMU ukazyvaemuyu peremennuyu. ________ /pointer/ _/_______/_ | | | &var2 | | | |_______|_| | |Polozhit' tuda 123 | V ________ / var2 / _/_______/_ | | | 123 | | | |_________| pointer = &var2; *pointer = 123; oznachaet *(&var2) = 123; oznachaet var2 = 123; To est' snova * i & vzaimno STIRAYUT drug druga. -------------------------------------------------------------------- Eshch£ primer: *pointer = *pointer + 66; ili *pointer += 66; -------------------------------------------------------------------- Vernemsya k primeru s funkciej (@). Kak on rabotaet? V stroke /* #1 */ My vyzyvaem funkciyu f(), peredavaya v nee UKAZATELX na peremennuyu y ("adres peremennoj y"). V stroke /* #2 */ Otvoditsya lokal'naya peremennaya s imenem ptr, kotoraya v kachestve nachal'nogo znacheniya poluchaet znachenie pervogo argumenta funkcii v tochke vyzova - to est' UKAZATELX na y. V stroke /* #3 */ My vidim *ptr = 7; chto sleduet rassmatrivat' kak *(&y) = 7; tochnee *(&main::y)=7; to est' kak y = 7; tochnee main::y=7; CHto i hotelos'. Pri etom otmetim, chto samo imya "y" etoj peremennoj vnutri funkcii f() NEVIDIMO i NEIZVESTNO! -------------------------------------------------------------------- PRIMER: obmen znachenij dvuh peremennyh. void main(){ int x, y; int temporary; /* vspomogatel'naya peremennaya */ x=1; y=2; temporary=x; x=y; y=temporary; printf("x=%d y=%d\n", x, y); /* Pechataet x=2 y=1 */ } ----------------------------------------------------------------------- Teper' to zhe s ispol'zovaniem adresov i ukazatelej: void swap(int *a, int *b){ int tmp; tmp = *a; *a = *b; *b = tmp; } void main(){ int x, y; x = 1; y = 2; swap(&x, &y); printf("x=%d y=%d\n", x, y); } ------------------------------------------------------------------------- Eshch£ primer: int x; int *ptr1, *ptr2; ptr1 = &x; ptr2 = &x; *ptr1 = 77; printf("%d\n", *ptr2); /* Pechataet 77 */ To est' na odnu peremennuyu MOGUT ukazyvat' neskol'ko ukazatelej. ------------------------------------------------------------------------- Eshch£ primer: int x; int *ptr1; /* Ne inicializirovana */ x = *ptr1; V ptr1 net ukazatelya ni na chto, tam est' musor. Ukazatel' ukazyvaet "v nikuda" (pal'cem v nebo). Skoree vsego proizojd£t sboj v rabote programmy. Moral': VSEGDA inicializiruj peremennye, ukazateli v tom chisle. MASSIVY YAzyk Si rabotaet s imenami massivov special'nym obrazom. Imya massiva "a" dlya int a[5]; yavlyaetsya na samom dele ukazatelem na ego nulevoj element. To est' u nas est' peremennye (yashchiki) s imenami a[0] a[1] ... a[4]. Pri etom samo imya a pri ego ispol'zovanii v programme oznachaet &a[0] a | | | V a[0] a[1] a[2] a[3] a[4] _________________________________________ | | | | | | | | | | | | | | | | | | ----------------------------------------- Poetomu int a[5]; /* Pereda£tsya ne KOPIYA samogo massiva, a kopiya UKAZATELYA na ego nachalo */ void f(int *a){ /* ili f(int a[]), chto est' ravnocennaya zapis' */ printf("%d\n", a[1]); a[2] = 7; } main (){ a[1] = 777; f(a); /* argument - massiv */ printf("%d\n", a[2]); } Vyzov f(a); sdelaet imenno ozhidaemye veshchi. V etom primere my vidim dva pravila: PRAVILO_1: Pri peredache v funkciyu imeni massiva v argument funkcii kopiruetsya ne ves' massiv (zhirnovato budet), a ukazatel' na ego 0-oj element. PRAVILO_2: Ukazatel' na nachalo massiva MOZHNO indeksirovat' kak sam massiv. |to vtoraya operaciya, pomimo *pointer, primenimaya k ukazatelyam: pointer[n]. Vtoroe pravilo vlechet za soboj ryad sledstvij. int a[5]; /* massiv */ int *ptr; /* ukazatel'naya peremennaya */ ptr = a; /* zakonno, oznachaet ptr = &a[0]; */ Teper' ptr[0] = 3; /* oznachaet a[0] = 3; */ ptr[1] = 5; /* oznachaet a[1] = 5; */ Bolee togo. Voz'mem teper' ptr = &a[2]; a[0] a[1] a[2] a[3] a[4] _________________________________________ | | | | | | a: | | | | | | | | | | | | ---------------------------------------------- | | | | ... ptr: | | | | ----------------------------- -2 -1 ptr[0] ptr[1] ptr[2] My kak by "prilozhili" k massivu a[] massiv ptr[]. V kotorom ptr[0] est' a[2] ptr[1] est' a[3] ptr[2] est' a[4] ptr[3] nahoditsya za koncom massiva a[], MUSOR Bolee togo, dopustimy otricatel'nye indeksy! ptr[-1] est' a[1] ptr[-2] est' a[0] ptr[-3] nahoditsya pered nachalom massiva a[], MUSOR Itak: indeksirovat' mozhno I massivy I ukazateli. Kstati, dlya imeni massiva a[] *a oznachaet to zhe samoe, chto i a[0]. |to obratnoe sledstvie iz shozhesti massivov i ukazatelej. 19.c /* Zadacha: napisat' funkciyu invertirovaniya poryadka simvolov v massive char. A B C D ---> D C B A V reshenii mozhno ispol'zovat' rekursiyu. */ /* My privedem rekursivnoe i nerekursivnoe resheniya (dva varianta) */ #include <stdio.h> /* Snachala - neskol'ko sluzhebnyh funkcij. */ /* FUNKCIYA PODSCHETA DLINY STROKI. Kak uzhe ob®yasnyalos', stroka teksta - eto massiv char, v konce kotorogo pomeshchen simvol '\0'. Sam simvol \0 ne schitaetsya. */ int strlen(char s[]){ /* funkciya ot massiva bukv */ int counter = 0; /* schetchik i odnovremenno indeks */ while(s[counter] != '\0') /* poka ne vstretilsya priznak konca teksta */ counter++; /* poschitat' simvol */ return counter; /* skol'ko simvolov, otlichnyh ot '\0' */ } /* FUNKCIYA PECHATI STROKI. Pechataem kazhdyj element massiva kak simvol pri pomoshchi putchar(c). Kak tol'ko vstrechaem element massiva, ravnyj '\0' - ostanavlivaemsya. Zamet'te, chto pri nalichii zavershayushchego simvola nam NE NADO peredavat' v funkciyu razmer massiva, on nam neinteresen. V konce eta funkciya perevodit stroku. */ int putstr(char s[]){ int i = 0; /* indeks */ while(s[i] != '\0'){ putchar(s[i]); i++; } putchar('\n'); return i; } /* TEPERX MY ZANIMAEMSYA FUNKCIEJ INVERTIROVANIYA. Dlya etogo nam nuzhna vspomogatel'naya funkciya: sdvig elementov massiva na 1 vlevo. Ishodnyj massiv: A B C D E F <---------- Rezul'tat: B C D E F F - Poslednij element udvaivaetsya. n - razmer massiva. Funkciya rabotaet tak: Ishodnyj massiv: A B C D E F n=6 Posle i=1 B B C D E F Posle i=2 B C C D E F Posle i=3 B C D D E F Posle i=4 B C D E E F Posle i=5 B C D E F F i=6 ==> ostanovka. */ void shiftLeft(char s[], int n){ int i; for(i=1; i < n; i++) s[i-1] = s[i]; } /* Funkciya invertirovaniya. Ideya takova: - esli dlina massiva men'she ili ravna 1, to invertirovat' nechego, ibo massiv sostoit iz 1 ili 0 elementov. - esli dlina massiva > 1, to a) Spasti nulevoj element massiva. A B C D E F | | V tmp b) Sdvinut' massiv vlevo B C D E F F c) V poslednij element massiva pomestit' spasennyj nulevoj element. tmp | V B C D E F A d) Invertirovat' nachalo massiva dlinoj n-1. {B C D E F}A Poluchitsya: F E D C B A CHto i trebovalos'. s[] - massiv, n - ego dlina. */ void reverse(char s[], int n){ char tmp; if(n <= 1) /* nechego invertirovat' */ return; tmp = s[0]; /* spasti */ shiftLeft(s, n); /* sdvinut' */ s[n-1] = tmp; /* peremestit' */ reverse(s, n-1); /* invertirovat' nachalo */ } /* VTORAYA VERSIYA nerekursivna. Rekursiya zamenena ciklom. Dlina nachala massiva, kotoruyu nado invertirovat', vynesena na peremennuyu length. */ void reverse1(char s[], int n){ char tmp; int length; for(length=n; length > 1; --length){ tmp = s[0]; shiftLeft(s, length); s[length-1] = tmp; } } char testString[] = "abcdefghijklmnopqrstuvwxyz"; /* Esli ne zadat' razmer massiva, on budet vychislen kompilyatorom avtomaticheski. On budet raven chislu bukv vnutri "..." PLYUS odna yachejka dlya nevidimogo simvola '\0' na konce. V dannom sluchae eto 27. */ void main(){ int len; len = strlen(testString); /* vychislit' dlinu stroki: 26 ('\0' na konce ne schitaetsya) */ printf("Stroka dlya testa: \"%s\", ee dlina %d\n", testString, len); /* Obratite vnimanie na dva momenta: - stroku (massiv char) sleduet pechatat' po formatu %s - chtoby vnutri "..." napechatat' simvol " nado izobrazit' ego kak \" A chtoby v putchar napechatat' simvol ' nado pisat' putchar('\''); */ /* Pervaya inversiya */ reverse(testString, len); putstr("Invertirovannaya stroka:"); putstr(testString); /* Vtoraya inversiya - vozvrashchaet v ishodnoe sostoyanie */ reverse1(testString, len); putstr("Invertirovannaya v ishodnoe sostoyanie stroka:"); putstr(testString); } 19_1.c /* Eshche bolee prostoj variant resheniya: prosto obmenivat' elementy mestami. A B C D E F G H I J J B C D E F G H I A | | eti J B C D E F G H I A J I C D E F G H B A | | potom eti J I C D E F G H B A J I H D E F G C B A | | potom eti ----> <----- J I H D E F G C B A J I H G E F D C B A | | J I H G E F D C B A | | J I H G F E D C B A stop. */ #include <stdio.h> /* Obmen znachenij dvuh peremennyh tipa char */ void swap(char *s1, char *s2){ char c; c = *s1; *s1 = *s2; *s2 = c; } void reverse(char s[], int n){ int first, last; first = 0; /* indeks pervogo elementa massiva */ last = n-1; /* indeks poslednego elementa massiva */ while(first < last){ /* poka first levee last */ swap(&s[first], &s[last]); first++; /* sdvinut' vpravo */ last--; /* sdvinut' vlevo */ } } char testString[] = "abcdefghijklmnopqrstuvwxyz."; void main(){ int len; len = strlen(testString); /* Est' takaya standartnaya funkciya */ reverse(testString, len); printf("Invertirovannaya stroka: %s\n", testString); } 19_2.c /* Eshche odin variant resheniya: sformirovat' otvet v dopolnitel'nom massive, a potom skopirovat' ego na prezhnee mesto. */ #include <stdio.h> char testString[] = "abcdefghijklmnopqrstuvwxyz."; /* Konstrukciya sizeof(massiv)/sizeof(massiv[0]) vydaet razmer massiva, dazhe esli on ne byl yavno ob®yavlen. |ta konstrukciya primenyaetsya (chashche vsego) dlya zadaniya massiva s razmerom, ravnym razmeru uzhe ob®yavlennogo massiva. */ char tempString[ sizeof(testString) / sizeof(testString[0]) ]; void reverse(char s[], int n){ int i; /* vyvernut', rezul'tat v tempString[] */ for(i=0; i < n; i++) tempString[n-1-i] = s[i]; tempString[n] = '\0'; /* priznak konca stroki */ /* skopirovat' na staroe mesto */ for(i=0; i < n; i++) s[i] = tempString[i]; s[n] = '\0'; /* priznak konca stroki */ } void main(){ int len; len = strlen(testString); /* Est' takaya standartnaya funkciya */ reverse(testString, len); printf("Invertirovannaya stroka: %s\n", testString); } 19_3.c /* Zadacha invertirovaniya massiva celyh chisel */ #include <stdio.h> int arr[] = {1, 5, 10, 15, 20, 25, 30}; int arrLen = sizeof(arr) / sizeof(arr[0]); /* razmer massiva */ /* Raspechatka massiva v stroku */ void printit(int row[], int n){ int i; for(i=0; i < n; i++){ printf("%d", row[i]); if(i == n-1) putchar('\n'); else putchar(' '); } } /* Pechat' otstupa. Otladochnaya funkciya */ void printShift(int n){ n = arrLen - n; while(n > 0){ printf(" "); n--; } } /* Sdvig massiva */ void shiftleft(int row[], int n){ int i; for(i=1; i < n; i++) row[i-1] = row[i]; } /* Invertirovanie */ void reverse(int row[], int n){ int pocket; printShift(n); /* trassirovka */ printf("CALLED reverse(row, %d)\n", n); /* trassirovka */ if(n <= 1){ printShift(n); /* trassirovka */ printf("return from reverse(row, %d);\n", n); /* trassirovka */ return; } pocket = row[0]; shiftleft(row, n); row[n-1] = pocket; printShift(n); /* trassirovka */ printit(arr, arrLen); /* trassirovka */ reverse(row, n-1); printShift(n); /* trassirovka */ printf("all done; return from reverse(row, %d);\n", n); /* trassirovka */ } void main(){ reverse(arr, arrLen); printit(arr, arrLen); } 20.c /* Zadacha: napisat' funkciyu dlya raspechatki massiva celyh chisel v vide tablicy v columns stolbcov. Pri etom poryadok elementov dolzhen byt' takov: 0 4 8 1 5 9 2 6 10 3 7 */ /* Pust' v massive n elementov. Esli n < columns, to my poluchaem takuyu situaciyu (n=2, columns=4) 0 1 pusto pusto Poetomu if(n < columns) columns = n; Dalee, pryamougol'naya tablica s columns stolbcami i lines strokami mozhet soderzhat' maksimum columns*lines elementov. Poetomu: columns*lines >= n Vychislim chislo strok. Nam nuzhno minimal'noe celoe chislo strok, takoe chto lines >= n/columns Takoe chislo vychislyaetsya po formule lines = (n + (columns - 1)) / columns; gde delenie celochislennoe. Dalee nado tol'ko vyvesti formulu dlya indeksa elementa v massive v zavisimosti ot nomera stroki (y) i stolbca (x). index(x, y) = (x * lines + y); prichem esli index >= n, to nichego ne vyvodit' */ #include <stdio.h> int array[100]; void printArray(int a[], int n, int columns){ int lines; /* chislo strok */ int x, y; /* nomer kolonki, nomer stroki - s nulya */ int index; /* indeks elementa v massive */ if(n < columns) columns = n; lines = (n + (columns-1)) / columns; /* Ispol'zuem vlozhennye cikly: po strokam, a vnutri - po stolbcam */ for(y=0; y < lines; y++){ for(x=0; x < columns; x++){ index = x * lines + y; if(index >= n) /* element za koncom massiva */ break; /* prervat' stroku */ /* break vyvodit tol'ko iz vnutrennego cikla (po stolbcam) */ /* sdelat' otstup v sleduyushchuyu kolonku */ if(x != 0) putchar('\t'); printf("%02d|%d", index, a[index]); /* Format %02d zastavlyaet pechatat' celoe chislo s ispol'zovaniem DVUH cifr, prichem esli chislo sostoit iz odnoj cifry, to speredi pripisyvaetsya nul'. */ } putchar('\n'); /* perejti k sleduyushchej stroke */ } } void main(){ int i, cols; /* Inicializaciya znachenij elementov massiva */ for(i=0; i < 100; i++) array[i] = i + 1; for(cols=4; cols <= 13; cols++){ printf("\t\t* * * TABLICA V %d STOLBCOV * * *\n", cols); printArray(array, 77, cols); putchar('\n'); } } 20_1.c #include <stdio.h> main(){ int x, y; int COLUMNS = 11; int LINES = 10; int value; /* cikl po strokam */ for(y=0; y < LINES; y++){ /* cikl po stolbcam */ for(x=0; x < COLUMNS; x++){ /* chto napechatat' */ value = LINES * x + y; /* esli eto ne nulevoj stolbec, to perejti v sleduyushchuyu kolonku */ if(x > 0) putchar('\t'); /* ... i v nej napechatat' znachenie */ printf("%d", value); } putchar('\n'); /* novaya stroka */ } } 20_2.c /* elem(x, y) = LINES * x + y; togda elem(0, y+1) - elem(COLUMNS-1, y) = 1 + LINES - COLUMNS*LINES; elem(x+1, y) - elem(x, y) = LINES; */ #include <stdio.h> int A = 150; /* Kolichestvo elementov */ int COLUMNS = 7; /* Kolichestvo stolbcov */ int LINES; /* Kolichestvo strok */ int value; /* Znachenie v tekushchej kletke */ int OFFSET_NEXT_COLUMN; int OFFSET_NEXT_LINE; /* Risuem stroku tablicy */ void line(){ int col; /* nomer kolonki */ for(col=0; col < COLUMNS; col++){ if(value >= A) /* otsutstvuyushchij element */ printf("* "); else printf("%03d ", value); /* Uvelichenie pri perehode v sosednyuyu kolonku */ value += OFFSET_NEXT_COLUMN; /* 1 */ } /* Perejti k sleduyushchej stroke */ putchar('\n'); /* Uvelichenie pri perehode iz konca odnoj stroki k nachalu sleduyushchej. Zametim, chto k value uzhe pribavleno OFFSET_NEXT_COLUMN iz tochki 1, poetomu pri perehode v nachalo sleduyushchej stroki v summe pribavlyaetsya OFFSET_NEXT_COLUMN + OFFSET_NEXT_LINE ravnoe 1 - LINES*COLUMNS + LINES, chto sootvetstvuet formule. */ value += OFFSET_NEXT_LINE; /* 2 */ } int main(){ int nline; /* Nomer stroki */ LINES = (A + (COLUMNS - 1)) / COLUMNS; OFFSET_NEXT_COLUMN = LINES; OFFSET_NEXT_LINE = 1 - LINES*COLUMNS; for(nline=0; nline < LINES; nline++) line(); /* vozvrat 0 iz main() oznachaet "programma zavershena uspeshno" */ return 0; } 21.c /* DVUMERNYE MASSIVY */ /* Dvumernyj massiv predstavlyaet soboj dvumernuyu pryamougol'nuyu tablicu iz numerovannyh peremennyh. On ob®yavlyaetsya tak: int array[LINES][COLUMNS]; A indeksiruetsya tak: array[y][x] gde 0 <= y <= LINES - 1 0 <= x <= COLUMNS - 1 +-------------+-------------+-------------+------> os' x | array[0][0] | array[0][1] | array[0][2] | ... +-------------+-------------+-------------+ | array[1][0] | array[1][1] | array[1][2] | ... +-------------+-------------+-------------+ | array[2][0] | array[2][1] | array[2][2] | ... +-------------+-------------+-------------+ | ... ... ... V os' y Poka, na dannoj stadii znaniya Si, ya rekomenduyu vam ob®yavlyat' dvumernye massivy kak global'nye i ne pytat'sya peredavat' ih imena v funkcii kak argumenty. */ /* Privedem primer, kotoryj zavodit dvumernyj massiv bukv, risuet v nem nekuyu geometricheskuyu figuru, i pechataet etot massiv. Zdes' my privodim algoritm Brezenhema dlya risovaniya pryamyh, ob®yasneniya KAK on eto delaet my opustim. Pardon. */ #define LINES 31 /* chislo strok */ #define COLUMNS 79 /* chislo stolbcov */ char field[LINES][COLUMNS]; /* V dannoj programme massiv NE yavlyaetsya parametrom, my rabotaem s nim kak s global'noj peremennoj. Funkciya risovaniya pryamoj linii, algoritm Brezenhema. */ void line(int x1, int y1, int x2, int y2, char sym){ int dx, dy, i1, i2, i, kx, ky; int d; /* "otklonenie" */ int x, y; int flag; dy = y2 - y1; dx = x2 - x1; if (dx == 0 && dy == 0){ field[y1][x1] = sym; /* edinstvennaya tochka */ return; } kx = 1; /* shag po x */ ky = 1; /* shag po y */ /* Vybor taktovoj osi */ if( dx < 0 ){ dx = -dx; kx = -1; } /* Y */ else if(dx == 0) kx = 0; /* X */ if(dy < 0) { dy = -dy; ky = -1; } if(dx < dy){ flag = 0; d = dx; dx = dy; dy = d; } else flag = 1; i1 = dy + dy; d = i1 - dx; i2 = d - dx; x = x1; y = y1; for(i=0; i < dx; i++){ field[y][x] = sym; /* narisovat' tochku */ if(flag) x += kx; /* shag po takt. osi */ else y += ky; if( d < 0 ) /* gorizontal'nyj shag */ d += i1; else{ /* diagonal'nyj shag */ d += i2; if(flag) y += ky; /* prirost vysoty */ else x += kx; } } field[y][x] = sym; /* poslednyaya tochka */ } int main(){ int x, y; /* Zapolnit' pole probelami */ for(y=0; y < LINES; y++) for(x=0; x < COLUMNS; x++) field[y][x] = ' '; /* Narisovat' kartinku */ line(0,0, 0, LINES-1, '*'); line(0,0, COLUMNS-1, 0, '*'); line(COLUMNS-1, 0, COLUMNS-1, LINES-1, '*'); line(0, LINES-1, COLUMNS-1, LINES-1, '*'); line(0,0, COLUMNS-1, LINES-1, '\\'); line(COLUMNS-1,0, 0,LINES-1, '/'); /* Raspechatat' massiv */ for(y=0; y < LINES; y++){ for(x=0; x < COLUMNS; x++) putchar(field[y][x]); putchar('\n'); } return 0; }