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. command­line 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[argc­1]
• 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