int - Politechnika Śląska
Transkrypt
int - Politechnika Śląska
Programowanie komputerów Jacek Lach Zakład Oprogramowania Instytut Informatyki Politechnika Śląska Plan • Tablice • Wskaźniki Jacek Lach. Programowanie komputerów • Zarządzanie pamięcią Tablice Jacek Lach. Programowanie komputerów • Składnia: <nazwa_typu> <nazwa_tablicy>[liczba_elementów] • Elementy tablicy numerowane są od 0 do liczba_elementów 1 • Dostęp do elementu tablicy: <nazwa_tablicy>[indeks] gdzie indeks jest wyrażeniem określającym numer elementu w tablicy Tablice Jacek Lach. Programowanie komputerów int tab[5]; /* 5-elem. tablica zmiennych typu int */ int suma, i; /* dostęp do elementów tablicy – suma elementów */ suma = tab[0]+tab[1]+tab[2]+tab[3]+tab[4]; /* suma - inne rozwiązanie */ for ( i=0, suma=0; i<5; suma+=tab[i++] ); Tablice • Elementami tablic mogą być: • Liczby całkowite Jacek Lach. Programowanie komputerów • Liczby rzeczywiste • Znaki • Wskaźniki • Struktury, unie • Inne tablice • <typ> <zmienna>[wym1][wym2]...[wymN] • int tab1[3] • int tab2[2][3] • int tab3[4][2][3] Inicjalizacja • int tab1[3] = {1,2,3}; • int tab2[20] = {1,2,3};/* co na pozostałych */ /* pozycjach? */ Jacek Lach. Programowanie komputerów • • int tabL[3000] = {0}; /* sposób na wyzerowanie */ /* tablicy */ • • int tab3[2][3] = {{1,2,3},{4,5,6}}; • int tab3[2][3] = {1,2,3,4,5,6}; /* efekt j.w */ • int tab4[2][3] = {{1},{4}}; • int tab5[2][3] = {1,4}; /* efekt? */ • int tab6[] = {2,4,6}; /* rozmiar dobierany */ • /* automatycznie */ Przykład #include<stdio.h> int main(void) Jacek Lach. Programowanie komputerów { int tab1[3][2] = {{33, 93}, {-103, 903}}; int tab2[3][2] = {3, 5, 7, 13}; tab1[0][0] = 1000; tab2[1][1] = tab1[1][0]; printf("\n %d\n %d", tab1[1][0], tab2[1] [1]); return 0; } Jacek Lach. Programowanie komputerów Tablice i łańcuchy znakowe • Definiowanie zmiennej przechowującej łańcuchy (teksty): jako tablicy znaków: char nazwa[długość] na przykład: char s[20]; • Na końcu łańcucha jest znak ’\0’ • Uwaga na długość! • Funkcje operujące na łańcuchach Łańcuchy znakowe • char tab4[3] = {‘a’, ‘b’, ‘c’}; //czy to jest łańcuch? Jacek Lach. Programowanie komputerów • char tab4[] = {‘a’, ‘b’, ‘c’}; • char tab5[4] = { ‘a’, ‘b’, ‘c’,’\0’}; • char tab5[4] = „abc”; • char tab5[] = „abc”; • char tab5[] = {„abc”}; Łańcuchy znakowe #include <stdio.h> #include <string.h> Jacek Lach. Programowanie komputerów int main(void) { char s[200] = "0123456789"; printf("Dlugosc lancucha \"%s\" wynosi %d\n", s, strlen(s)); printf("Rozmiar lancucha = %d\n", sizeof(s)); printf("Pierwszy element = %c \n", s[0]); return 0; } Liczenie znaków #include <stdio.h> Jacek Lach. Programowanie komputerów /* zliczamy cyfry, odstępy i inne znaki */ main() { int c, i, nwhite, nother; int ndigit[10]; nwhite = nother = 0; for (i = 0; i < 10; ++i) ndigit[i] = 0; Jacek Lach. Programowanie komputerów Liczenie znaków while ((c = getchar()) != EOF) if (c >= '0' && c <= '9') ++ndigit[c-'0']; else if (c == ' ' || c == '\n' || c == '\t') ++nwhite; else ++nother; printf("cyfry: "); for (i = 0; i < 10; ++i) printf(" %d", ndigit[i]); printf(", odstępy: %d, inne: %d\n", nwhite,nother); } return 0; Wyjście: cyfry: 10 3 0 0 0 0 0 0 0 1, odstępy: 306, inne: 372 Typy wskaźnikowe • Wskaźnik – symboliczna reprezentacja adresu Jacek Lach. Programowanie komputerów • scanf(”%d”, &wiek) &wiek jest stałą typu wskaźnik • Przed użyciem wskaźnik powinien zostać zainicjalizowany • Zmienne typu wskaźnik: • char *wsk_a; • int *wsk_i; • char **wsk_wsk; • void *ptr; Typy wskaźnikowe Jacek Lach. Programowanie komputerów • Inicjalizacja: • • • • • int k = 3; int *w = &k; int *p_int = NULL; char *p_char = NULL; char **pp = NULL; • Przypisania: • • • • • void *ptr; int w; w = *p_int; ptr = p_char; p_char = (char*)ptr; Jacek Lach. Programowanie komputerów Dostęp do zmiennej int main(void) { char c = 'a', ch = 'z', *pz = &c; int i = 22, *pi = &i; double d = 3.2e-3, *pd = &d; *pz = 'b'; // c = 'b'; *pi = 2006; //i = 2006; *pd = 1.2; pz = &ch; *pz = 'A'; //ch = 'A'; c = *pz; //c = 'A'; return 0; } Jacek Lach. Programowanie komputerów Operacje na wskaźnikach • Wskaźnikowi można przypisać inny wskaźnik do tego samego typu: int *ptr_a; int *ptr_b; ... ptr_a = ptr_b; • Wskaźniki mogą być: • powiększane o wartość całkowitą (skalowaną!). Dodanie 1 do wskaźnika powiększa adres o rozmiar wskazywanego typu • pomniejszane o wartość całkowitą (skalowaną!) • porównywane • Dodawanie liczby całkowitej do wskaźnika Jacek Lach. Programowanie komputerów • Czynnik skalujący: sizeof(typ_elementów_tablicy) • Dla operacji wsk = ptr + 4 wynikiem jest dodanie 4*sizeof(char) do adresu przechowywanego w ptr i przypisanie adresu wynikowego do wsk Dodawanie liczby całkowitej do wskaźnika double *pd; double t[10]; Jacek Lach. Programowanie komputerów pd = &(t[5]); printf(”%p\n”,pd); /* 0065FB60 */ pd = pd + 3; printf(”%p\n”,pd); /* 0065FB78 */ /* teraz pd wskazuje na t[8] */ Operacje na wskaźnikach • Odejmowanie – jeżeli oba wskaźniki wskazują na elementy tej samej tablicy Jacek Lach. Programowanie komputerów • Wynik: liczba elementów pomiędzy wskaźnikami • Typ wyniku: ptrdiff_t, zdefiniowany w <stddef.h> (można też używać size_t) • Porównywanie – jeżeli oba wskaźniki wskazują na elementy tej samej tablicy • Wynik pt < wsk jest prawdą, jeżeli pt wskazuje na element tablicy z indeksem mniejszym niż indeks elementu, na który wskazuje wsk Wskaźniki i tablice • Nazwa tablicy jest synonimem adresu jej początkowego elementu Jacek Lach. Programowanie komputerów • Przykład: double x[5]; double *p; p = x; /* tak jak p = &(x[0]); */ *p = 12; /* *p odnosi się do x[0] */ p++; /* teraz p wskazuje na x[1] */ *p = 13; /* tutaj x[0]=12, x[1]=13 */ Jacek Lach. Programowanie komputerów Wskaźniki i tablice /* tablica */ int tab[5], c; /* wskaźniki na */ int *pa, *pb; pa = &tab[0]; pb = tab+2; c = *(tab+4); tab PaO pa int element nr 0 pb element nr 1 element nr 2 element nr 3 c element nr 4 Nazwy tablic a wskaźniki • ptr + 1 – obiekt następny po ptr • ptr + ind – element oddalony o ind miejsc od ptr Jacek Lach. Programowanie komputerów • &tab[ind] ≡ tab + ind • Dostęp do elementów tablicy: • *tab, *(tab+1), *(tab+2), ... • *(ptr+ind) ≡ ptr[ind] • wskaźnik + przesunięcie ≡ tablica i indeks Nazwy tablic a wskaźniki • Nazwa tablicy nie jest zmienną! • => np. nie może być zwiększana Jacek Lach. Programowanie komputerów • ptr++ dozwolone • tab++ zabronione • Ciekawostka: • t[i] ≡ *(t+i) ≡ *(i+t) ≡ i[t] t[5] ≡ 5[t] • sizeof Jacek Lach. Programowanie komputerów Kontrola zakresu • ? Inne błędy • int *p; *p = 5; Jacek Lach. Programowanie komputerów • char s[3]=”Ala”; • int k; scanf(”%d”, k); • char *buf; scanf(”%s”,buf); Wyprowadzanie łańcucha #include <stdio.h> main() Jacek Lach. Programowanie komputerów { char s[]="Hello again!\n"; char *p; p = s; while (*p) putchar (*p++); return 0; } Nasza pamięć Jacek Lach. Programowanie komputerów • Człowiek dysponuje (co najmniej) dwoma rodzajami pamięci: • dużą: trwałą, ale trudną w zapisie (czas zapisu jest długi) • małą: zanikającą w ciągu kilku sekund, ale łatwą do zapisania • Mała oznacza naprawdę małą pamięć: • przechowuje około 7 obiektów Nasza pamięć • Wczesne oszacowania pojemności: 10^20 bitów • Zdolność zapamiętywania: 2 bity / sekundę Jacek Lach. Programowanie komputerów • ...czyli w ciągu życia: ok. 10^9 bitów (200 MB) • Moc obliczeniowa: ok. 10^12...10^14 oper / s (kilka ... kilkadziesiąt TFLOP) • Operacje sekwencyjne: ok. 1000 oper / s • Moc pobierana: ok. 10 W • http://www.merkle.com/humanMemory.html Funkcje • Człowiek potrafi analizować jednocześnie około 7 obiektów Jacek Lach. Programowanie komputerów • Co stanie się, jeśli program ma więcej niż 7 linii? • Program należy podzielić na funkcje: • Zapisać kilka linii kodu i nadać im nazwę • Uruchamiać je stosując nazwę • Oczywiście, nie jest to jedyny powód, dla którego należy stosować funkcje... Definicja funkcji Jacek Lach. Programowanie komputerów • Definicja funkcji ma następującą postać: • typ_wyniku nazwa_funkcji(opcjonalne deklaracje_parametrów) { deklaracje instrukcje } Przykład /* power: podnoszenie base do n-tej potęgi; n >= 0 */ Jacek Lach. Programowanie komputerów int power(int base, int n) { int i, p; /* zmienne lokalne */ p = 1; for (i = 1; i <= n; ++i) p = p * base; return p; } Przykład Jacek Lach. Programowanie komputerów /* test funkcji power */ Wyniki: main() 0 1 1 { 1 2 -3 int i; 2 4 9 3 8 -27 4 16 81 for (i = 0; i < 10; ++i) 5 32 -243 printf("%d %d %d\n", 6 64 729 i, power(2,i), power(-3,i)); 7 128 -2187 return 0; 8 256 6561 9 512 -19683 } Parametry i wyniki funkcji Jacek Lach. Programowanie komputerów • Pierwszy wiersz funkcji power, int power(int base, int n) deklaruje typy parametrów (argumentów formalnych) i ich nazwy oraz typ wyniku funkcji • Wartość wyznaczana przez power jest zwracana instrukcją return • Funkcja nie musi zwracać wartości • Zwracana wartość może zostać zignorowana Deklaracja (prototyp) funkcji Jacek Lach. Programowanie komputerów • Kompilator musi wiedzieć, że dany identyfikator oznacza funkcję, przed jej wywołaniem; musi również znać listę parametrów • W języku C można: • zdefiniować funkcję przed jej wywołaniem, lub • zadeklarować funkcję przed jej wywołaniem stosując prototyp i zdefiniować ją później • Deklaracja, nazywana prototypem funkcji, musi zgadzać się z definicją funkcji Jacek Lach. Programowanie komputerów Przekazywanie wartości do funkcji void zamien_1(int x, int y) void zamien_2(int *x, int *y) { { int temp; int temp; temp = x; temp = *x; x = y; *x = *y; y = temp; *y = temp; } } int main(void) { int a = 0, b = 7; zamien_1(a, b); printf("%d %d\n", a, b); zamien_2(&a, &b); printf("%d %d\n", a, b); return 0; } Przekazywanie wartości do funkcji Jacek Lach. Programowanie komputerów • Wszystkie argumenty są przekazywane przez wartość • Wywoływana funkcja otrzymuje kopie przekazanych wartości w zmiennych lokalnych • Aby uzyskać efekt przekazania argumentu przez zmienną (jak w języku Pascal), można użyć wskaźnika jako typu parametru i przekazać funkcji adres miejsca, które ma zostać zmodyfikowane Jacek Lach. Programowanie komputerów Przekazywanie tablic do funkcji #define MAX 7 void drukuj(int w[]); // void drukuj(int *w); main(){ int wektor[MAX] = {100, 200, 300}; drukuj(wektor); return 0; } void drukuj(int w[]){ ... } • Definicje parametrów int w[] i int *w są równoważne • Dostęp do elementów: w[2], *(w+2), itd... • Operacje na wskaźniku w dotyczą tablicy wektor Przekazywanie tablic do funkcji Jacek Lach. Programowanie komputerów • Po zastosowaniu nazwy tablicy jako argumentu funkcji, przekazywany jest tylko adres początkowego elementu tablicy • Nie są kopiowane elementy tablicy! • Do funkcji można przekazać część tablicy, np. drukuj(&wektor[4]) lub drukuj(wektor+4) Wykorzystanie wskaźników Jacek Lach. Programowanie komputerów void drukuj(int w[]) { int i; for(i = 0; i < MAX; i++) printf("%d\n", w[i]); } void drukuj(int *w) { int i; void drukuj(int w[]) { } int i; for(i = 0; i < MAX; i++, w++) printf("%d\n", *w); } for(i = 0; i < MAX; i++) { printf("%d\n", *w); w++; //przejdź do //następnego elementu } Tablice wielowymiarowe Jacek Lach. Programowanie komputerów • C pozwala definiować „prostokątne”, wielowymiarowe tablice • Tablica definiowana jako dwuwymiarowa jest w rzeczywistości jednowymiarową tablicą elementów, z których każdy jest jednowymiarową tablicą • Składnia: jak dla tablic jednowymiarowych • Przykład: • double matrix[5][10]; • matrix[3][7]=45.67; Tablice wielowymiarowe Jacek Lach. Programowanie komputerów • Elementy są umieszczane w pamięci wierszami • Najbardziej prawy indeks (nr kolumny) zmienia się najszybciej przy przeglądaniu kolejnych komórek pamięci • matrix[wiersz][kolumna] Inicjalizacja Jacek Lach. Programowanie komputerów • Tablica jest inicjalizowana listą inicjalizatorów umieszczonych w nawiasach klamrowych • Każdy wiersz tablicy dwuwymiarowej jest inicjalizowany odpowiednią podlistą • Przykład: • double m[2][3] = {{1.2, 2.3, 3.4}, {5.4, 4.3, 3.2}}; Przykład Jacek Lach. Programowanie komputerów Problem: przeliczyć miesiąc i dzień na dzień roku static char daytab[2][13] = { {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, }; /* day_of_year: ustalenie dnia roku mając miesiąc i int day_of_year(int year, int month, int day) { int i, leap; leap = year%4 == 0 && year%100 != 0 || year%400 for (i = 1; i < month; i++) day += daytab[leap][i]; return day; } 31}, 31} dz. */ == 0; Jacek Lach. Programowanie komputerów Przykład Problem: przeliczyć dzień roku na miesiąc i dzień static char daytab[2][13] = { {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; /* month_day: ustalenie miesiąca i daty mając dzień roku */ void month_day(int year, int yearday, int *pmonth, int *pday) { int i, leap; leap = year%4 == 0 && year%100 != 0 || year%400 == 0; for (i = 1; yearday > daytab[leap][i]; i++) yearday -= daytab[leap][i]; *pmonth = i; *pday = yearday; } Wskaźniki i tablice wielowymiarowe • Rozważmy następujące definicje: int A[3][4]; int *B[3]; Jacek Lach. Programowanie komputerów • Wskaźniki i tablice wielowymiarowe Jacek Lach. Programowanie komputerów • for(i = 0; i < 3; i++) B[i] = &A[i][0]; • for(i = 0; i < 3; i++) B[i] = A[i]; • Wskaźniki i tablice wielowymiarowe • int A[3][4] Jacek Lach. Programowanie komputerów • jest prawdziwą tablicą dwuwymiarową • 12 elementów rozmiaru int zostało alokowanych w pamięci • do wyznaczenia indeksu elementu A[row,col] stosowany jest wzór row * 4 + col Jacek Lach. Programowanie komputerów Tablice wielowymiarowe int main(void) { int A[3][4] = {{1}, {2}, {3}}; int *B[3]; int i, j; for(i = 0; i < 3; i++) B[i] = &A[i][0]; for(i = 0; i < 3; i++) { for(j = 0; j < 4; j++) { printf("%6d", A[i][j]); printf("%6d", B[i][j]); } printf("\n"); } } Tablice wielowymiarowe Jacek Lach. Programowanie komputerów • B, jest tylko tablicą 3 wskaźników i nie inicjalizuje ich; inicjalizacja musi zostać dokonana jawnie, statycznie lub podczas działania programu. Jacek Lach. Programowanie komputerów Tablice wielowymiarowe • Porównajmy definicje i schemat dla tablicy wskaźników: char *name[] = {"Illegal month", "Jan", "Feb", "Mar"}; • z ich odpowiednikami dla tablicy dwuwymiarowej: char aname[][15] = { "Illegal month", "Jan", "Feb", "Mar" }; Przykład Jacek Lach. Programowanie komputerów • Wczytać słowa z pliku i wyprowadzić słowa uporządkowane alfabetycznie • Rozwiązanie 1: char tab[100][35]; char t_wyn[100][35]; Jacek Lach. Programowanie komputerów Przykład • Rozwiązanie 2: z wykorzystaniem jednowymiarowej tablicy wskaźników char *p[100]; Sprawdzenie wyniku przypisania: for(i=0; i<100; i++) int i; printf(”%s”\n,p[i]); for(i=0; i<100; i++) p[i] = &tab[i]; Dostęp do znaku: printf(”%c %c”\n, tab[2][2],p[2][2]); Jacek Lach. Programowanie komputerów Przykład Jacek Lach. Programowanie komputerów Przykład Przykład • Rozwiązanie 3: Jacek Lach. Programowanie komputerów • Dwuwymiarowa tablica tab zamieniona na tablicę jednowymiarową • char tab[100*35]; • p[0] = &tab[0]; • p[1] = &tab[4]; • ... Jacek Lach. Programowanie komputerów Przykład Przykład Jacek Lach. Programowanie komputerów • Zamiana znaków odstępu na znak końca łańcucha • Problem efektywnej gospodarki pamięcią • char *tekst; • unsigned long len = oblicz_dl_tekstu(); • tekst = malloc(len * sizeof(char)); • Inne rozwiązania ... Przekazywanie tablic wielowymiarowych do funkcji • Deklaracja parametru musi zawierać liczbę kolumn Jacek Lach. Programowanie komputerów • Liczba wierszy nie ma znaczenia, bo parametr jest i tak przekazywany jako wskaźnik do tablicy wierszy • Przekazywanie tablic wielowymiarowych do funkcji Jacek Lach. Programowanie komputerów • Jeśli tablica daytab ma być przesłana do funkcji f, deklaracja powinna wyglądać tak: f(int daytab[2][13]) { ... } • Albo tak: f(int daytab[][13]) { ... } ponieważ liczba wierszy jest nieistotna • Można przekazać ją też tak: f(int (*daytab)[13]) { ... } co mówi, że parametr jest wskaźnikiem do tablicy 13 liczb int. Przekazywanie tablic wielowymiarowych do funkcji • f(int (*daytab)[13]) { ... } Jacek Lach. Programowanie komputerów • daytab jest wskaźnikiem na tablicę 13 liczb całkowitych • Nawiasy są konieczne, ponieważ [] mają wyższy priorytet od * • Bez nawiasów, deklaracja int *daytab[13] oznaczałaby tablicę 13 wskaźników na int • Ogólnie, tylko pierwszy wymiar tablic jest dowolny; pozostałe muszą zostać podane. Parametry linii poleceń Jacek Lach. Programowanie komputerów • Do programu można przekazywać parametry podczas jego uruchamiania (ang. commandline arguments, parameters) • Kiedy wywoływana jest funkcja main, może otrzymać dwa parametry: • pierwszy (zwykle nazywany argc, skrót od argument count) jest liczbą podanych parametrów, z którymi wywołano program • drugi (argv, skrót od argument vector) jest tablicą wskaźników do łańcuchów zawierających parametry, po jednym w każdym elemencie tablicy • POSIX: trzeci: środowisko Przykład Jacek Lach. Programowanie komputerów • Przykład: program wypisujący swoje parametry echo.exe hello, world • powinien wypisać hello, world Parametry main • main(int argc, char *argv[]) Jacek Lach. Programowanie komputerów • main(int argc, char **argv) • Pierwszy łańcuch, argv[0] jest nazwą, z którą wywołano program; dlatego argc jest równe co najmniej 1 • Jeśli argc jest równe 1, oznacza to brak przekazanych programowi parametrów • echo.exe hello, world • argc == 3, natomiast argv[0], argv[1] i argv[2] są odpowiednio równe "echo.exe", "hello," i "world". Parametry main Jacek Lach. Programowanie komputerów • Pierwszym opcjonalnym parametrem jest argv[1], ostatnim argv[argc1] • Dodatkowo, standard wymaga, aby argv[argc] był równy NULL Jacek Lach. Programowanie komputerów echo1 #include <stdio.h> /* wypisz parametry programu; wersja 1 */ main(int argc, char *argv[]) { int i; for (i = 1; i < argc; i++) printf("%s%s", argv[i], (i < argc-1) ? " " : ""); printf("\n"); return 0; } Jacek Lach. Programowanie komputerów echo2 #include <stdio.h> /* wypisz parametry programu; wersja 1 */ main(int argc, char *argv[]) { while (--argc > 0) printf("%s%s", *++argv, (argc > 1) ? " " : ""); printf("\n"); Dopuszczalne, ponieważ return 0; argv jest parametrem } funkcji; *argv[] jest tu równoważne **argv argv • argv Wskazuje na zerowy element tablicy argumentów Jacek Lach. Programowanie komputerów • ++argv Przesuwa wskaźnik na następny element tablicy (czyli na następny argument) • *argv Wskaźnik do argumentu • (*argv)[0] Pierwszy znak argumentu • *++argv Przesuwanie wzdłuż tablicy wskaźników i wyłuskiwanie wartości • *++(argv[i]) Przesuwanie wzdłuż znaków tablicy określonej przez argv[i] Przykład Jacek Lach. Programowanie komputerów • Wywołanie: sortuj.exe slownik.dat /r lub: sortuj.exe slownik.dat /m Jacek Lach. Programowanie komputerów Przykład main(int argc, char *argv[]) { char dane[100][35]; int i, flaga_sort = 0; if(argc < 3) { printf(“Brak argumentów programu %s\n”, argv[0]); return -1; } Jacek Lach. Programowanie komputerów Przykład for(i = 1; i < argc; i++) { if(argv[i][0] == ‘/’) // szukamy argumentu //zaczynającego się znakiem ‘/’ { switch(argv[i][1]) // co jest za znakiem // ‘/’? { case ‘m’: flaga_sort = -1; break; case ‘r’: flaga_sort = 1; break; default: printf(“Nieznana opcja sortowania\n”); return 1; } } if(flaga_sort) break; } Przykład ... switch(flaga_sort) Jacek Lach. Programowanie komputerów { case -1: sort_mal(dane); break; case 1: sort_rosn(dane); break; } return 0; } Przykład #include <stdio.h> Jacek Lach. Programowanie komputerów int main(int argc, char *argv[], char *env[]) { int i=0; while(env[i]) { printf("%s\n", env[i++]); } return 0; } Przykład int getopt( int argc, char *argv[], const char *optstring); Jacek Lach. Programowanie komputerów • przetwarza parametry wywołania programu • kolejne wywołania powodują przetworzenie kolejnego elementu parametrów • optstring: • : wymaganie argumentu • :: argument opcjonalny • + POSIXLY_CORRECT • parametry traktowane jako argumenty opcji Przykład while (1) { c = getopt(argc, argv, “abc:d:0”); Jacek Lach. Programowanie komputerów if (c == -1) break; switch(c) { ... } } while (optind < argc) { dodatkowe parametry } Przykład Jacek Lach. Programowanie komputerów run -0 -a -c 1 -beka -duko --ddr pierwszy drugi przebieg: 1 opcja 0 przebieg: 2 opcja a przebieg: 3 opcja c , wartosc: '1' przebieg: 4 opcja b przebieg: 5 run: invalid option -- e otrzymalem ? przebieg: 6 run: invalid option -- k otrzymalem ? przebieg: 7 opcja a przebieg: 8 opcja d , wartosc: 'uko' przebieg: 9 run: invalid option -- otrzymalem ? przebieg: 10 option d with value 'dr' przebieg: 11 elementy nie bedace opcjami: pierwszy drugi Wskaźniki do funkcji • W języku C można definiować wskaźniki do funkcji • Wskaźniki do funkcji można inicjalizować Jacek Lach. Programowanie komputerów • Można tworzyć tablice wskaźników do funkcji • Można je przekazywać jako argumenty do innej funkcji • Można je zwracać jako wartość funkcyjną, itd. Wskaźniki do funkcji Jacek Lach. Programowanie komputerów • Definicja wskaźnika do funkcji o prototypie: int funkcja(char, int); ma postać: int (* wsk_f)(char, int); • Nawiasy są obowiązkowe, ponieważ int * wsk_f(char, int); oznacza coś zupełnie innego • Możliwe jest przypisanie: wsk_f = funkcja; • Definicja z przypisaniem wartości początkowej: int (*wsk_f)(char, int) = funk; Wskaźniki do funkcji Jacek Lach. Programowanie komputerów • Funkcja sortowania qsort void qsort(void **, size_t, size_t, int(*comp)(void *, void*)); • Funkcja oczekuje czterech argumentów, którymi są: • tablica wskaźników do elementów tablicy dla sortowania, • rozmiar wiersza tablicy, • liczba wierszy, • wskaźnik do funkcji porównującej. Wskaźniki do funkcji Jacek Lach. Programowanie komputerów • Wskaźnika do funkcji można używać w wyrażeniach, na przykład: if((*wsk_f)(‘a’,25) < 0) ... Przykład struct pam_module { const char *name; /* Name of the module */ Jacek Lach. Programowanie komputerów /* These are function pointers to the module's key functions. }; int (*pam_sm_authenticate)(pam_handle_t *pamh, int flags, int argc, const char **argv); int (*pam_sm_setcred)(pam_handle_t *pamh, int flags, int argc, const char **argv); int (*pam_sm_acct_mgmt)(pam_handle_t *pamh, int flags, int argc, const char **argv); int (*pam_sm_open_session)(pam_handle_t *pamh, int flags, int argc, const char **argv); int (*pam_sm_close_session)(pam_handle_t *pamh, int flags, int argc, const char **argv); int (*pam_sm_chauthtok)(pam_handle_t *pamh, int flags, int argc, const char **argv); */ Jacek Lach. Programowanie komputerów Przykład PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) { ... } /* static module data */ struct pam_module _pam_moth_modstruct = { "pam_moth", pam_sm_authenticate, pam_sm_setcred, Zmienne globalne i lokalne int x; /* zmienna globalna */ float funkcja(int z) Jacek Lach. Programowanie komputerów { int x; /* zmienna lokalna */ return 3*z; } main() { int y; .... } Zmienne lokalne • Zmienne zadeklarowane wewnątrz funkcji są prywatne (lokalne) dla tej funkcji Jacek Lach. Programowanie komputerów • Żadna inna funkcja nie ma do nich bezpośredniego dostępu • Domyślnie, takie zmienne są automatyczne • Zmienne automatyczne są alokowane w pamięci na czas wykonywania funkcji, potem są usuwane • Nie zachowują wartości między wywołaniami funkcji • Początkowo mają nieokreśloną wartość • Zmienne lokalne mogą być też statyczne; wtedy nie są alokowane i usuwane razem z wyw. funkcji Klasy pamięci • Statyczna • Automatyczna Jacek Lach. Programowanie komputerów • Specyfikatory klasy pamięci: • automatyczna • auto • register • statyczna • static • extern • typedef Zmienne globalne Jacek Lach. Programowanie komputerów • Zmienna zewnętrzna musi zostać zdefiniowana, dokładnie raz, poza jakąkolwiek funkcją (przydział pamięci) • Zmienna musi również zostać zadeklarowana dla każdej funkcji, która będzie jej używała; deklaracja określa typ zmiennej • Deklaracją może być: • jawna deklaracja extern – zmienna w innym pliku • pośrednia deklaracja wynikająca z kontekstu; definicja jest też deklaracją, wystarczy umieścić ją przed użyciem zmiennej Deklaracja – definicja • Deklaracja informuje o właściwościach (typie) zmiennej Jacek Lach. Programowanie komputerów • Definicja dodatkowo powoduje rezerwację pamięci • extern int n; //deklaracja • extern int tab[]; //deklaracja • int n; //definicja • int tab[100]; //definicja Przydział, zwalnianie pamięci • Chcąc uzyskać dostęp do pamięci operacyjnej, trzeba ją zarezerwować Jacek Lach. Programowanie komputerów • Czynność określana jest terminem „alokowanie” od ang. allocate • Operacja rezerwowania pamięci zapewnia „bezpieczny” dostęp do pamięci • System operacyjny i środowisko uruchomieniowe gwarantują iż zarezerwowany obszar pamięci nie zostanie udostępniony innemu programowi lub procesowi • Jeśli jakiś program lub proces będzie próbował odwołać się do pamięci już zarezerwowanej zostanie zatrzymany (najczęściej) • Zarezerwowaną pamięć, jeśli dalszy dostęp nie jest już konieczny, należy zwolnić Jacek Lach. Programowanie komputerów Przydział, zwalnianie pamięci • Operacja rezerwowania pamięci obejmuje: przydział określonego miejsca w PaO oraz przekazanie adresu przydzielonego obszaru programowi lub procesowi, w którym wystąpiło żądanie przydziału pamięci • Adres zapamiętuje się stosując zmienne typu wskaźnikowego • W czasie rezerwowania pamięci może wystąpić błąd, np. gdy nie ma wystarczającej liczby wolnych komórek pamięci • Zwolnienie pamięci „likwiduje” blokadę przydzielonego fragmentu PaO Przydział pamięci Jacek Lach. Programowanie komputerów • • • • • • • • • Sposobów rezerwowania pamięci jest co najmniej kilka. W języku C najczęściej posługujemy się funkcją malloc: void *malloc(size_t n); Funkcja rezerwuje w pamięci obszar o rozmiarze n bajtów i zwraca adres tego obszaru Typ zwracanego adresu to void*, czyli adres zmiennej nieokreślonego typu W przypadku niepowodzenia zwraca wartość NULL Wartość NULL zwracana jest także w przypadku gdy n jest równe 0 Operator sizeof może być użyty do określenia wielkości danego typu MALLOC_CHECK_, MALLOC_PERTURB_ Przydział pamięci • Inny sposób przydzielania pamięci calloc: Jacek Lach. Programowanie komputerów • void *calloc(size_t nitems, size_t size); • Funkcja rezerwuje w pamięci obszar o rozmiarze nitems*size bajtów i zwraca adres tego obszaru • W przypadku niepowodzenia zwraca wartość NULL • Wartość NULL zwracana jest także w przypadku, gdy nitems*size jest równe 0 Jacek Lach. Programowanie komputerów Zwalnianie pamięci • Do zwalniania pamięci przydzielonej przez funkcję malloc: void free(void *ptr); funkcja zwalnia przydzielony obszar pamięci, którego adres zapamiętano w zmiennej ptr #include <stdio.h> #include <stdlib.h> int *pbuf; ... pbuf = (int *)malloc( sizeof(int) ); /*rzutowanie typu*/ if ( !pbuf == NULL ) printf (”Błąd przydziału pamięci”); /* albo if(!pbuf) …printf … else { ... } free ( pbuf ); /*niejawna konwersja typu z int* na void* */ Zmiana rozmiaru bloku • Możliwa jest zmiana rozmiaru już przydzielonego obszaru pamięci Jacek Lach. Programowanie komputerów • Umożliwia to funkcja void *realloc(void *ptr, size_t n); • Funkcja zmniejsza lub zwiększa uprzednio przydzielony obszar pamięci • Jeśli zachodzi konieczność, funkcja kopiuje zawartość obszaru pamięci w nowe miejsce i zwraca odpowiedni adres, jeśli taka konieczność nie występuje adres obszaru nie zmienia się • Jeśli n jest równe zero, pamięć jest zwalniana i funkcja zwraca wartość NULL • Jeśli ptr jest równe NULL, to funkcja działa jak malloc Błędy Jacek Lach. Programowanie komputerów • Brak kontroli poprawności allokacji (nie sprawdza się, czy wartość zwrócona przez funkcję malloc nie jest równa NULL) • należy pamiętać o tym, że nigdy nie ma pewności, że operacja przydziału pamięci zakończy się powodzeniem • oraz o tym, że odwołanie się do wskaźnika o wartości NULL powoduje zatrzymanie działania programu Błędy Jacek Lach. Programowanie komputerów • Niezwolnienie pamięci (zapomina się o zwalnianiu przydzielonej pamięci) • system sam „czyści” pamięć po zakończeniu działania programu, jednakże ... • zapotrzebowanie programu na pamięć rośnie, co jest szczególnie niekorzystne w przypadku algorytmów rekurencyjnych i może zagrozić działaniu systemu operacyjnego • należy zatem zwalniać pamięć jawnie