3 pytania
1 pytanie to funkcja z tablicami nie używając nawiasów kwadratowych. Kopiowanie tablic. Podstawowy wzór języka C.
2 pytanie deklaracja wskaźnika
3 pytanie to teoretyczne… coś o wskaźnikach, wszystko szczegółowo podać… kiedy operacje mają sens itp.
Pełna odpowiedź daje ocenę dostateczną.
Wskaźnik jest zmienną lub stałą zawiarającą adres innej zmiennej. Najczęściej są czterobajtowe.
Operator wyłuskania (*). Jego argumentem jest jakieś wyrażenie wskaźnikowe (jest to takie, które po policzeniu staje się wskaźnikiem)
*wsk <- definicja operatora wyłuskania
+ -> na początku nie było go w C. Podobno nic nie robi, ale może zepsuć program. Zwraca on wartość, a nie zmienną A.
- -> negacja arytmetyczna
A – zmienna
+A – jest wartością tej zmiennej
*wsk <- Tryb podstawowy
Int *wsk; <- deklaracja wskaźnika wsk
Kkp rysunek
Int zm;
Int *wsk = &zm
Int **wsk1; <- będzie typu podstawowego (przez to że są dwa wsk?)
Jak wskaźniki mają zerową wartość to znaczy że na nic nie wskazują. Jak ma wartość przypadkową to nie możemy na co wskazują?
Operator pobrania adresu
Operator pobrania adresu – zwraca adres. Np. zbada zm i zwróci jej adres. Możemy go wstawić do wskaźnika.
Wsk = & zm;
Znaczek – & ? ^^
& zm; <- nie może być stała, musi być jakaś zmienna. Zmienne mają adres a stałe nie mają
*wsk=1000; <- nie zmieni wsk. Zmieni zmienną zm. <-> zm=1000;
Wsk1 =&wsk;
**wsk1; <- dwie gwiazdki będzie to samo co zm. <-> zm = 2000;
We wskaźniki nie można przyjmować wartości? Sami nie możemy tych wartości sami wymyślać. Wartości całkowitych. Z wyjątkiem 0. Zero możemy sobie sami wpisać
Int tab[10];
TUTAJ MA BYĆ TA CHORA TABELKA ^^
Int *wsk=& tab[0];
++wsk; <- zamiast 4001 będzie 4004
wsk=wsk+2
Skalowanie wskaźników polega na tym że jeżeli do wskaźnika dodajemy liczbę całkowitą. To ta liczba jest mnożona przez długoś elementu na który wskazuje wskaźnik i dopiero ta pomnożona liczba jest dodawana do adresu.
Przykład: wsk=wsk+2
Jest także wskaźnik typu „pustego”?
void*wsk <- wskazuje na typ nieokreślony, nie można z niego wyłuskać oraz nie można nic dodawać. Kompilator nie wie jaka jest długoś elementu.
Char*wsk <- do tego podobno można dodawać
Dodając liczby całkowite musimy sami sprawdzać aby nie wyjść poza adres/wielkość tablicy. Żeby się nie znaleźć z adresem poza tablicą. System ani kompilator nie będą nam sprawdzać ^^.
NIE MOŻNA DODAWAĆ JAKICHKOLWIEK LICZB DO WSKAŹNIKÓW, można tylko zero.
WSK=1
NIE MOŻEMY PRZEKROCZYĆ TABLICY
Jeżeli za bardzo przekroczymy tablice to możemy wejść na adresy systemu operacyjnego? Czy coś takiego. Dopisać….
Przyrównywanie i inne operacje na wskaźnikach:
Wsk++
Wsk—
Wsk+n
Wsk-n
Wsk==0
Wsk!=0
Wsk==wsk1
Wsk!=wsk1
!wsk // gdy wskaźnik jest równy 0
!!wsk // gdy wskaźnik jest różny od zera
Można porównywać wskaźniki ze sobą np. wsk > wsk1 lub takie jak >= < <=
Wskaźniki można między sobą odejmować wsk – wsk1
Jest także operacja np.
Double *wsk;
Int z;
Wk=&z; <- Nie da się
Wsk=(double*)&z; <- Jest to tak zwane rzutowanie wskaźników
*wsk=15;
*(int*)wsk=15; // wyłuskane z? Wartość 15 będzie w zmiennej z.
Wsk=(double*)1; <- tak się podobno da.
Podstawowy wzór języka C
*(wsk+1)=wsk[i];
Operator wyłuskania = [i] <- pobranie elementu z tablicy
Tu są 3x równa się xD. To znaczy 3 kreski. Że są równoważne
Rezerwowanie tablicy int tab[10] <- zadeklarowaliśmy tablice o wielkości 10. 40 bajtów zarezerwowanych
Int tab[10];
Tab jest uważane za stały wskaźnik.
Są modyfikatory 0, 1, 2. Są liczbami całkowitymi.
Tab[0] <- 4 bajty
Bierzemy adres tab[0] i wyłuskujemy 4 bajty z niego?
Tab[1] to 4 bajty dalej położone. *(Tab+1) do adresu tab dodajemy jedynkę. Wykonywane jest skalowanie wskaźników, jeden jest mnożony przez 4. Więc do adresu tab będą dodane 4 bajty. Nastąpi wyłuskanie i będą to te 4 bajty.
Int z;
Int *wsk=&z;
Te na dole są identyczne!! Te 3 na dole ^^. To chyba przypisywanie liczby
*wsk=150;
*(wsk+0)=150
Wsk[0]=150;
z=150; <- to też jest to samo. Tylko łatwiejsze
Deklarowanie wskaźników związanych z tablicami:
Tablica 10 wskaźników
*tab[k]
Int *tab[10]; <- deklaracja tego wskaźnika na tablicy. Czy jakoś takoś.
(*wsk)[k] *wsk[k] <- to drugie zabrałoby nam k przed wyłuskaniem. Chuj wi co to znaczy
Int (*wsk)[20]; <- 20 elementowa
Int(*wsk1)[60] <- wsk1 jako tablica 60 elementowa
Wzór int(*wsk[)]
Tablica 10 wskaźników
RYSUNEK TEGO GÓWNA ŚMIESZNEGo
*tab[2] == tb[0]
Tutaj kolumny są różnej długości. Kolumna 2 ma długość 50, kolumna też ma jakąś długość… pozostałe kolumny nie istnieją?
Int tb[50]
Tab[2]=&tb[0];
Jedno zadanie będzie polegało na napisaniu prostej funkcji bez użycia nawisów kwadratowych. To chyba na kolos? Musimy znać obie formy
Czy to jest wskaźnik? Nie jest bo nie ma operatora wyłuskania…
Int fun(int,int);
Aby uzyskać wskaźnik do funkcji o znaneym prototypie. Należy zastąpić w nim nazwę funkcji przez (* nazwa_wskaźnika)
Np. int (*wsk)(int, int);
FUNKCJA WSK - Int *wsk (int,int) <- priorytet nawiasów jest większy…. Gwiazdka jest operatorem o jeden priorytet mniejszym. Mamy funkcje *wsk i ta funkcja ma dwa parametry całkowite a później zwraca wskaźnik do int’a. To nie jest wskaźnik.
Inne przykłady deklaracji:
Zamiast t była nazwa funkcji int nazwa()
Int (*t[12])(); <- tablica wskaźników. Funkcja t[i] jest jakimś wskaźnikiem. Jak jest wyłuskanie to uzyskujemy funkcje bez parametrów i zwraca int. Jeżeli mamy coś takiego to należy oczekiwać na to t[i] możemy podstawić adres funkcji. Jeżeli mamy int fun(); to możemy podstawić adres przez t[i]=&fun; lub t[i]=fun;
Double (*(*t)())(); <- A CHUJ NIE WIEM CO ON TU GADA!
Gwiazdka musi być użyta do nazwy…
To jest odwrotne do tego na górze! To na dole nie jest już wskaźnikiem gdyż * ma priorytet niższy niż nawiasy okrągłe. Jest to deklaracja jakieś funkcji fun
Double (*fun())();
ZADANIE KURWA!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Zadeklarować tablice 10 wskaźników. Do funkcji całkowitych mających dwa parametry int i double*.
int (*tab[10])(int, double*); <- nikt nam nie zabierze tablicy.. później () aby nikt nam tego wszystkiego nie zabrał.
Zadeklaruj wskaźnik do tablicy 10 wskaźników do funkcji całkowitych…
int (*tab[10])(int, double*); <- tab zastępujemy przez wskaźnik
czyli
int(*(*wsk)[10])(int, double*);
int fun1(int, int);
int fun2(int, int);
int main()
{
int(*wsk)(int, int);
if(getch()==’+’) wsk=fun1; <- jeżeli napisze + to pod wsk podstawimy adres funkcji fun1
else wsk=fun2; <- jeżeli coś innego to podstawi się adres fun2
printf(„%d”), wsk(3,5)); <- tutaj robimy użycie wsk
int fun1(int a, int b)
{
return a+b;
}
Int fun2(int a, int b)
{
Return a*b;
}
W programie wielokrotnie musi robić zamianę dwóch zmiennych… No więc… doszedł wniosku że tyle razy ma to zamieniać więc pomyślał że zrobi sobie fuknkcję, która sama to będzie robić. Przez zmienną tymczasową. I napisał sobie funkcję.
Void swap(int *a, int *b)
{
Int temp;
Temp=*a;
*a=*b;
*b=temp;
}
Void main()
{
Int=5, y=7;
Swap(&x,&y);
Printf(„%d, x);
}
Tablice dynamiczne
Int ile, *tab; <- deklaracja tablicy, zmienna ile pokazuje ile danych będzie potrzebnych do przebiegu.
Scanf(„%d”, &ile); <- przypisanie wielkości tablicy
tab=(int*)calloc(ile, sizeof(int)); LUB tab=(int*)malloc(uke*suzeif(int));
if może być używane do wyrażeń wskaźnikowych
if(!tab)
{
printf(„Out of memory”);
Exit(1); <- exit 1 oznacza zakończenie niepoprawne, a exit 0 oznacza zakończenie poprawni
}
Tab[ind] 0 <= ind <i le
! też może być przy wskaźniku… chuj wi na co ale zawsze coś.
FUNKCJE PRZYDZIAŁU PAMIĘCI
Void *malloc(size_t il); <- size_t typ zmienna całkowita bez znaku malloc = alloc.h, stdlib.h
NULL – nie ma <- Out of memory
Malloc – przypisuje podobno adresy pamięci (Tak maniek mówił)
Void *calloc(size_t il, size_t n); <- Tutaj ma dwa parametry. Pierwszy długość bloku, drugie ilość bloków. Mnoży te dwa parametry przez siebie. Calloc zeruje przydzieloną pamięć.
Funkcja
Void *realloc(void *adres, size_t il); <- służy do zmiany przydzielonego rozmiaru. Może być funkcją „kombajn”
Void free(void *adres); <- zwalnia nasze obszary. Nie może być stosowana dla obszarów przydzielonych statycznie.
Int set_new_mode(int mode) <- zero normalnie
_PNH set_new_handler(_PNH handller_);
Set_new_mode(1);
Int handller(size_t il)
{
Printf(„Out of memory”);
Exit(1) <- jedna z możliwości
Zwraca 0 -> zwróć NULL gdzie był nieudany przydział
Zwraca 1 -> powtórz przydział
}
Set_new_handller(handller);
Int ile, *tab;
Scamf(„%d”, *ile);
Tab=(int **) malloc (ile *sizeof(int*));
If(!tab) ….. exit(1)
Int zamienieliśmy przez typ gwiazdki a to jest podobno to samo coś tam na górze…
Deklaracja tablicy dynamicznej?
Elen – tablicadynamiczna zawierająca ile liczb całkowitych
Int k,num;
for(k=0;k<ile;k++)
{
scanf(„%d”, &num);
elen[k]=num; <- num to ilość elementów w kolumnie
if(!num)tab[k]=NULL;
else
{tab[k]=(int*)
calloc(num, sizeof(int));
if(!tab[k]) …. exit(1);
}
}
Tab[i][j] 0 <= i <= ile-1 // - <= j <= elen[i] – 1
Wydaje mi się że to jest inny dział xD
Char *s; <- wskaźnik do bajtu. Za tym bajtem może być więcej bajtów. Wskaźnik niezainicjowany bo s na nic nie wskazuje.
Char *s1 = „ABC”; <- to jest już zainicjowane. Przy drugim zapisie rezerwuje obszar 4 bajtów do którego wpisuje ABC (+ binarne zero i dlatego są 4 bajty)
Kompilator rezerwuje s1 i jest zainicjowany bo wskazuje na tekst ABC.
Char t[81]; <- W tym przypadku rezerwuje 81 bajtów i nazywa to t.
Między t a s1 różnica jest: przy t możemy napisać t[0]=’x’; t[1]=y; t[2]=’z’; t[3]=’0’ w bajcie zerowym będzie x, w pierwszym y
ZDJĘCIA Z APARATU!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Tablica trzywskaźnikowa bo są trzy napisy.
Char *tb[]= {tekst1, tekst2, tekst3} tb ma 3 elementy
REKLAMY
Const
Volatile
Const double pi=3,14;
Pi=1;
Const char*p (samo p może być zmienione)
Możemy napisać p=*bajt; <- możemy zmienić adres tego p
Ale nie możemy napisać
*p = x;
Char *const r;
R= &bajt; <- Nie możemy tak napisać!!!!! To byłoby źle
Natomiast możemy napisać:
*r = ‘x
#include <string.h>
Str
Strlen(const char *s); < funkcja nie zmieni nam łańcucha s
Strlen <- Bada długość łańcucha wskazywanego przez ten wskaźnik s.
Strle(s) -> 3 (Zwróci nam wartość, 3 )
S -> A B C 0 <- tabelka ghiuhihih xD
Strlen („AB”); <- Zwróci nam 2 ^^
Sizeof („AB”) <- podaje wartość fizyczną. Zwróci 3 (razem z tym zerem ghihihi)
Sizeof (s) <- zwróci 4 (znów razem z zrem)
Implementacja funkcji strlen
Strlen(const char *s) strlen („AB”);
{
char *t = s;
while(*t(t++;
return t-s;
}
{
Int n;
For(n=0;*s;s++)n++;
Return n;
}
Char *strcpy (char *dst, const char *src); <-Kopiuje łańcuchy. Z drugiego kopiuje na pierwsze
{
While(*dst++ = *src++);
}
KOLOKWKIUM za 3 tygodnie 24 kwietnia
char *strcat (char *dst, const char *s)
tworzy konkatenację (concatenation/połączenie) łańcuchu. Porządny łańcuch z zerowym coś tam O.o.
„AB” „CD” <- dst. „ABCD”
Int strcmp(const char*s, const char*t)
S<t
“ab” leks< “cd” leks <- leksykalne/słownikowe i takie tam
Strcmp <-
>0 s lek>t
< 0 s lek<t
=0 s == t (zwraca zero gdy s ==t)
(przegląda poszczególne bajty s i t do momentu gdy w obu bajtach znajdą się bajty zerowe. Jeżeli przy przeglądaniu jeden bajt będzie różny na tej samej pozycji to jeżeli typ s jest większy od t do będzie zwrócona wartość większa od zera)
(funkcja odwaraca wartości większe od zera)
(mniejsze od zera gdy s jest mniejsze od zera)
„Ab” leks < „c”
„Ab” leks > „Cxx”
„Abca” leks < „abcag”
Strcmpi
„ABC” === „aBc”
Stricmp
Char *strhr (const char*s, int c);
Funkcja przeszukuje łańcuch s i szuka kodu w ANSCII C. Jak nie będzie to zwaraca zerowy
Strchr („ABCD”, ‘C’); <- nie będzie zerowy
#0 <- strchr („ABCDC”, ‘C’);
Char *p = strchr (str, ‘C’);
It (!p) nie ma dużego C w str
Else
Strchr (p, ‘C’) (Szuka drugiego C w łańcuchu str )
Char * strstr (const char *s, const char*t) <- Szuka wystąpienia łaćucha t w s
Strstr („ABCD”, „XYZ”) -> 0
Strstr(„ABCD”, „BC”)
Char *Strset(char *s, int fill); <- (Wypełnia łańcuch s kodem ANSCII fill)
Chat tab[] = “ABCDE”;
Strset(tab+3, ‘X’);
Tab ABCXX
Char *strupr(char *s) <- wszystkie małe na duże
Strlwr <- wszyskie duże na małe
Char *Strn (char *t, const char*s, size_t n) <- n to dodatkowy parameter który pokazuje ile jest znaków do przetworzenia
Char *strncpy (char *t, const char*s, size_t n;)
Char *strncat (….. ,size_t n);
Strncmp (………….. ,size_t n);
Strncmp(„ABX”, „ABcd”, 2) -> 0
Strnset(…. , size_t n);
Char tab[] = “ABCDE”;
Tab[3] = ‘x’ ==== strnset (tab+3, ‘X’ 1)
Tab+3 ABCXE
Mem
void *memcpy (boid *dst, const void*src, size_t n); <- jeżeli się nakładają to nie będzie dobrze działać. Kopiuje.
Memove
Int memcmp (const void *s1, const void *s2, size_t n); <- Szuka bajtów, które się nie różnią to wychodzi 0.
Void *memset(void *dst, int c, size_t); <- Ustawia coś, tyle razy go ustawia ile jest napisane w 3 parametrze.
Memset (tab, 0,sizeof(tab));
Memccpy (void *dest, const void*src, int c, size_t n); <- też kopiuje. Ale może wcześniej skończyć kopiowanie. Jeżeli jest bajt o wartości C to kończy kopiowanie
0 gdy nie wystąpiło C ||||||| Gdy wystąpiło C to wyświetla adres następnego bajtu po tym C