18:03 <@Gynvael> Witam wszystkich na drugiej czesci wykladu o pointerach w jezyku C.
18:04 <@Gynvael> Log z poprzedniej czesci jest http://wyklady.net/logi/gyn_pointery1.htm <=- o tam
18:04 <@Gynvael> http://wyklady.net/logi/gyn_pointery1.zip <=- a to jest odnosnik do packa z przykladami i logiem z poprzedniego wykladu
18:05 <@Gynvael> lodz niezle splituje dzisiaj --;
18:05 <@Gynvael> Po wykladzie bedzie mozna ocenic i skomentowac wyklad na naszym forum, a konkretnie http://forum.wyklady.net/index.php?topic=42.0 <=- w tym miejscu
18:05 <@Gynvael> OK. Trzy slowa przypomnienia
18:06 <@Gynvael> Pointery (wskaznki) sa to zmienne, ktorych wartosciom jest ADRES innej zmiennej
18:06 <@Gynvael> ADRES to jest liczba, numer bajtu w pamieci, liczac od 0
18:07 <@Gynvael> W jezyku C sa dwa operatory sciscie zwiazane z pointerami
18:07 <@Gynvael> jest to referencja, czyli &
18:07 <@Gynvael> Sluzy ona do pobrania adresu zmiennej
18:07 <@Gynvael> przykladowo
18:07 <@Gynvael> int a;
18:07 <@Gynvael> int *b = &a;
18:08 <@Gynvael> Drugim widocznym tutaj znakiem jest asterisk (gwiazdka ;p), ktory sluzy do oznaczenia typu pointera przy deklaracji, oraz do ODWOLANIA SIE DO WARTOSCI ktora znajduje sie POD ADRESEM ktory jest w pointerze (na ktory wskazuje pointer)
18:08 <@Gynvael> na przyklad
18:08 <@Gynvael> int c;
18:08 <@Gynvael> int *d = &c;
18:08 <@Gynvael> *d = 1234;
18:09 <@Gynvael> W jezyku C adres maja:
18:09 <@Gynvael> 1) zmienne (chyba ze poprzedzone przedrostkiem register)
18:09 <@Gynvael> 2) struktury (a raczej 'obiekty', czyli zmienne typu struktura).. pola struktur tez maja adresy, jak normalne zmienne
18:09 <@Gynvael> 3) tablice
18:09 <@Gynvael> oraz 4) funkcje
18:10 <@Gynvael> Dzisiaj postaram sie przedstawi przyklady na uzycie wszystkich w/w wskaznikow.
18:10 <@Gynvael> Na poczatek prosty przyklad na przypomnienie jak sie uzywa pointerow
18:11 <@Gynvael> http://gynvael.vexillium.org/pointers2/ptr_var1.c
18:11 <@Gynvael> Kompilacja oraz uruchmienie tego przykladu daje nastepujce rezultaty:
18:12 <@Gynvael> (ewentualnymi warningami przy kompilacji prosze sie nie przejmowac, nie chcialem przesadzac z iloscia castow w kodzie ;p)
18:12 <@Gynvael> 18:12:03 >gcc ptr_var1.c -o ptr_var1.exe
18:12 <@Gynvael> 18:12:07 >ptr_var1.exe
18:12 <@Gynvael> Adresy zmiennych:
18:12 <@Gynvael> &a == 00404080
18:12 <@Gynvael> &b == 0022FF70
18:12 <@Gynvael> &c == 0022FF74
18:12 <@Gynvael> a == 0 b == 0000000E c == 2
18:12 <@Gynvael> a == 0 b == 00404080 c == 2
18:12 <@Gynvael> a == 12 b == 00404080 c == 2
18:12 <@Gynvael> a == 12 b == 0022FF74 c == 2
18:12 <@Gynvael> a == 12 b == 0022FF74 c == 10
18:12 <@Gynvael> 18:12:15 >
18:13 <@Gynvael> Pare slow o kodzie
18:13 <@Gynvael> Sa 3 zmienne, dwie typu int, jedna globalna (a), jedna lokalna (c), oraz jedna zmienna typu adres inta, czyli pointer do inta (b)
18:14 <@Gynvael> Potem jest jakis printf ktory wypisuje adresy tychze zmiennych
18:14 <@Gynvael> Jak widac zmienna bedaca pointerem (b) tez ma swoj adres ;>
18:14 <@Gynvael> Potem jest sekwencja instrukcji typu "wypisz wartosci, zrob cos"
18:14 <@Gynvael> Na poczatku wartosci sa nastepujace:
18:14 <@Gynvael> a == 0 b == 0000000E c == 2
18:15 <@Gynvael> u was wartosci 'b' oraz 'c' moga byc inne, poniewaz sa to niezainitowane zmienne lokalne, wiec ich wartosc jest prawie ze losowa ;p
18:15 <@Gynvael> b = &a; /* 'b' zawiera adres 'a' */
18:16 <@Gynvael> pierwsza instrukcja, zapisanie do 'b' adresu zmiennej 'a' (czyli uzycie referencji w praktyce)
18:16 <@Gynvael> a == 0 b == 00404080 c == 2
18:16 <@Gynvael> jak widac 'b' przyjal wartosc adresu zmiennej 'a'
18:16 <@Gynvael> <@Gynvael> &a == 00404080
18:16 <@Gynvael> jak widac to sie zgadza ;>
18:16 <@Gynvael> *b = 12; /* wpisujemy 12 pod adres znajdujacy sie w 'b' */
18:16 <@Gynvael> potem wrzucamy 12 pod adres na ktory wskazuje 'b', czyli tak naprawde wrzucamy to do 'a'
18:16 <@Gynvael> a == 12 b == 00404080 c == 2
18:16 <@Gynvael> jak widac 'a' faktycznie sie zmienilo
18:17 <@Gynvael> b = &c; /* 'b' zawiera adres 'c' */
18:17 <@Gynvael> zmieniamy adres w 'c'
18:17 <@Gynvael> *
18:17 <@Gynvael> w 'b' na adres 'c'
18:17 <@Gynvael> ;>
18:17 <@Gynvael> a == 12 b == 0022FF74 c == 2
18:17 <@Gynvael> <@Gynvael> &c == 0022FF74
18:17 <@Gynvael> jak widac sie zgadza wszystko
18:17 <@Gynvael> *b = 10; /* wpisujemy 10 pod adres znajdujacy sie w 'b' */
18:17 <@Gynvael> a == 12 b == 0022FF74 c == 10
18:17 <@Gynvael> i to tez sie zgadza
18:17 <@Gynvael> Tyle jesli chodzi o przypomnienie.
18:18 <@Gynvael> Aha. Pytanka prosze na priv ;>
18:18 <@Gynvael> Bede odpowiadal albo na kanale albo na privie ;>
18:18 <@Gynvael> (W zaleznosci od pytanka)
18:18 <@Gynvael> OK, lecimy dalej
18:19 <@Gynvael> Jak i po co uzywac pointerow do zmiennych ?
18:19 <@Gynvael> Czasami zdarza sie ze mamy jakas funkcje
18:19 <@Gynvael> ktora cos zwraca
18:19 <@Gynvael> na przyklad zmienna typu int
18:19 <@Gynvael> niech to bedzie
18:20 <@Gynvael> int dodaj(int a, int b) {
18:20 <@Gynvael> return a+b;
18:20 <@Gynvael> }
18:20 <@Gynvael> Jedna wartosc bez problemu mozna zwrocic
18:20 <@Gynvael> Ale co jesli chcemy zwrocic wiecej wartosci ? Dwie ? Trzy ?
18:21 <@Gynvael> Mozna podac funkcji ADRESY (pointery do) zmiennych w ktore maja byc te wartosci 'zwracane' wrzucone
18:21 <@Gynvael> Jest to dosc powszechna praktyka ;>
18:21 <@Gynvael> wezmy przykladowo funkcje dodawania
18:21 <@Gynvael> ktora przyjmuje liczby a,b i c
18:21 <@Gynvael> i ma zwrocic:
18:21 <@Gynvael> a+b
18:21 <@Gynvael> b+c
18:22 <@Gynvael> a+b+c
18:22 <@Gynvael> wszystko naraz
18:22 <@Gynvael> http://gynvael.vexillium.org/pointers2/ptr_var2.c
18:22 <@Gynvael> int dodaj(int a, int b, int c, int *wynik_ab, int *wynik_bc)
18:22 <@Gynvael> {
18:22 <@Gynvael> *wynik_ab = a+b;
18:22 <@Gynvael> *wynik_bc = b+c;
18:22 <@Gynvael> return a+b+c;
18:22 <@Gynvael> }
18:22 <@Gynvael> tak wyglada ta funkcja
18:23 <@Gynvael> jak widac przyjmuje 5 argumentow, z czego 3 sa argumentami wejsciowymi (a,b,c), a dwa pozostale to adresy do zmiennych gdzie chcemy wrzucic wynik
18:23 <@Gynvael> samo cialo funkcji jest raczej proste, po prostu dodawanie (a+b), a wynik wrzucony do MIEJSCA NA KTORE WSKAZUJE zmienna wynik_ab
18:23 <@Gynvael> analogicznie w drugim wypadku
18:23 <@Gynvael> wynik a+b+c zwracamy tradycyjna metoda
18:24 <@Gynvael> int wyn, wyn1, wyn2;
18:24 <@Gynvael> wyn = dodaj(1,2,3, &wyn1, &wyn2);
18:24 <@Gynvael> wywolanie jak widac
18:24 <@Gynvael> 3 argumenty wejsciowe, liczby 1,2,3, oraz adresy dwoch zmiennych
18:25 <@Gynvael> skompilujmy i zobaczmy ze faktycznie dziala:
18:25 <@Gynvael> 18:12:15 >gcc ptr_var2.c -o ptr_var2.exe
18:25 <@Gynvael> 18:25:09 >ptr_var2.exe
18:25 <@Gynvael> wyn == 6
18:25 <@Gynvael> wyn1 == 3
18:25 <@Gynvael> wyn2 == 5
18:25 <@Gynvael> 18:25:16 >
18:25 <@Gynvael> 1+2 == 3, wiec wyn1 jest ok
18:26 <@Gynvael> 2+3 == 5, wiec wyn2 tez jest ok
18:26 <@Gynvael> 1+2+3 == 6, wiec wyn rowniez sie zgadza
18:26 <@Gynvael> Jak by byly jakies pytanka to priv ;>
18:26 <@Gynvael> OK
18:26 <@Gynvael> A teraz przypomnienie castowania (rzutowania) typow pointerow
18:26 <@Gynvael> Jak wiadomo pointer to jest adres zmiennej o danym konkretnym typi
18:26 <@Gynvael> *typie
18:26 <@Gynvael> np
18:27 <@Gynvael> int *a;
18:27 <@Gynvael> to jest pointer do int'a
18:27 <@Gynvael> float *b;
18:27 <@Gynvael> a to jest pointer do floata
18:27 <@Gynvael> casting (rzutowanie) to jest przekonanie kompilatora ze pointer wskazuje na inny typ niz mu sie wydaje ;>
18:28 <@Gynvael> a uogulniajac, jest to zmiana typu zmiennej ;>
18:28 <@Gynvael> np
18:28 <@Gynvael> float a = 12.3f;
18:28 <@Gynvael> int *b = (int*)&a;
18:28 <@Gynvael> przekonalismy kompilator ze &a to pointer do inta, a nie do floata
18:28 <@Gynvael> OK, ale co to nam daje ?
18:28 <@Gynvael> Wezmy sobie jakies proste zadanie
18:29 <@Gynvael> Na przyklad przekopiowanie tablicy unsigned char (bajtow) z jednej tablicy do drugiej
18:29 <@Gynvael> najprostsza funkcja tego typu wyglada nastepujaco:
18:29 <@Gynvael> void copy1(char gdzie[], char co[], int ile)
18:29 <@Gynvael> {
18:29 <@Gynvael> int i;
18:29 <@Gynvael> for(i = 0; i < ile; i++)
18:29 <@Gynvael> gdzie[i] = co[i];
18:29 <@Gynvael> }
18:29 <@Gynvael> (tak wiem, mozna bez 'i' to zrobic, z while, ale uznalem ze to jest czytelniejsze)
18:30 <@Gynvael> czyli jak widac
18:30 <@Gynvael> kopiujemy bajt po bajcie
18:30 <@Gynvael> z co[i] do gdzie[i]
18:30 <@Gynvael> dla kazdego bajtu tablicy
18:30 <@Gynvael> czy gdzie nie powinien byc pointerem?
18:30 <@Gynvael> zastosowalem tutaj zapis
18:30 <@Gynvael> char gdzie[] i char co[]
18:31 <@Gynvael> ten zapis jest ROWNOWAZNY zapisowi
18:31 <@Gynvael> char *gdzie i char *co
18:31 <@Gynvael> ale sugeruje programiscie ze gdzie i co sa tablicami
18:31 <@Gynvael> ok
18:31 <@Gynvael> a teraz jak uzyc castowania ?
18:31 <@Gynvael> pamietamy ze unsigned char ma wielkosc 1 bajt
18:31 <@Gynvael> sa zmienne, np int czy float
18:31 <@Gynvael> ktore maja wielkosc 4 bajty
18:31 <@Gynvael> sa tez wieksze zmnienne (long long 8 bajtow, long double 12 bajtw)
18:32 <@Gynvael> mozna wiec wziasc adresy tych tablic
18:32 <@Gynvael> powiedziec kompilatorowi ze to sa adresy tak na prawde do intow
18:32 <@Gynvael> i kopiowac cale inty naraz
18:32 <@Gynvael> po 4 bajty naraz zamiast po jeden
18:32 <@Gynvael> mozna isc tez o krok dalej
18:32 <@Gynvael> i przekonac kompilator ze to sa pointery do long double,
18:33 <@Gynvael> w ten sposob bedzie on kopiowal 12 bajtow naraz (no prawie naraz, o tym za chwile)
18:33 <@Gynvael> http://gynvael.vexillium.org/pointers2/ptr_cast1.c
18:33 <@Gynvael> rzucmy okiem na ten przyklad
18:33 <@Gynvael> pare slow o nim
18:33 <@Gynvael> sa tam 3 funkcje kopiujace
18:34 <@Gynvael> copy1 kopiujaca bajt po bajcie
18:34 <@Gynvael> copy2 ktora korzysta z rzutowania na int*
18:34 <@Gynvael> oraz copy3 korzystajaca z rzutowania na long double*
18:35 <@Gynvael> w main() natomiast jest kod alokujacy 200MB pamieci (razy 2, wiec warto sie upewnic ze ma sie 400mb ramu wolnego, albo zmniejszyc ilosc w kodzi np do 50*1024*1024)
18:35 <@Gynvael> oraz mierzacy czas wykonania kazdej z tych funkcji
18:35 <@Gynvael> na koncu sa prezentowane pomiary
18:35 <@Gynvael> oraz uwalniane sa buforki
18:35 <@Gynvael> tzn zwalniana jest zaalkowana pameic
18:36 <@Gynvael> Skompilujmy i zobaczmy jak dziala
18:36 <@Gynvael> i czy faktycznie kopiowanie po pare bajtow naraz jest szybsze
18:36 <@Gynvael> niz kopiowanie po bajcie
18:37 <@Gynvael> 18:25:16 >gcc ptr_cast1.c -o ptr_cast1.exe
18:37 <@Gynvael> 18:36:31 >ptr_cast1.exe
18:37 <@Gynvael> czas copy1 == 2234
18:37 <@Gynvael> czas copy2 == 500
18:37 <@Gynvael> czas copy3 == 422
18:37 <@Gynvael> 18:37:39 >
18:37 <@Gynvael> Czasy sa podane w milisekundach (przynajmniej wg mojego kompilatora ;p)
18:38 <@Gynvael> Jak widac kopiowanie po jednym bajcie jest wooolne
18:38 <@Gynvael> Kopiownie po 4ry bajty naraz, czyli po zrzutowaniu na int* jest juz szybsze, i to ponad 4 razy
18:39 <@Gynvael> natomiast kopiowanie po 12 bajtow jest niewiele szybsze niz kopiowanie po 4 bajty
18:39 <@Gynvael> Rzucmy okiem na kod
18:39 <@Gynvael> Kod funkcji copy2 ;>
18:39 <@Gynvael> funkcja przyjmuje 3 rzeczy
18:39 <@Gynvael> pointer do tablicy docelowej
18:39 <@Gynvael> do tablicy zrodlowej
18:39 <@Gynvael> oraz ilosc bajtow do przekopiowania
18:40 <@Gynvael> sa w niej zdeklarowane 4 zmienne:
18:40 <@Gynvael> int *igdzie, *ico;
18:40 <@Gynvael> int il = ile / sizeof(int);
18:40 <@Gynvael> int i;
18:40 <@Gynvael> czyli dwa pointery do intow
18:40 <@Gynvael> ilosc intow do przekopiowania (zmienna il) (przypominam ze to jest dzielenie calkowite)
18:40 <@Gynvael> jakis iterator 'i'
18:40 <@Gynvael> potem jest jakis wstepny setup, czyli
18:40 <@Gynvael> ile %= sizeof(int);
18:40 <@Gynvael> igdzie = (int*)gdzie;
18:40 <@Gynvael> ico = (int*)co;
18:41 <@Gynvael> do ile trafia reszta z dzielenia ile przez 4
18:41 <@Gynvael> chodzi tutaj o zapisanie wielkosci do przekopiowania
18:41 <@Gynvael> np
18:41 <@Gynvael> 123
18:41 <@Gynvael> jako 30*4 + 3
18:41 <@Gynvael> czyli il * 4 + ile
18:42 <@Gynvael> dzieki temu wiemy ze mozemy kopiowac il razy po 4ry bajty naraz
18:42 <@Gynvael> i wiemy ze koncowe bajty musimy kopiowac po bajcie
18:42 <@Gynvael> bo np zostana nam 3, 2 albo 1 bajt
18:42 <@Gynvael> moze tez nic nie zostac
18:42 <@Gynvael> do igdzie i ico zapisujemy adresy tablic (oczywiscie informujac kompilator ze dla nas to sa pointery do intow, a nie unsigned charow)
18:43 <@Gynvael> w tym przypadku tak na prawde nie ma znaczenia czy uzyjemy pointerow do intow, floatow czy unsigned longow
18:43 <@Gynvael> wazne zeby byla wielkosc '4' bajty
18:43 <@Gynvael> i zeby wszedzie byl ten sam typ
18:43 <@Gynvael> dodam tylko ze kopiowanie floatow z jednego miejsca do drugiego NIE jest wykonywane przez koprocesor
18:44 <@Gynvael> for(i = 0; i < il; i++)
18:44 <@Gynvael> *igdzie++ = *ico++;
18:44 <@Gynvael> to jest petla kopiujaca
18:44 <@Gynvael> czyli
18:44 <@Gynvael> *idzie = *ico;
18:44 <@Gynvael> skopiuj wartosc na ktora wskazuje ico do miejsca wskazywanego przez idzie
18:44 <@Gynvael> *igdzie
18:45 <@Gynvael> potem wiekszamy adresy
18:45 <@Gynvael> igdzie++ ico++
18:45 <@Gynvael> przypominam ze inkrementacja pointero to tak na prawde dodanie sizeof(typ) do nich
18:45 <@Gynvael> czyli w przypadku inta igdzie++ zwiekszy adres o 4, a nie o 1 bajt
18:45 <@Gynvael> poniewaz igdize jest pointerem do inta
18:45 <@Gynvael> a int ma wielkosc 4
18:45 <@Gynvael> wiec logicznym jest ze nastepny int po tym na ktory obecnie pointer wskazuje, lezy o 4 bajty dalej
18:46 <@Gynvael> gdzie = (char*)igdzie;
18:46 <@Gynvael> co = (char*)ico;
18:46 <@Gynvael> potem przepisujemy pointery, rzutujemy je na pointery do charow
18:46 <@Gynvael> caly czas je inkrementowalismy, wiec one sa teraz gdzies pod koniec tablic
18:46 <@Gynvael> for(i = 0; i < ile; i++)
18:46 <@Gynvael> *gdzie++ = *co++;
18:46 <@Gynvael> i kopiujemy bajt po bajcie ostatnie 3 (lub mniej) bajtow
18:46 <@Gynvael> copy3 jest analogiczna
18:46 <@Gynvael> tyle ze zamiast int* jest long double*
18:47 <@Gynvael> Pytanie sie pojawia
18:47 <@Gynvael> czemu kopiowanie 12 bajtow
18:47 <@Gynvael> bylo tylko o ciupenke szybsze niz kopiowanie co 4 bajty
18:47 <@Gynvael> coz, mamy procki 32 bitowe
18:47 <@Gynvael> ktore maja rejestry 32 bitowe
18:47 <@Gynvael> gcc ptr_cast1.c -S -masm=intel wygeneruje plik ptr_cast1.s
18:48 <@Gynvael> w ktorym jest wygenerowany przez kompilator kod tych funkcji w assemblerku
18:48 <@Gynvael> widac tam
18:48 <@Gynvael> mov eax, DWORD PTR [edx]
18:48 <@Gynvael> mov ecx, DWORD PTR [edx+8]
18:48 <@Gynvael> mov edx, DWORD PTR [edx+4]
18:48 <@Gynvael> mov DWORD PTR [ebx], eax
18:48 <@Gynvael> mov DWORD PTR [ebx+4], edx
18:49 <@Gynvael> mov DWORD PTR [ebx+8], ecx
18:49 <@Gynvael> ===code==
18:49 <@Gynvael> ze to tak na prawde jest kopiowanie 3 razy po 4 bajty
18:49 <@Gynvael> a nie 12 bajtow naraz
18:49 <@Gynvael> stad ta niewielka roznica w szybkosci
18:49 <@Gynvael> coz.. 32 bitowe procki, 32 bitowe (4 bajtowe) rejestry
18:49 <@Gynvael> czemu nie sa uzywane rejestry koprocesora do kopiowania ?
18:49 <@Gynvael> wkoncu one sa 12 bajtowe
18:50 <@Gynvael> bodajze chodzi tutaj o to ze kopiowanie z uzyciem tych rejestrow wymagalo by dodatkowo synchronizacji CPU z FPU
18:50 <@Gynvael> a to dodatkowe cykle
18:50 <@Gynvael> no nic
18:50 <@Gynvael> ;>
18:50 <@Gynvael> te funkcje kopiujace mozna jeszcze zoptypamlizowac, ale trzeba by w sumie wstawki asma piac
18:50 <@Gynvael> *pisac
18:50 <@Gynvael> mozna by uzyc SSE, MMX etc
18:50 <@Gynvael> ale coz
18:50 <@Gynvael> to temat na zupelnie inny wykald
18:51 <@Gynvael> ;>
18:51 <@Gynvael> *wyklad
18:51 <@Gynvael> ok
18:51 <@Gynvael> pytania jesli sa to przypominam ze na priv
18:51 <@Gynvael> Rzucmy okiem jeszcze na jeden przyklad uzycia castowania
18:51 <@Gynvael> Czasem trzeba porownac stringi
18:51 <@Gynvael> nie ma rady, co zrobic
18:51 <@Gynvael> uzywa sie zazwyczaj funkcji strcmp, albo jej podobnej
18:52 <@Gynvael> natomiast jesli wiemy ze nasz string ma okreslona dlugosc
18:52 <@Gynvael> np 4 bajty ZAWSZE
18:52 <@Gynvael> albo 8 bajtow
18:52 <@Gynvael> albo 12 bajtow
18:52 <@Gynvael> to mozemy skorzystac z rzutowania
18:52 <@Gynvael> zrzutowac sobie taki string na przyklad na inta
18:52 <@Gynvael> i porownywac liczby inta
18:53 <@Gynvael> http://gynvael.vexillium.org/pointers2/ptr_cast2.c
18:53 <@Gynvael> if(strcmp("ala", "kot") == 0)
18:53 <@Gynvael> puts("ala === kot"); else puts("ala != kot");
18:53 <@Gynvael> tak wyglada normalna metoda porownywania dwoch stringow
18:53 <@Gynvael> natomiast metoda wykorzystujaca cast wyglada tak:
18:53 <@Gynvael> if(*(int*)"ala" == *(int*)"kot")
18:53 <@Gynvael> puts("ala === kot"); else puts("ala != kot");
18:53 <@Gynvael> dodam ze jest ona oczywiscie duzo szybsza
18:54 <@Gynvael> jeszcze slowko wytlumaczenia czemu sobie string tak po prostu na pointer rzutuje
18:54 <@Gynvael> jak mowilem na ostatnim wykladzie, oraz na wykladach rok temu
18:54 <@Gynvael> string, czyli "asdf" to tak na prawde ADRES miejsca w pamieci gdzie ten string jest ulokowany
18:55 <@Gynvael> const char*
18:55 <@Gynvael> a jesli to adres, to mozna zmienic typ na jaki wskazuje
18:55 <@Gynvael> i rozegrac to po swojemu ;>
18:55 <@Gynvael> ok
18:55 <@Gynvael> ok
18:56 <@Gynvael> A teraz bardzo krotki przyklad
18:56 <@Gynvael> na temat pointerow ktore na pewno kazdy uzywal
18:56 <@Gynvael> mianowicie
18:56 <@Gynvael> funkcje printf, puts, scanf przyjmuja pointery za parametry
18:56 <@Gynvael> rzucmy okiem na funkcje
18:56 <@Gynvael> http://gynvael.vexillium.org/pointers2/ptr_everyday1.c
18:56 <@Gynvael> mamy dwie zmienne
18:57 <@Gynvael> za pomoca scanf z ciagiem formatujacym "%i %i" wczytujemy do nich wartosci
18:57 <@Gynvael> skad scanf wie ze do nich wczytujemy? ano podajem mu ADRESY tych zmiennych
18:57 <@Gynvael> potem wypisujemy (ciag formatujacy to tak na prawde, jak pisalem wyzej, adres meijsca w pameici gdzie ten ciag jest, czyli pointer)
18:57 <@Gynvael> zmienne a, b
18:57 <@Gynvael> [Arashi] Cesc Kochanie! Kogo dzis zownzujemy?
18:58 <@Gynvael> oraz zapisujemy ilosc bajtow wypisanych do zmiennej 'n'
18:58 <@Gynvael> (o ;> autogrit ;p)
18:58 <@Gynvael> %n sluzy do tego
18:58 <@Gynvael> zeby printf wiedzial gdzie ta ilosc bajtow zapisac, trzeba mu podac ADRES zmiennej
18:58 <@Gynvael> potem wypisujemy ta ilosc bajtow
18:58 <@Gynvael> 3 instrukcje, 6 pointerow ;>
18:59 <@Gynvael> OK
18:59 <@Gynvael> Teraz beda pointery do struktur, na przykaldzie LISTY
18:59 <@Gynvael> To w sumie jedno z miejsc gdzie bez pointerow praktycznie nie da sie obyc
19:00 <@Gynvael> Pare slow na temat tego co to jest lista
19:00 <@Gynvael> Lista jest to STRUKTURA DANYCH, taka jak np tablica, ktora sluzy do przechowywania ilus tam danych
19:00 <@Gynvael> na przykald int'ow
19:00 <@Gynvael> ilu intow ?
19:00 <@Gynvael> No wlasnie ;>
19:00 <@Gynvael> W przypadku tablicy
19:00 <@Gynvael> np
19:00 <@Gynvael> int tab[100]
19:00 <@Gynvael> musimy okreslic
19:00 <@Gynvael> jaka tablica ma miec wielkosc
19:01 <@Gynvael> np 100 intow
19:01 <@Gynvael> a jesli nie wiemy ile intow bedzie potrzeba ?
19:01 <@Gynvael> na poprzednim wykladzie byl przyklad co zrobic jesli dowiadujemy sie chwile przed uzyciem ile jest potrzebne bajtow/intow
19:01 <@Gynvael> wtedy pomagala alokacja dynamiczna
19:01 <@Gynvael> a jesli sie wogole nie dowiadujemy ?
19:01 <@Gynvael> ;>
19:01 <@Gynvael> Lista ;>
19:01 <@Gynvael> (linked list)
19:02 <@Gynvael> lista zwieksza sie dynamicznie (jak podpowiada ruf3k) ;>
19:02 <@Gynvael> w C nie ma jako takich list (jak w perlu czy PHP)
19:02 <@Gynvael> wiec trzeba je sobie oprogramowac
19:03 <@Gynvael> Pamietacie przyklad z kartka i cyframi z pierwszego wykladu ?
19:03 <@Gynvael> w ostatnim przykaldzie wtedy pokazywalem jak rozwiazac problem kiedy liczby sa nie pokolei

