1 / 20

WSKAŹNIKI: Definicja: Wskaźnik jest zmienna, która zawiera adres innej zmiennej

WSKAŹNIKI: Definicja: Wskaźnik jest zmienna, która zawiera adres innej zmiennej Niech x będzie zmienną typu int, a wx wskaźnikiem (nie jest ważny w tej chwili sposób utworzenia tego skaźnika). Jendoargumentowy operator &

kapono
Download Presentation

WSKAŹNIKI: Definicja: Wskaźnik jest zmienna, która zawiera adres innej zmiennej

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. WSKAŹNIKI: Definicja: Wskaźnik jest zmienna, która zawiera adres innej zmiennej Niech x będzie zmienną typu int, a wx wskaźnikiem (nie jest ważny w tej chwili sposób utworzenia tego skaźnika). Jendoargumentowy operator & podaje adres obiektu, zatem &x jest adresem zmiennej x. Możemy więc napisac: px=&x PAMIETAJ: Komputer rozmieszcza wartości zmiennych w „komórkach” pamięci. „Komórki” są „numerowane” - adresowane. Wartość x zidentyfikować możemy a) odczytując zawartość odpowiedniej „komórki” b) podając numer „komórki” - wskaźnik napisanie: y=x oznacza przeniesienie wartości „komórki” x-sa do „komórki” y wx=&x oznacza nadanie zmiennej wx wartości równej adresowi „komórki z wartością x”. „Odczytanie” adresu - to wykonanie operacji &x

  2. Operator „odwrotny” względem operatora & to * Tak więc jeśli wx jest wskażnikiem zmiennej x, to *wx „odczytuje” zawartość „komórki” o adresie wx - to znaczy, że wartość *wx jest równa x! Zatem jeśli mamy w programie fragment int x,y; int *wx; wx=&x; to oznacza, że y=x !!!! y=*wx; PAMIĘTAJ: jeśli wx jest wskażnikiem to zmiennej typu, powiedzmy AAA (o definiowaniu typów mówiliśmy poprzednio) to aby instrukcja y=*wx nie prowadziła do błędów (np. zaokrągleń) to *wx oraz y (y=*wx) powinny być typu AAA

  3. Jeśli (jak poprzednio) wx jest wskaźnikiem do x, wx=&x, to wyrażenie y=*wx+1 oznacza y=x+1; wyrażenie printf(``%d\n”,*wx) oznacza to, co printf(``%d\n”,x). ALE WYRAŻENIE: y=*(wx+1) nie oznacza, że y=x+1!!! bowiem wówczas wx+1 jest adresem kolejnej „komórki” (następnej po adresie zmiennej x), a *(wx+1) jest „zawartością tej (następnej) komórki” ! Zawartość ta może nie być przez programistę kontrolowana; będzie ona kontrolowana wtedy, gdy wx wskazuje np. na adres pierwszego elementu tablicy. Napisanie *wx=0 oznacza wyzerowanie wartości zmiennej x, czyli x=0 Napisanie *wx+=1 to x+=1, zatem zwiększenie x o jeden Podobnie działa (*wx)++ . WAŻNA UWAGA: napisanie *wx++ to nie to samo co (*wx)++, bowiem operacje zawsze wykonywane są od prawej do lewej. Tak więc *wx++ to zwiększenie wskaźnika o jeden, a następnie pobranie wartości komórki odpowiadającej temu (zwiększonemu) wskaźnikowi. (*wx)++ to zwiększenie zmiennej x o jeden.

  4. Jeśli wx=&x oraz wy=&y, to operacja wy=wx powoduje, że oba skaźniki: wx i wy będą wskazywały na x; zawarość komórki y nie zmieni się. Zmianę wartości y spowoduje y=*wx WSKAŹNIKI JAKO ARGUMENTY FUNKCJI chcemy napisać funkcję (procedurę; w C nie ma rozróżnienia), która zamienia zmienną x na y a y na x. Jeśli napiszemy swap(x,y) int x,y; { int temp; temp=x; x=y; y=temp; } TO BĘZIE ŹLE!. Pamiętamy, że argumenty funkcji przekazywane są przez wartość. W momencie wywołania funkcji tworzone są „lokalne” kopie argumentów a ich wartość w programie wywołującym NIE ZMIENIA SIĘ

  5. Jednak program wywołujący może przekazywać WSKAŹNIKI do ZMIENIANYCH WARTOŚCI ( Jak to było w PASCALU?) piszemy: swap(wx,wy) int *wx,*wy; { int temp; temp=*wx; *wx=*wy; *wy=temp; } A WYWOŁUJEMY: swap(&a,&b) jeśli chcemy zamienić wartościami a z b (Pamiętamy funkcję scanf, wówczas nie podaliśmy uzasadnienia, dlaczego czytając zmienna x pisaliśmy &x; uzasadnienie takiego zapisu znajdujemy teraz)

  6. Zadanie 13 Napisz program „kalkulator”, który oblicza sumę lub różnicę lub iloczyn lub iloraz dwu wczytanych liczb rzeczywistych w zależnosci od wczytanego operatora: + lub - lub * lub / . Obliczanie sumy, różnicy iloczynu i ilorazu MUSI BYĆ zapisane w oddzielnych funkcjach (procedurach). Wynik działania MUSI BYĆ parametrem funkcji, a NIE MOŻE być podstawiony pod nazwę funkcji. Decyzja wyboru działania MUSI być zapisana instrukcja switch

  7. WSKAŹNIKI I TABLICE Jeśli mamy deklarację: int tab[100]; to deklaruje ona tablicę o elementach tab[0],...,tab[99] Niech ta będzie wskaźnikiem do obiektów całkowitych int *ta; to wówczas instrukcja ta=&tab[0]; „ustawia” wskaźnik ta, aby wskazywał na pierwszy (tj. zerowy) element tablicy. Jeśli x jest całkowite, to wtedy x=*ta „skopiuje” wartość tab[0] do x. JEŚLI WIĘC ta wskazuje na tab[0], to ta+i wskazuje na tab[i]. Zatem x=*(ta+i) jest równoważne x=tab[i]. Kompilator zmienia odwołanie do tablicy na wskaźnik do jej początku. Skutkiem tego NAZWA tablicy jest „sama z siebie” wyrażeniem wskaźni- kowym. Ponieważ nazwa tablicy REPREZENTUJE położenie jej ele- mentu zerowego, to instrukcje ta=&tab[0]; oraz ta=tab; SĄ RÓWNOWAŻNE.

  8. Dalej: Jeśli ta=&a[0] to (jak już wiemy) x=*(ta+i) oznacza, że x=a[i], ALE RÓWNIEŻ x=*(tab+i) oznacza x=a[i]! oraz, że ta=&a[i] (wskazanie na i-ty element tablicy) jest równoważne ta+i, tzn. „przesunięciu” wskaźnika! PODSUMOWANIE: Nazwa tablicy (np. tab) to wskaźnik. Wyrażenie z tablicą i indeksem można zapisać za pomocą wskaźnika i przesunięcia i odwrotnie, nawet w tej samej instrukcji. Ale istnieje istotnaróżnica pomiedzy nazwą tablicy a wskaźnikiem. Wskaźnik jest zmienną, a więc operacja np.. ta=a, ta++, ta*=a, itp. ma sens, natomiast instrukcje tab=a, tab++, tab*=a SĄ NIEDOZWOLONE

  9. Jeżeli nazwa tablicy to wskaźnik (do pierwszego elementu), a kolejne elementy tablicy wyznaczane są „przesuwaniem” wskaźnika, to deklarując tablice nie zawsze musimy podawać jej rozmiar. Dotyczy to funkcji (i procedur, o procedurach - później). Dla przykładu, funkcja obliczająca długość łańcucha s: dlugosc(s); char s[]; /*nie podajemy wymiaru, jest on automatycznie dobierany */ { /* przy wywoływaniu */ int k; /*deklarujemy zmienną do zliczania długości */ k=0; /*zerujemy zmienną */ while(s[k++]) /*a to w istocie znaczy s[k++]!=0, czyli „jeśli s[k++] jest */ /*prawdą, to pętla jest wykonywana, pamiętaj o różnicy k++ oraz ++k */ /* w pierwszym przypadku najpierw dla k, potem zwiększamy o 1! */ ; /*instrukcja pusta, pętla while jest PUSTA */ return(k-1); /*ale po zbadaniu warunku while wartość k zwiększyła się o 1, musimy to skorygować */ } Funkcje dlugosc wywolujemy np. i=dlugosc(lancuch), gdzie lancuch jest typem string

  10. Zadanie 14 Funkcję obliczającą długość łańcucha można również zapisać w postaci: dlugosc(s); char *s { int n; for (n=0; *s!=‘\0’; s++) n++; return(n); } Zadanie polega na DOKŁADNYM opisie „co robią kolejne instrukcje” oraz napisaniu programu głównego, który będzie wywoływał funkcję dlugość. Należy też uzasadnić następujące stwierdzenie: definicje parametrów formalnych funkcji typu: char s[]; oraz char *s; są całkowicie równoważne.

  11. Aby mocniej podkreślić związek: wskaźniki<->tablice rozważmy funkcje, która kopiuje pierwszych n znaków łańcucha string na łańcuch substr fun1(string,substr,n) char string[],substr[]; int n; { int i; for(i=0; i<n; i++) substr[i]=string[i]; sub[i]=0; } fun1(string,substr,n) char *string,*substr; int n; { while(n--) *subsrt++=*string++; /*CO TO JEST?? */ *sub=0; } Obie funkcje są IDENTYCZNE. W pierwszej (po lewej) operujemy na elemen- tach tablic. W drugiej - na wskaźnikach. Co oznacza while(n--)? Jeśli w programie głównym chcemy wywołać funkcję kopiującą n znaków i „zabezpieczyć” się przed przypadkiem, w którym łańcuch string jest krótszy niż n, to możemy np. zastosować instrukcję: if(!fun1(string,substr,n)) printf(“ nie mogę kopiowac\n”); Wytłumacz co te instrukcje znaczą.

  12. Zadanie 15. Funkcja len(string) zwraca długość łańcucha. Posługując się podanymi przykładami napisz analogiczne dwie funkcje kopiujące „od końca” n elementów łańcucha string na łańcuch substr Oto funkcja len(s) len(s) char *s; { char *b; b=s; while(*s++) ; return(s-begin-1); }

  13. WSKAŹNIKI ZNAKOWE A FUNKCJE Stała tekstowa “ala ma kota” jest tablicą znakową. Pamiętaj, że w repre- zentacji wewnętrznej kompilator “dopisuje” znak \0 . Długość tekstu jest więc o 1 większa niż sam tekst. Znak \0 jest znakiem końca tekstu. W języku C nie istnieją operatory obsługi tekstów jako całość; nie ma też typu “pascalowskiego” string. Jeżeli zmienna zdanie zadeklarowana jest jako char *zdanie; to instrukcja zdanie= “ala ma kota”; przypisuje jej WSKAŹNIK do podanego tekstu. NIE jest to „kopiowanie” tekstu - zaangażowane są jedynie wskaźniki. Ponieważ zagadnienie to jest bardzo ważne dla zrozumienia języka, rozważmy jeszcze przykład: napiszemy funkcje kopiowania tekstu t na tekst s na różne sposoby nie możemy napisac: char zdanie[13]; zdanie= “ala ma kota”; DLACZEGO?

  14. copy(s,t) char *s,*t; { int i=0; while((s[i]=t[i])!=‘\0’) i++ } copy(s,t) char s[],t[]; { int i=0; while((s[i]=t[i])!=‘\0’) i++ } to wersja „klasyczna” to wersja „wskaźnikowa” a to wersja „uproszczona wskaźnikowa” copy(s,t) char *s,*t; { while((*s++=*t++)!=‘\ 0’) ; }

  15. Jeden z najczęściej czynionych błędów to przypisywanie wskaźników obiektom całkowitym. Pamiętaj: WSKAŹNIKI NIE SĄ (OGÓLNIE) OBIEKTAMI CAŁKOWITYMI TABLICE WIELOWYMIAROWE np. int tab[2][10]; definicja tablicy o elementach 00,01,02,03,...09,10,11,...19 wywołanie: tab[i][j] gdzie 0<=i<=1;0<=i<=9. INACZEJ NIŻ W PASCALU, gdzie było: tab[i.j]!!! <- ŹLE W C!! Nadawanie wartości początkowej tablicy tab[2][10]: int tab[2][10]= { {1,12,14,21,1,3,5,5,8,91}, {2,15,21,28,3,5,2,1,1,100} };

  16. Jeśli tablica dwu (lub więcej) wymiarowa ma być przekazywana funkcji to w deklaracji parametrów funkcji MUSIMY podać rozmiar kolumn, np. w main mamy float tab[3][100]; i mamy funkcje f(tabela) to w deklaracji musi być lub float f(tabela) float(tabela) float tabela[3][100]; float tabela[][100]; { { ale NIE MOŻE BYĆ float tabela [][]; !!! to po prawej stronie może być również: float (*tabela)[100]; ale NIE MOŻE BYĆ: float *tabela[100] !!!! Nawias [] ma WYŻSZY priorytet niż *, a więc ostatnia instrukcja deklaruje tabelę 100 wskaźników do float, a nie tabele [][100] !

  17. przykład funkcji, która z podania roku. miesiąca i dnia (np. 1977 3 13) wyznacza który był to dzień w roku na począstku programu (zewnętrznie do funkcji) deklarujemy stat int dni_miesiac[2][13]= { {0,31,28,31,30,31,30,31,31,3031,30,31}, {0,31,29,31,30,31,30,31,31,3031,30,31} }; dzien_roku(rok,miesiac,dz) int rok,miesiac,dz {int i,j; j=rok%4==0&&rok%100!=0||rok%400==0; /*czyli j=1 jeśli prawa strona jest prawdą (rok przestepny) i 0 jeśli fałsz*/ for(i=1;i<month;i++) dz=dz+dni_miesiac[j][i]; return(dz); }

  18. ZADANIE 16 do wyboru: a lub b a) napisz funkcje, która na podstawie roku i numeru dnia w roku wyznacza miesiąc i dzień miesiąca (tj. funkcje „odwrotną” do ostatniego przykładu. b) w szkole są klasy 1a, 1b,1c,2a,2b,3a,3b,3c,4a i 4b. Liczby uczniów w klasach wynoszą kolejno: 25,24,25,29,31,26,24,25,31,30 Zakładając, że spis uczniów w szkole sporządzony jest nastepująco najpiew wszyscy z 1a, numery od 1 do 25; nastepnie z 1b - numery od 26 do 49, ..... itd., aż do klasy 4b, napisz program, który określi pod którym numerem w szkole jest np.. uczeń 20 z klasy 3c, itp..

  19. TABLICE WSKAŹNIKÓW pamiętamy o równoważności: tablica (jednowymiarowa) <-> wskaźnik zatem wskaźniki zebrane w tablicę jednowymiarową odpowiadają tablicy dwuwymiarowej. Jeśli mamy deklaracje int a[10][10]; int *b[10]; to oba odwołania do elementów: a[5][5] oraz b[5][5] są poprawne. Różnica pomiędzy nimi jest analogiczna jak w przypadku nieindeksowanych wskaźników i tablic jednowymiarowych. Tablica*b[10] wprowadza 10 wskażników; każdy z nich wskazuje na obiekt całkowity, na tablicę o elementach całkowitych. Jednak jedynie a[10][10] jest prawdziwą tablicą 1oo-elementową: wszystkie komórki są dostępne w „tradycyjny” („pascalowski”) sposób.

  20. INICJOWANIE WSKAŹNIKÓW. przykład funkcji, która zwraca nazwę miesiąca na podstawie jego numeru (1 - styczeń, 2 - luty, itd.) char *nazwa_miesiaca(n) int n; { static char *name[]= { “zly miesiac”,”styczeń”,”luty”,”marzec”,kwiecień”,”maj”,”czerwiec”, “lipiec”,”wrzesien”,”pazdziernik”,”listopad”,”grudzień” }; return((n<1||n>12))?name[0]:name[n]); } ZADANIE 17 Program z zadania 16a zmodyfiukuj tak, aby zamiast numeru miesiaca podawał jego nazwe

More Related