19:04 <@Gynvael> uzywalismy wtedy par, wartosc (liczba) i adres nastepnej wartosci
19:04 <@Gynvael> na tym wlasnie polega lista
19:04 <@Gynvael> wezmy sobie strukture
19:04 <@Gynvael> struct node_st
19:04 <@Gynvael> {
19:04 <@Gynvael> int dane;
19:04 <@Gynvael> struct node_st *next;
19:04 <@Gynvael> };
19:04 <@Gynvael> struktura ma dwa pola, jedno na dane
19:04 <@Gynvael> a drugie to adres nastepnej struktury
19:05 <@Gynvael> jedyne na dobra sprawe co musimy pamietac, to adres poczatkowy listy
19:05 <@Gynvael> struct node_st *lista;
19:06 <@Gynvael> na poczatku jest on ustawiony na NULL (to jest (void*)0 w C, czyli adres 0, tak na prawde do tego adresu dostep ma tylko system operacyjny, wiec spokojnie mozemy sobie z niego korzystac do oznaczania konca / wartosci nieustalonej / pustej etc)
19:06 <@Gynvael> (bo zadna zmienna nigdy nie bedzie miala adresu 0 ;>)
19:06 <@Gynvael> ok
19:06 <@Gynvael> a teraz zalozmy
19:06 <@Gynvael> ze mamy taka liste
19:06 <@Gynvael> lista -> [ 2 ] -> [ 1 ] -> NULL
19:07 <@Gynvael> czyli lista zawiera adres obiektu/zmiennej typu struktura/ ktorego dane == 2
19:07 <@Gynvael> i ten obiekt zawiera adres innego obiektu ktorego dane === 1
19:07 <@Gynvael> i ten obiekt zawiera adres NULL, czyli koniec listy
19:07 <@Gynvael> i chcemy cos dodac
19:07 <@Gynvael> np 3
19:07 <@Gynvael> mozna to zrobic latwo w 4 krokach

19:08 <@Gynvael> na tym obrazku jest pokazane JAK
19:08 <@Gynvael> w KROK 1 alokujemy pamiec na nowa zmienna - strukture typu node_st
19:08 <@Gynvael> w KROK 2 zapisujemy dane ktore chcemy dodac
19:08 <@Gynvael> do tej zmiennej ktora stworzylismy
19:09 <@Gynvael> w KROK 3 zapisujemy pointer do pierwszego elementu listy w tym node (tej zmiennej stworzonej)
19:09 <@Gynvael> i w KROK 4 kazemy zmiennej 'lista' wskazywac na nowy element
19:10 <@Gynvael> jak widac dodalismy element [ 3 ] do listy ;>
19:10 <@Gynvael> ok
19:10 <@Gynvael> rzucmy okiem na kod
19:10 <@Gynvael> http://gynvael.vexillium.org/pointers2/ptr_list.c
19:10 <@Gynvael> sa tam 3 funkcje + main
19:10 <@Gynvael> czyli
19:10 <@Gynvael> dodaj(int a) ktory dodaje do listy
19:10 <@Gynvael> (na poczatek listy)
19:11 <@Gynvael> czysc() ktory czysci liste (wywala wszystko z niej)
19:11 <@Gynvael> oraz wypisz() ktore wypisuje wszystkie elementy listy
19:11 <@Gynvael> dodaj() juz omowilem
19:11 <@Gynvael> najpierw wypisz
19:11 <@Gynvael> wypisz dziala w nastepujacy sposob:
19:11 <@Gynvael> jest sobie zmienna lokalna 'i'
19:11 <@Gynvael> zapisuje do niej poczatek listy
19:11 <@Gynvael> i = lista;
19:12 <@Gynvael> po czym do pooki 'i' nie pokazuje na NULL
19:12 <@Gynvael> tzn nie jest rowne NULL
19:12 <@Gynvael> robie cos takiego:
19:12 <@Gynvael> 1) wypisuje dane elemntu wskazywanego przez 'i', czyli i->dane
19:12 <@Gynvael> 2) kaze isc 'i' na nastepny element, czyli przyjac mu wartosc adresu nastepnego elementu
19:12 <@Gynvael> i = i->next;
19:12 <@Gynvael> i tak az natrafie na NULL
19:13 <@Gynvael> czysczenie listy jest podobne
19:14 <@Gynvael> mianowicie dokladnie to samo
19:14 <@Gynvael> tylko dodatkowo zapamietujemy adres elementu przed i = i->next
19:14 <@Gynvael> i ten elementu usuwamy (free)
19:14 <@Gynvael> tj uwalniamy pameic ktora system dal nam na niego
19:14 <@Gynvael> ok
19:14 <@Gynvael> jak tego uzyc widac w main()
19:14 <@Gynvael> kompilacja i uruchomienie:
19:15 <@Gynvael> 18:37:39 >gcc ptr_list.c -o ptr_list.exe
19:15 <@Gynvael> 19:14:58 >ptr_list.exe
19:15 <@Gynvael> lista->
19:15 <@Gynvael> lista-> 4, 3, 2, 1,
19:15 <@Gynvael> lista-> 6, 5, 4, 3, 2, 1,
19:15 <@Gynvael> lista->
19:15 <@Gynvael> lista-> 1,
19:15 <@Gynvael> 19:15:03 >
19:15 <@Gynvael> kod maina wyglada jak widac, czyli:
19:16 <@Gynvael> hmmm skonfrontujmy moze kod maina z tym co jest wypisywane
19:16 <@Gynvael> najpierw lista jest pusta, wiec
19:16 <@Gynvael> lista->
19:16 <@Gynvael> potem
19:16 <@Gynvael> dodaj(1);
19:16 <@Gynvael> dodaj(2);
19:16 <@Gynvael> dodaj(3);
19:16 <@Gynvael> dodaj(4);
19:16 <@Gynvael> wiec na liste trafily 4 elementy
19:16 <@Gynvael> lista-> 4, 3, 2, 1,
19:16 <@Gynvael> co faktycznie wypisz() uwidacznia
19:16 <@Gynvael> dodaj(5);
19:16 <@Gynvael> dodaj(6);
19:16 <@Gynvael> potem trafiaja kolejne dwa
19:16 <@Gynvael> lista-> 6, 5, 4, 3, 2, 1,
19:16 <@Gynvael> a potem lista jest czyszczona
19:17 <@Gynvael> czysc();
19:17 <@Gynvael> lista->
19:17 <@Gynvael> i dodawany jest do niej jeden element
19:17 <@Gynvael> dodaj(1);
19:17 <@Gynvael> lista-> 1,
19:17 <@Gynvael> ;> czyli wyglada na to ze dziala
19:17 <@Gynvael> pare slow jeszcze o listach
19:17 <@Gynvael> ale tylko pare bo nie jest to wyklad ze struktur danych
19:17 <@Gynvael> list jest sporo rodzajow
19:18 <@Gynvael> to byla lista jednokierunkowa
19:18 <@Gynvael> sa jeszcze listy dwukierunkowe
19:18 <@Gynvael> cykliczne
19:18 <@Gynvael> listy do ktorych mozna dodawac cos na poczatek, koniec, w srodek etc
19:18 <@Gynvael> jak sie programista uwezmie to moze do takiej listy dorobic iteratory, indexowanie, sortowanie, enumeracje z filtrami etc
19:18 <@Gynvael> ;>
19:18 <@Gynvael> ale coz, to tez temat na inny wyklad
19:19 <@Gynvael> po za tym sa jeszcze inne ciekawe strukturki danych
19:19 <@Gynvael> roznego rodzaju drzewa (np BST (binary sort tree))
19:20 <@Gynvael> listy haszowe (tzw tablice rozproszone, hashlisty)
19:20 <@Gynvael> i inne grafy ;>
19:20 <@Gynvael> ich zastosowanie jest w bardzo wielu dziedzinach
19:20 <@Gynvael> i kazda z nich wymaga/uzywa pointerow ;>
19:20 <@Gynvael> OK
19:21 <@Gynvael> tyle o pointerach do struktur na przykladzie list
19:21 <@Gynvael> Chcial bym jeszcze dzisiaj powiedziec o pointerach do funkcji i do tablic
19:21 <@Gynvael> ale zaczne od tych pierwszych, poniewaz tych drugich sie praktycznie nie uzywa
19:21 <@Gynvael> Jak wspominalem funkcje maja swoje adresy ;>
19:22 <@Gynvael> Programista w C raczej z tych adresow nie korzysta za czesto, ale reverserzey juz czesciej ;>
19:22 <@Gynvael> anyway
19:22 <@Gynvael> jak uzyskac adres funkcji ?
19:22 <@Gynvael> np funkcji main
19:22 <@Gynvael> int main(void)
19:22 <@Gynvael> jest jak w przypadku tablic
19:22 <@Gynvael> czyli nazwa jest adresem
19:22 <@Gynvael> main jest wiec adresem funkcji main
19:22 <@Gynvael> czy mozna napisac &main ?
19:22 <@Gynvael> mozna
19:22 <@Gynvael> dodam jako ciekawostke ze napisanie *main tez zwroci adres funkcji
19:23 <@Gynvael> a napisanie **********************main rowniez zwroci ten adres ;>
19:23 <@Gynvael> taka sobie anomalia skladni C, wystepujaca tylko w przypadku adresow funkcji
19:23 <@Gynvael> ;>
19:23 <@Gynvael> ale piszmy 'main' ;>
19:24 <@Gynvael> OK
19:24 <@Gynvael> rzucmy okiem na przyklad
19:24 <@Gynvael> http://gynvael.vexillium.org/pointers2/ptr_func1.c
19:24 <@Gynvael> mamy tam 3 bardzo proste funkcje
19:24 <@Gynvael> dodaj, odejmij i pomnoz
19:25 <@Gynvael> wszystkie 3 przyjmuja dwie wartosci, i zwracaja ich sume/roznice/iloczyn
19:25 <@Gynvael> a potem mamy main
19:25 <@Gynvael> i mamy taki dziwny twor
19:25 <@Gynvael> int (*func)(int a, int b);
19:25 <@Gynvael> pytanie brzmi, wtf ;p
19:25 <@Gynvael> jest to pointer do funkcji
19:25 <@Gynvael> a konkretniej
19:26 <@Gynvael> jest to ZMIENNA o nazwie 'func', typu ADRES FUNKCJI ZWRACAJACEJ 'int' I PRZYJMUJACEJ dwa inty jako ARGUMENTY
19:26 <@Gynvael> MOZNA podac nazwy argumentow (
19:26 <@Gynvael> 'a', 'b'), ale nie trzeba tego robic ;>
19:26 <@Gynvael> ja podalem, chyba z rozpedu ;p;>
19:26 <@Gynvael> mamy tez tam dwie zmienne
19:27 <@Gynvael> 'a' i 'b' (nazwy argumentow 'func' nie maja nic wspolnego z tymi zmiennymi btw)
19:27 <@Gynvael> potem mamy wypisanie adresow funkcji
19:27 <@Gynvael> wypisanie warunkow poczatkowcyh
19:27 <@Gynvael> i pare operacji
19:27 <@Gynvael> ok, najpierw kompilacja, a potem porownamy kod z tym co jest wypisywane
19:29 <@Gynvael> 19:15:03 >gcc ptr_func1.c -o ptr_func1.exe
19:29 <@Gynvael> 19:28:16 >ptr_func1.exe
19:29 <@Gynvael> adresy funkcji:
19:29 <@Gynvael> dodaj == 004012E0
19:29 <@Gynvael> odejmij == 004012EB
19:29 <@Gynvael> pomnoz == 004012F8
19:29 <@Gynvael> a == 10, b == 5, func == 0022FFA8
19:29 <@Gynvael> a == 10, b == 5, func == 004012EB
19:29 <@Gynvael> a == 5, b == 5, func == 004012EB
19:29 <@Gynvael> a == 5, b == 5, func == 004012E0
19:29 <@Gynvael> a == 10, b == 5, func == 004012E0
19:29 <@Gynvael> a == 10, b == 5, func == 004012F8
19:29 <@Gynvael> a == 50, b == 5, func == 004012F8
19:29 <@Gynvael> 19:29:28 >
19:30 <@Gynvael> dobra
19:30 <@Gynvael> wypisalo jakies adresy funkcji
19:30 <@Gynvael> potem troche debug messages ;>
19:30 <@Gynvael> porownajmy to z kodem
19:30 <@Gynvael> a == 10, b == 5, func == 0022FFA8
19:30 <@Gynvael> takie sa warunki poczatkowe
19:30 <@Gynvael> 'func' jest zmienna lokalna, wiec na poczatku zawiera jakies smieci ;>
19:30 <@Gynvael> u was adresy moga byc ofc rozne
19:31 <@Gynvael> func = odejmij;
19:31 <@Gynvael> func przyjmuje adres funkcji odejmij
19:31 <@Gynvael> a == 10, b == 5, func == 004012EB
19:31 <@Gynvael> odejmij == 004012EB
19:31 <@Gynvael> prosze zauwazyc ze faktycznie
19:31 <@Gynvael> func == odejmij
19:31 <@Gynvael> potem mamy
19:31 <@Gynvael> a = func(a, b);
19:31 <@Gynvael> i tutaj wychodzi jak wywolac funkcje ktorej adres mamy
19:32 <@Gynvael> po prostu traktjemy 'func' jak normalna funkcje
19:32 <@Gynvael> nie potrzeba zadnych * & etc
19:32 <@Gynvael> po prostu dajemy nawias i argumenty i tyle
19:32 <@Gynvael> ok
19:32 <@Gynvael> czyli w func mamy odejmowanie
19:32 <@Gynvael> wiec wynikiem a powinno byc 10-5, czyli 5
19:32 <@Gynvael> a == 5, b == 5, func == 004012EB
19:32 <@Gynvael> zauwazmy ze faktycznie tak jest
19:32 <@Gynvael> func = dodaj;
19:32 <@Gynvael> potem wrzucamy tam dodawanie
19:32 <@Gynvael> a == 5, b == 5, func == 004012E0
19:32 <@Gynvael> dodaj == 004012E0
19:33 <@Gynvael> adresy sie zgadzaja
19:33 <@Gynvael> a = func(a, b);
19:33 <@Gynvael> odpalamy dodawanie
19:33 <@Gynvael> a == 10, b == 5, func == 004012E0
19:33 <@Gynvael> 5+5 == 10, wiec jest ok
19:33 <@Gynvael> func = pomnoz;
19:33 <@Gynvael> potem wrzucamy mnozenie
19:33 <@Gynvael> a == 10, b == 5, func == 004012F8
19:33 <@Gynvael> pomnoz == 004012F8
19:33 <@Gynvael> adresy sie zgadzaja
19:33 <@Gynvael> i je odpalamy
19:33 <@Gynvael> a = func(a, b);
19:33 <@Gynvael> a == 50, b == 5, func == 004012F8
19:33 <@Gynvael> 5*10 = 50, jest ok
19:34 <@Gynvael> czyli wyglada na to ze sie zgadza ;>
19:34 <@Gynvael> ok
19:34 <@Gynvael> po co nam pointery do funkcji ?
19:34 <@Gynvael> 1) jesli chcemy kozystac z jakis dynamicznych bibliotek etc, to trzeba z nich pobrac adresy funkcji
19:35 <@Gynvael> wlasnie do pointerow na funkcje
19:35 <@Gynvael> (patrz DLLki albo .so pod nixami, oraz funkcje GetProcAddress i nie pamietam nixowego odpowiednika ;ppp)
19:35 <@Gynvael> -=193536=- dlsym()
19:36 <@Gynvael> 2) jesli mamy jakas funkcje, np sortujaca
19:36 <@Gynvael> np qsort
19:36 <@Gynvael> to ona moze przyjac adres funkcji porownujacej
19:36 <@Gynvael> dzieki czemu w zaleznosci od tego jak bedziemy chcieli sortowac, i co bedziemy chcieli sortowac, to wystarczy dac adresik odpowiedniej funkcji porownujacej i juz ;>
19:37 <@Gynvael> 3) do zautomatyzowania niektorych rzeczy
19:37 <@Gynvael> jakich ?
19:37 <@Gynvael> na przklad wezmy te dodawania, mnozenia etc
19:37 <@Gynvael> zalozmy ze mamy jakies liczby
19:37 <@Gynvael> i chcemy zeby jakies operacje byly na nich wykonane
19:38 <@Gynvael> mozna np zrobic tablice ADRESOW funkcji, i w petli pobieraz te adresy i odpalac funkcje z tych adresow
19:38 <@Gynvael> http://gynvael.vexillium.org/pointers2/ptr_func2.c
19:38 <@Gynvael> rzucmy okiem na ten kod
19:38 <@Gynvael> int (*func[])(int a, int b) = { dodaj, dodaj, odejmij, dodaj, pomnoz, odejmij, dodaj }; // 7
19:38 <@Gynvael> pojawila sie taka tablica (prosze pamietac gdzie [] ma byc)
19:38 <@Gynvael> mamy tam adresy funkcji po kolei
19:38 <@Gynvael> czy tez nazwy operacji/przeksztalcen jakie maja byc wykonane na naszych liczbach a i b
19:38 <@Gynvael> dodaj, dodaj, odejmij, pomnoz etc
19:39 <@Gynvael> potem mamy petle
19:40 <@Gynvael> for(i = 0; i < 7; i++)
19:40 <@Gynvael> {
19:40 <@Gynvael> printf("--> a == %i, b == %i, func == %p\n",a,b,func[i]);
19:40 <@Gynvael> a = func[i](a,b);
19:40 <@Gynvael> printf("<-- a == %i, b == %i, func == %p\n",a,b,func[i]);
19:40 <@Gynvael> }
19:40 <@Gynvael> w ktorej jak widac po kolei wywolujemy kazde przeksztalcenie
19:40 <@Gynvael> a = func[i](a,b);
19:40 <@Gynvael> fajny zapis swoja droga ne ?;> func[i](a,b) ;>
19:41 <@Gynvael> oczywiscie jest to i-ty adres z tablicy, wywolany z parametrami a i b
19:41 <@Gynvael> kompilacja i odpalenie
19:42 <@Gynvael> 19:29:28 >gcc ptr_func2.c -o ptr_func2.exe
19:42 <@Gynvael> 19:41:58 >ptr_func2.exe
19:42 <@Gynvael> adresy funkcji:
19:42 <@Gynvael> dodaj == 004012E0
19:42 <@Gynvael> odejmij == 004012EB
19:42 <@Gynvael> pomnoz == 004012F8
19:42 <@Gynvael> a == 10, b == 5, func == 0022FF40
19:42 <@Gynvael> --> a == 10, b == 5, func == 004012E0
19:42 <@Gynvael> <-- a == 15, b == 5, func == 004012E0
19:42 <@Gynvael> --> a == 15, b == 5, func == 004012E0
19:42 <@Gynvael> <-- a == 20, b == 5, func == 004012E0
19:42 <@Gynvael> --> a == 20, b == 5, func == 004012EB
19:42 <@Gynvael> <-- a == 15, b == 5, func == 004012EB
19:42 <@Gynvael> --> a == 15, b == 5, func == 004012E0
19:42 <@Gynvael> <-- a == 20, b == 5, func == 004012E0
19:42 <@Gynvael> --> a == 20, b == 5, func == 004012F8
19:42 <@Gynvael> <-- a == 100, b == 5, func == 004012F8
19:42 <@Gynvael> --> a == 100, b == 5, func == 004012EB
19:42 <@Gynvael> <-- a == 95, b == 5, func == 004012EB
19:42 <@Gynvael> --> a == 95, b == 5, func == 004012E0
19:42 <@Gynvael> <-- a == 100, b == 5, func == 004012E0
19:42 <@Gynvael> 19:42:00 >
19:42 <@Gynvael> na pocatku mamy wartosci
19:42 <@Gynvael> 10 i 5
19:42 <@Gynvael> w tablicy mamy
19:43 <@Gynvael> dodaj, dodaj, odejmij, dodaj, pomnoz, odejmij, dodaj
19:43 <@Gynvael> wiec
19:43 <@Gynvael> na poczztku 10 i 5
19:43 <@Gynvael> potem dodawanie
19:43 <@Gynvael> 15 i 5
19:43 <@Gynvael> prosze zauwazyc w kodzie ze wszystkie wyniki sa ofc do 'a' zapisywane ;>
19:43 <@Gynvael> potem znowu dodaj
19:43 <@Gynvael> 20 i 5
19:43 <@Gynvael> odejmij
19:43 <@Gynvael> 15 i 5
19:43 <@Gynvael> dodaj
19:43 <@Gynvael> 20 i 5
19:43 <@Gynvael> pomnoz
19:43 <@Gynvael> 100 i 5
19:43 <@Gynvael> etc etc ;>
19:44 <@Gynvael> wiec widac ze dziala
19:44 <@Gynvael> a teraz przykald troche bardziej z zycia
19:44 <@Gynvael> mianowicie zroby sobie BAAARDZO prosty jezyk skryptowy
19:44 <@Gynvael> a w zasadzie to kalkulator ;>
19:44 <@Gynvael> http://gynvael.vexillium.org/pointers2/ptr_func3.c
19:45 <@Gynvael> co tam nowego ?
19:45 <@Gynvael> dwie funkcje
19:45 <@Gynvael> int zeruj(void) { return 0; }
19:45 <@Gynvael> int podnies(int a) { return a+1; }
19:45 <@Gynvael> jedna zwraca 0 i nie przyjmuje argumentow
19:45 <@Gynvael> a druga zwraca a+1, i przyjmuje tylko 'a' za argument
19:46 <@Gynvael> po za tym zrobilem sobie taka strukturke
19:46 <@Gynvael> struct
19:46 <@Gynvael> {
19:46 <@Gynvael> const char *what;
19:46 <@Gynvael> int (*func)();
19:46 <@Gynvael> }
19:46 <@Gynvael> dwa pla
19:46 <@Gynvael> pola
19:46 <@Gynvael> pointer na nazwe ROZKAZU
19:46 <@Gynvael> i adres funkcji WYKONUJACEJ ROZKAZ
19:46 <@Gynvael> potem jest definicja tablicy rozkazow
19:46 <@Gynvael> cmds[] =
19:46 <@Gynvael> {
19:46 <@Gynvael> { "add", dodaj },
19:46 <@Gynvael> { "sub", odejmij },
19:46 <@Gynvael> { "mul", pomnoz },
19:47 <@Gynvael> { "inc", podnies},
19:47 <@Gynvael> { "nul", zeruj },
19:47 <@Gynvael> { "esc", (void*)exit },
19:47 <@Gynvael> { NULL, NULL }
19:47 <@Gynvael> };
19:47 <@Gynvael> i teraz pare uwag
19:47 <@Gynvael> 1) tablica jak widac jest zakonczona NULL, NULL
19:47 <@Gynvael> dzieki temu bedziemy wykrywac koniec tablicy
19:47 <@Gynvael> 2) nie mamy juz pointer int (*func)(int a, int b)
19:47 <@Gynvael> tylko int (*func)()
19:47 <@Gynvael> czym to sie rozni ?
19:48 <@Gynvael> miedzy C a C++ jest taka jedna roznica
19:48 <@Gynvael> mianowicie w C++ asdf() oznacza to samo co asdf(void)
19:48 <@Gynvael> natomiast w C, asdf() oznacza ze funkcja asdf przyjmuje nieokreslona ilosc argumentow (dowolna ilosc)
19:48 <@Gynvael> prosze zauwazyc ze mam pewien rozrzut
19:48 <@Gynvael> mianowicie funkcje dodaj, odejmij i pomnoz przymuja po 2 argumenty
19:49 <@Gynvael> funkcja podnies przyjmuje jeden
19:49 <@Gynvael> a funkcja zeruj nie przyjmuje zadnego
19:49 <@Gynvael> taka jedna uwaga, mianowicie jesli podamy funkcji WIECEJ argumentow
19:49 <@Gynvael> niz ona chce
19:49 <@Gynvael> to sie jej NIC ZLEGO NIE STANIE
19:49 <@Gynvael> ;>
19:50 <@Gynvael> wiec mozemy sobie pozwolic na wywolanie funkcji nie przyjmujacej ZADNYCH argumentow z dwoma parametrami
19:50 <@Gynvael> ona po prostu z nich nie skorzysta i tyle
19:50 <@Gynvael> a
19:50 <@Gynvael> wazna rzecz
19:50 <@Gynvael> tak jest tylko w wypadku funkcji typu cdecl
19:50 <@Gynvael> (patrz konwencje wywolania funkcji)
19:51 <@Gynvael> dodam tylko ze w wiekszosci kompilator C domyslnym typem sa funkcje cdecl ;>
19:51 <@Gynvael> wiec nie ma sie co przejmowac
19:51 <@Gynvael> ok
19:51 <@Gynvael> wiec mam funkcje
19:51 <@Gynvael> add - odejmwoanie
19:51 <@Gynvael> eee ;p
19:51 <@Gynvael> add - dodawanie
19:51 <@Gynvael> sub - odejmowanie
19:51 <@Gynvael> mul - mnozenie
19:51 <@Gynvael> inc - inkrementacja
19:51 <@Gynvael> nul - zerowanie
19:51 <@Gynvael> esc - wysjscie, a dokladniej wywolanie funkcji exit
19:52 <@Gynvael> zrzutowalem ja sobie na (void*), tzn adres do niej
19:52 <@Gynvael> przypominam ze void* to adres do czegokolwiek, i dodam ze tam i tak kompilator zastosuje niejawne rzutownie do typu int (*)()
19:52 <@Gynvael> - to jak w C++ bedzie przyjmowanie ilu sie chce argumentow?
19:52 <@Gynvael> a to dobre pytanie
19:53 <@Gynvael> ja bym strzelal ze (...) ;p
19:53 <@Gynvael> ale moge sie mylic ;>
19:53 <@Gynvael> ok
19:53 <@Gynvael> ok
19:53 <@Gynvael> teraz jak wyglada algorytm:
19:53 <@Gynvael> - pobiez komende od usera
19:53 <@Gynvael> tj z klawiaury ;>
19:54 <@Gynvael> po czym sprawdz czy jest taka komenda w tablicy (porownywanie stringow za pomoca castowania sie klania ;>)
19:54 <@Gynvael> jesli znajdziesz funkcje
19:54 <@Gynvael> if(*(int*)command == *(int*)cmds[i].what)
19:54 <@Gynvael> to ja odpal
19:54 <@Gynvael> a = cmds[i].func(a,b);
19:54 <@Gynvael> i wypisz co tam sie pozmienialo
19:54 <@Gynvael> printf("a == %i, b == %i\n",a,b);
19:54 <@Gynvael> jesli nie znajdzie funkcji w tablicy
19:54 <@Gynvael> to po prostu ignoruje poleceie ;>
19:54 <@Gynvael> ok
19:54 <@Gynvael> skompilujmy i sie pobawmy
19:55 <@Gynvael> 19:55:29 >gcc ptr_func3.c -o ptr_func3.exe
19:55 <@Gynvael> 19:55:42 >ptr_func3.exe
19:55 <@Gynvael> a == 0, b == 5
19:56 <@Gynvael> inc
19:56 <@Gynvael> a == 1, b == 5
19:56 <@Gynvael> inc
19:56 <@Gynvael> a == 2, b == 5
19:56 <@Gynvael> mul
19:56 <@Gynvael> a == 10, b == 5
19:56 <@Gynvael> // te komendy ja sobie wklepuje z klawiaturki ;> skompilujcie sobie i sie pobawcie ;>
19:56 <@Gynvael> nul
19:56 <@Gynvael> a == 0, b == 5
19:56 <@Gynvael> esc
19:56 <@Gynvael> 19:56:33 >
19:56 <@Gynvael> jak widac dziala ladnie ;>
19:56 <@Gynvael> gdzie taki mechanizm sie stosuje ?
19:56 <@Gynvael> NickServ i ChanServ ;>
19:57 <@Gynvael> kojarzycie ?
19:57 <@Gynvael> ich komendy sa wlasnie w takiej tablicy z pointerami na funkcje ;>
19:57 <@Gynvael> http://gynvael.vexillium.org/?stuff=ircbot.stuff
19:57 <@Gynvael> to jest przykaldowy bot ircowy
19:58 <@Gynvael> ktory komendy od servera (PING, JOIN etc) rowniez ma wklepane wraz z funkcjami obslugujacymi w tablicy
19:58 <@Gynvael> handle_proc_t handles[ ] = { { "PING", handle_ping }, { "PRIVMSG", handle_msg }, { "JOIN", handle_join },
19:58 <@Gynvael> etc
19:58 <@Gynvael> OK
19:58 <@Gynvael> W sumie mi sie czas konczy
19:58 <@Gynvael> mialem jeszcze mowic o pointerach do tablic
19:58 <@Gynvael> ale coz
19:58 <@Gynvael> dam wiec linki do przykladow
19:58 <@Gynvael> http://gynvael.vexillium.org/pointers2/ptr_arr1.c
19:58 <@Gynvael> http://gynvael.vexillium.org/pointers2/ptr_arr2.c
19:59 <@Gynvael> jako zadanie domowe - przeanalizujcie sobie
19:59 <@Gynvael> http://pweb.netcom.com/~tjensen/ptr/pointers.pdf
19:59 <@Gynvael> to jest baaaardzo dobry imho dokument o pointerach w C, po angielsku niestety
20:00 <@Gynvael> http://forum.wyklady.net/index.php?topic=42.0
20:00 <@Gynvael> o
20:00 <@Gynvael> a tam mozna ocenic moj dzisiejszy wyklad
20:00 <@Gynvael> zapraszam do komentowania ;>
20:00 <@Gynvael> i dziekuje za uczestniczenie ;>
20:00 <@Gynvael> za jakis tydzien z hakiem prawdopodobnie poprowadze cos o programowaniu siecowym w C ;>
20:00 <@Gynvael> dziekuje