Wykład VIII
Obsługa błędów, przeszukiwanie i sortowanie tablic
Robert Muszyński
Katedra Cybernetyki i Robotyki, PWr
Zagadnienia: źródła błędów, organizacja obsługi błędów, standardowe funkcje obsługi błędów, przeszukiwanie tablic: liniowe, binarne, sortowanie tablic: przez wstawianie, drzewiaste, bąbelkowe, szyb- kie, przez scalanie.
Copyright c 2007–2014 Robert Muszyński
Niniejszy dokument zawiera materiały do wykładu na temat podstaw programowania w językach wysokiego poziomu. Jest on
udostępniony pod warunkiem wykorzystania wyłącznie do własnych, prywatnych potrzeb i może być kopiowany wyłącznie w całości,
razem ze stroną tytułową.
Ogólne uwagi o postępowaniu z błędami
• W naszych „małych” programach ilustracyjnych zazwyczaj nie martwiliśmy się o stan zakończonego programu.
• W „poważnym” programie koniecznie trzeba dbać o zwracanie sensownych
i użytecznych wartości opisujących ten stan.
Ogólne uwagi o postępowaniu z błędami
• W naszych „małych” programach ilustracyjnych zazwyczaj nie martwiliśmy się o stan zakończonego programu.
• W „poważnym” programie koniecznie trzeba dbać o zwracanie sensownych i użytecznych wartości opisujących ten stan.
• Nie ma jednego, najlepszego sposobu przekazywania informacji o błędach
pojawiających się w programie – można to robić zarówno centralnie jak
i lokalnie.
Ogólne uwagi o postępowaniu z błędami
• W naszych „małych” programach ilustracyjnych zazwyczaj nie martwiliśmy się o stan zakończonego programu.
• W „poważnym” programie koniecznie trzeba dbać o zwracanie sensownych i użytecznych wartości opisujących ten stan.
• Nie ma jednego, najlepszego sposobu przekazywania informacji o błędach pojawiających się w programie – można to robić zarówno centralnie jak i lokalnie.
• Do tego pojawiają się pytania: Czy jest obowiązkowe testowanie i obsługi-
wanie absolutnie wszystkich sytuacji nienormalnych? Czy próby wybrnięcia
z nieoczekiwanych błędów mają w ogóle sens?
Ogólne uwagi o postępowaniu z błędami
• W naszych „małych” programach ilustracyjnych zazwyczaj nie martwiliśmy się o stan zakończonego programu.
• W „poważnym” programie koniecznie trzeba dbać o zwracanie sensownych i użytecznych wartości opisujących ten stan.
• Nie ma jednego, najlepszego sposobu przekazywania informacji o błędach pojawiających się w programie – można to robić zarówno centralnie jak i lokalnie.
• Do tego pojawiają się pytania: Czy jest obowiązkowe testowanie i obsługi- wanie absolutnie wszystkich sytuacji nienormalnych? Czy próby wybrnięcia z nieoczekiwanych błędów mają w ogóle sens?
• Rozsądna zasada: jeśli wystąpił błąd to jesteśmy w kłopotach – lepiej nie
kombinujmy za dużo, tylko starajmy się wybrnąć z całej sytuacji w najprost-
szy możliwy sposób. W braku lepszego pomysłu możemy WKZP ( wyświetlić
komunikat i zakończyć program ).
Ogólne uwagi o postępowaniu z błędami
• W naszych „małych” programach ilustracyjnych zazwyczaj nie martwiliśmy się o stan zakończonego programu.
• W „poważnym” programie koniecznie trzeba dbać o zwracanie sensownych i użytecznych wartości opisujących ten stan.
• Nie ma jednego, najlepszego sposobu przekazywania informacji o błędach pojawiających się w programie – można to robić zarówno centralnie jak i lokalnie.
• Do tego pojawiają się pytania: Czy jest obowiązkowe testowanie i obsługi- wanie absolutnie wszystkich sytuacji nienormalnych? Czy próby wybrnięcia z nieoczekiwanych błędów mają w ogóle sens?
• Rozsądna zasada: jeśli wystąpił błąd to jesteśmy w kłopotach – lepiej nie kombinujmy za dużo, tylko starajmy się wybrnąć z całej sytuacji w najprost- szy możliwy sposób. W braku lepszego pomysłu możemy WKZP ( wyświetlić komunikat i zakończyć program ).
• Inny wniosek: własne funkcje piszmy tak, aby w razie porażki ich użytkownik
uzyskał informacje o miejscu i przyczynie powstania błędu i mógł podjąć
właściwe decyzje co do sposobu dalszego postępowania.
Ogólne uwagi o postępowaniu z błędami cd.
• Ważne jest by przy wystąpieniu błędu program zwrócił odpwiednią wartość
i/lub właściwie sformułowany komunikat o błędzie.
Ogólne uwagi o postępowaniu z błędami cd.
• Ważne jest by przy wystąpieniu błędu program zwrócił odpwiednią wartość i/lub właściwie sformułowany komunikat o błędzie.
• Przyjmuje się, że zwracana przez program wartość zero świadczy o popraw-
nym wykonaniu programu, natomiast jakakolwiek niezerowa wartość sygna-
lizuje sytuację awaryjną.
Ogólne uwagi o postępowaniu z błędami cd.
• Ważne jest by przy wystąpieniu błędu program zwrócił odpwiednią wartość i/lub właściwie sformułowany komunikat o błędzie.
• Przyjmuje się, że zwracana przez program wartość zero świadczy o popraw- nym wykonaniu programu, natomiast jakakolwiek niezerowa wartość sygna- lizuje sytuację awaryjną.
• Komunikat wypisywany w przypadku błędu musi dawać użytkownikowi pro-
gramu szansę na podjęcie dalszych działań. Dobra zasada: Mów to, o czym
naprawdę myślisz, zdawaj sobie sprawę z tego, co mówisz, bądź zwięzły.
Ogólne uwagi o postępowaniu z błędami cd.
• Ważne jest by przy wystąpieniu błędu program zwrócił odpwiednią wartość i/lub właściwie sformułowany komunikat o błędzie.
• Przyjmuje się, że zwracana przez program wartość zero świadczy o popraw- nym wykonaniu programu, natomiast jakakolwiek niezerowa wartość sygna- lizuje sytuację awaryjną.
• Komunikat wypisywany w przypadku błędu musi dawać użytkownikowi pro- gramu szansę na podjęcie dalszych działań. Dobra zasada: Mów to, o czym naprawdę myślisz, zdawaj sobie sprawę z tego, co mówisz, bądź zwięzły.
• Należy zadbać o to, by wypisywany komunikat nie „ugrzązł” gdzieś wśród
danych wyjściowych lub w potoku.
Ogólne uwagi o postępowaniu z błędami cd.
• Ważne jest by przy wystąpieniu błędu program zwrócił odpwiednią wartość i/lub właściwie sformułowany komunikat o błędzie.
• Przyjmuje się, że zwracana przez program wartość zero świadczy o popraw- nym wykonaniu programu, natomiast jakakolwiek niezerowa wartość sygna- lizuje sytuację awaryjną.
• Komunikat wypisywany w przypadku błędu musi dawać użytkownikowi pro- gramu szansę na podjęcie dalszych działań. Dobra zasada: Mów to, o czym naprawdę myślisz, zdawaj sobie sprawę z tego, co mówisz, bądź zwięzły.
• Należy zadbać o to, by wypisywany komunikat nie „ugrzązł” gdzieś wśród danych wyjściowych lub w potoku.
• Rodzaje typowych błędów:
? błędy ochrony pamięci
? błędy zakresu
Ogólne uwagi o postępowaniu z błędami cd.
• Ważne jest by przy wystąpieniu błędu program zwrócił odpwiednią wartość i/lub właściwie sformułowany komunikat o błędzie.
• Przyjmuje się, że zwracana przez program wartość zero świadczy o popraw- nym wykonaniu programu, natomiast jakakolwiek niezerowa wartość sygna- lizuje sytuację awaryjną.
• Komunikat wypisywany w przypadku błędu musi dawać użytkownikowi pro- gramu szansę na podjęcie dalszych działań. Dobra zasada: Mów to, o czym naprawdę myślisz, zdawaj sobie sprawę z tego, co mówisz, bądź zwięzły.
• Należy zadbać o to, by wypisywany komunikat nie „ugrzązł” gdzieś wśród danych wyjściowych lub w potoku.
• Rodzaje typowych błędów:
? błędy ochrony pamięci
? błędy zakresu
? błędy wejścia/wyjścia
Ogólne uwagi o postępowaniu z błędami cd.
• Ważne jest by przy wystąpieniu błędu program zwrócił odpwiednią wartość i/lub właściwie sformułowany komunikat o błędzie.
• Przyjmuje się, że zwracana przez program wartość zero świadczy o popraw- nym wykonaniu programu, natomiast jakakolwiek niezerowa wartość sygna- lizuje sytuację awaryjną.
• Komunikat wypisywany w przypadku błędu musi dawać użytkownikowi pro- gramu szansę na podjęcie dalszych działań. Dobra zasada: Mów to, o czym naprawdę myślisz, zdawaj sobie sprawę z tego, co mówisz, bądź zwięzły.
• Należy zadbać o to, by wypisywany komunikat nie „ugrzązł” gdzieś wśród danych wyjściowych lub w potoku.
• Rodzaje typowych błędów:
? błędy ochrony pamięci
? błędy zakresu
? błędy wejścia/wyjścia
? błędy obliczeniowe (dzielenie przez zero, przekroczenie zakresu itp.)
Ogólne uwagi o postępowaniu z błędami cd.
• Ważne jest by przy wystąpieniu błędu program zwrócił odpwiednią wartość i/lub właściwie sformułowany komunikat o błędzie.
• Przyjmuje się, że zwracana przez program wartość zero świadczy o popraw- nym wykonaniu programu, natomiast jakakolwiek niezerowa wartość sygna- lizuje sytuację awaryjną.
• Komunikat wypisywany w przypadku błędu musi dawać użytkownikowi pro- gramu szansę na podjęcie dalszych działań. Dobra zasada: Mów to, o czym naprawdę myślisz, zdawaj sobie sprawę z tego, co mówisz, bądź zwięzły.
• Należy zadbać o to, by wypisywany komunikat nie „ugrzązł” gdzieś wśród danych wyjściowych lub w potoku.
• Rodzaje typowych błędów:
? błędy ochrony pamięci
? błędy zakresu
? błędy wejścia/wyjścia
? błędy obliczeniowe (dzielenie przez zero, przekroczenie zakresu itp.)
? błędy konwersji
Przykład – kopiowanie plików
'
&
$
%
#include <stdio.h>
main(int argc, char *argv[]) { FILE *fp1, *fp2;
int c;
fp1 = fopen(argv[1], "r");
fp2 = fopen(argv[2], "w");
while ((c = getc(fp1)) != EOF) putc(c,fp2);
}
Przykład – kopiowanie plików
'
&
$
%
#include <stdio.h>
main(int argc, char *argv[]) { FILE *fp1, *fp2;
int c;
fp1 = fopen(argv[1], "r");
fp2 = fopen(argv[2], "w");
while ((c = getc(fp1)) != EOF) putc(c,fp2);
}
Na pozór działa poprawnie, ale co będzie w przypadku jakiegoś błędu, np.:
• niepoprawnej liczby argumentów,
• niepoprawnej nazwy pliku(ów),
• braku pliku źródłowego,
• braku praw dostępu do pliku źródłowego,
• braku prawa do zapisu pliku docelowego,
• niedostępnego dysku jednego z plików,
• braku miejsca na dysku itd.
Przykład – kopiowanie plików
'
&
$
%
#include <stdio.h>
main(int argc, char *argv[]) { FILE *fp1, *fp2;
int c;
fp1 = fopen(argv[1], "r");
fp2 = fopen(argv[2], "w");
while ((c = getc(fp1)) != EOF) putc(c,fp2);
}
Na pozór działa poprawnie, ale co będzie w przypadku jakiegoś błędu, np.:
• niepoprawnej liczby argumentów,
• niepoprawnej nazwy pliku(ów),
• braku pliku źródłowego,
• braku praw dostępu do pliku źródłowego,
• braku prawa do zapisu pliku docelowego,
• niedostępnego dysku jednego z plików,
• braku miejsca na dysku itd.
Uwzględniając błędy funkcji systemowych dostajemy:
'
&
$
%
main(int argc, char *argv[]) { /* wersja 2: z wykrywaniem bledow */
FILE *fp1, *fp2; int c; /* funkcji systemowych */
if (argc != 3) {
fprintf(stderr,"%s: wymagane 2 argumenty (podane %d)\n",argv[0],argc-1);
exit(1);}
if ((fp1 = fopen(argv[1], "r")) == NULL) {
fprintf(stderr,"%s: blad otwarcia pliku %s do odczytu\n",argv[0],argv[1]);
exit(2);}
if ((fp2 = fopen(argv[2], "w")) == NULL) {
fprintf(stderr,"%s: blad otwarcia pliku %s do zapisu\n",argv[0],argv[2]);
exit(3);}
while ((c = getc(fp1)) != EOF) if (putc(c, fp2) == EOF) {
fprintf(stderr,"%s: blad zapisu na pliku %s\n",argv[0],argv[2]);
exit(4);}
if (ferror(fp1) != 0) {
fprintf(stderr,"%s: blad czytania z pliku %s\n",argv[0],argv[1]);/*******/
exit(5);} /* pomijamy zamykanie plikow */
exit(0); /* i bledy z tym zwiazane */
} /*****************************/
Obsługa błędów – przykłady
Funkcja kopiująca napisy
'
&
$
%
/* kopiuj napis wskazywany przez wej do wyj */
/* PRE: poprawnie zainicjowane wej i wyj */
/* POST: skopiowanie napisu *wej na *wyj */
void KopiujNapis(char *wej, char *wyj) {
while ((*wyj++ = *wej++) != ’\0’);
}
Obsługa błędów – przykłady
Funkcja kopiująca napisy
'
&
$
%
/* kopiuj napis wskazywany przez wej do wyj */
/* PRE: poprawnie zainicjowane wej i wyj */
/* POST: skopiowanie napisu *wej na *wyj */
void KopiujNapis(char *wej, char *wyj) {
while ((*wyj++ = *wej++) != ’\0’);
}
Kopiowanie plików – całkiem podobne
'
&
$
%
/* kopiuj zawartosc pliku wej do pliku wyj */
/* PRE: poprawnie zainicjowane wej i wyj */
/* POST: skopiowanie strumienia wej na wyj */
void KopiujPlik(FILE *wej, FILE *wyj) {
int c;
while ((c = getc(wej)) != EOF) putc(c,wyj);
}
& %
#include <stdio.h> /* cat: sklej zawartosc plikow */
int main(int argc, char *argv[]) { FILE *fp;
void KopiujPlik(FILE *, FILE *);
char *prog = argv[0]; /* nazwa programu do komunikatow */
if (argc == 1) /* wywolanie bez arg.: kopiuj stdin */
KopiujPlik(stdin, stdout);
else
while (--argc > 0)
if ((fp = fopen(*++argv, "r")) == NULL) {
fprintf(stderr, "%s: nie moge otworzyc %s\n", prog, *argv);
exit(1);
} else {
KopiujPlik(fp, stdout);
fclose(fp);
}
if (ferror(stdout)) {
fprintf(stderr, "%s: wystapil blad zapisu stdout\n", prog);
exit(2);
}
exit(0);
}
& %
#include <stdio.h> /* cat: sklej zawartosc plikow */
int main(int argc, char *argv[]) { FILE *fp;
void KopiujPlik(FILE *, FILE *);
char *prog = argv[0]; /* nazwa programu do komunikatow */
if (argc == 1) /* wywolanie bez arg.: kopiuj stdin */
KopiujPlik(stdin, stdout);
else
while (--argc > 0)
if ((fp = fopen(*++argv, "r")) == NULL) {
fprintf(stderr, "%s: nie moge otworzyc %s\n", prog, *argv);
exit(1);
} else {
KopiujPlik(fp, stdout);
fclose(fp);
}
if (ferror(stdout)) {
fprintf(stderr, "%s: wystapil blad zapisu stdout\n", prog);
exit(2);
}
exit(0);
}
& %
#include <stdio.h> /* cat: sklej zawartosc plikow */
int main(int argc, char *argv[]) { FILE *fp;
void KopiujPlik(FILE *, FILE *);
char *prog = argv[0]; /* nazwa programu do komunikatow */
if (argc == 1) /* wywolanie bez arg.: kopiuj stdin */
KopiujPlik(stdin, stdout);
else
while (--argc > 0)
if ((fp = fopen(*++argv, "r")) == NULL) {
fprintf(stderr, "%s: nie moge otworzyc %s\n", prog, *argv);
exit(1);
} else {
KopiujPlik(fp, stdout);
fclose(fp);
}
if (ferror(stdout)) {
fprintf(stderr, "%s: wystapil blad zapisu stdout\n", prog);
exit(2);
}
exit(0);
}
& %
#include <stdio.h> /* cat: sklej zawartosc plikow */
int main(int argc, char *argv[]) { FILE *fp;
void KopiujPlik(FILE *, FILE *);
char *prog = argv[0]; /* nazwa programu do komunikatow */
if (argc == 1) /* wywolanie bez arg.: kopiuj stdin */
KopiujPlik(stdin, stdout);
else
while (--argc > 0)
if ((fp = fopen(*++argv, "r")) == NULL) {
fprintf(stderr, "%s: nie moge otworzyc %s\n", prog, *argv);
exit(1);
} else {
KopiujPlik(fp, stdout);
fclose(fp);
}
if (ferror(stdout)) {
fprintf(stderr, "%s: wystapil blad zapisu stdout\n", prog);
exit(2);
}
exit(0);
}
& %
#include <stdio.h> /* cat: sklej zawartosc plikow */
int main(int argc, char *argv[]) { FILE *fp;
void KopiujPlik(FILE *, FILE *);
char *prog = argv[0]; /* nazwa programu do komunikatow */
if (argc == 1) /* wywolanie bez arg.: kopiuj stdin */
KopiujPlik(stdin, stdout);
else
while (--argc > 0)
if ((fp = fopen(*++argv, "r")) == NULL) {
fprintf(stderr, "%s: nie moge otworzyc %s\n", prog, *argv);
exit(1);
} else {
KopiujPlik(fp, stdout);
fclose(fp);
}
if (ferror(stdout)) {
fprintf(stderr, "%s: wystapil blad zapisu stdout\n", prog);
exit(2);
}
exit(0);
}
& %
#include <stdio.h> /* cat: sklej zawartosc plikow */
int main(int argc, char *argv[]) { FILE *fp;
void KopiujPlik(FILE *, FILE *);
char *prog = argv[0]; /* nazwa programu do komunikatow */
if (argc == 1) /* wywolanie bez arg.: kopiuj stdin */
KopiujPlik(stdin, stdout);
else
while (--argc > 0)
if ((fp = fopen(*++argv, "r")) == NULL) {
fprintf(stderr, "%s: nie moge otworzyc %s\n", prog, *argv);
exit(1);
} else {
KopiujPlik(fp, stdout);
fclose(fp);
}
if (ferror(stdout)) {
fprintf(stderr, "%s: wystapil blad zapisu stdout\n", prog);
exit(2);
}
exit(0);
}
& %
#include <stdio.h> /* cat: sklej zawartosc plikow */
int main(int argc, char *argv[]) { FILE *fp;
void KopiujPlik(FILE *, FILE *);
char *prog = argv[0]; /* nazwa programu do komunikatow */
if (argc == 1) /* wywolanie bez arg.: kopiuj stdin */
KopiujPlik(stdin, stdout);
else
while (--argc > 0)
if ((fp = fopen(*++argv, "r")) == NULL) {
fprintf(stderr, "%s: nie moge otworzyc %s\n", prog, *argv);
exit(1);
} else {
KopiujPlik(fp, stdout);
fclose(fp);
}
if (ferror(stdout)) {
fprintf(stderr, "%s: wystapil blad zapisu stdout\n", prog);
exit(2);
}
exit(0);
}
& %
#include <stdio.h> /* cat: sklej zawartosc plikow */
int main(int argc, char *argv[]) { FILE *fp;
void KopiujPlik(FILE *, FILE *);
char *prog = argv[0]; /* nazwa programu do komunikatow */
if (argc == 1) /* wywolanie bez arg.: kopiuj stdin */
KopiujPlik(stdin, stdout);
else
while (--argc > 0)
if ((fp = fopen(*++argv, "r")) == NULL) {
fprintf(stderr, "%s: nie moge otworzyc %s\n", prog, *argv);
exit(1);
} else {
KopiujPlik(fp, stdout);
fclose(fp);
}
if (ferror(stdout)) {
fprintf(stderr, "%s: wystapil blad zapisu stdout\n", prog);
exit(2);
}
exit(0);
}
Uwagi o postępowaniu z błędami cd.
• Wszystkie błędy pojawiające się w programie można podzielić na „krytyczne”
(zakończenie działania programu) i „niekrytyczne” (mimo wszystko coś się
da zrobić).
Uwagi o postępowaniu z błędami cd.
• Wszystkie błędy pojawiające się w programie można podzielić na „krytyczne”
(zakończenie działania programu) i „niekrytyczne” (mimo wszystko coś się da zrobić).
• W programie można skonstruować „cetralny system obsługi błędów”, w któ-
rym to funkcje w przypadku napotkania błędu albo wywołują „centralną” funk-
cję obsługi błędów i przekazują jej sterowanie, albo odpowiednio przygoto-
wują i zwracają kod błędu, który po dotarciu „na sam szczyt” obsługiwany
jest przez odpowiednią funkcję obsługi błędów.
Uwagi o postępowaniu z błędami cd.
• Wszystkie błędy pojawiające się w programie można podzielić na „krytyczne”
(zakończenie działania programu) i „niekrytyczne” (mimo wszystko coś się da zrobić).
• W programie można skonstruować „cetralny system obsługi błędów”, w któ- rym to funkcje w przypadku napotkania błędu albo wywołują „centralną” funk- cję obsługi błędów i przekazują jej sterowanie, albo odpowiednio przygoto- wują i zwracają kod błędu, który po dotarciu „na sam szczyt” obsługiwany jest przez odpowiednią funkcję obsługi błędów.
• Można także wykorzystać „rozproszony system obsługi błędów” – każda
funkcja sama obsługuje napotkane błędy, wysyłając odpowiednie komuni-
katy i ewentualnie kończąc program.
Uwagi o postępowaniu z błędami cd.
• Wszystkie błędy pojawiające się w programie można podzielić na „krytyczne”
(zakończenie działania programu) i „niekrytyczne” (mimo wszystko coś się da zrobić).
• W programie można skonstruować „cetralny system obsługi błędów”, w któ- rym to funkcje w przypadku napotkania błędu albo wywołują „centralną” funk- cję obsługi błędów i przekazują jej sterowanie, albo odpowiednio przygoto- wują i zwracają kod błędu, który po dotarciu „na sam szczyt” obsługiwany jest przez odpowiednią funkcję obsługi błędów.
• Można także wykorzystać „rozproszony system obsługi błędów” – każda funkcja sama obsługuje napotkane błędy, wysyłając odpowiednie komuni- katy i ewentualnie kończąc program.
• W każdym razie przy obsłudze błędów można wesprzeć się standardową
funkcją void perror(const char *s), która wypisuje tekst z tablicy s i za-
leżny od implementacji komunikat o błędzie, odpowiadający wartości zmien-
nej errno, czy też funkcją char *strerror(int nr), zwracającą wskaźnik
do zależnego od implementacji tekstu komunikatu odpowiadającego błędo-
wi o numerze nr (nagłówki stdio.h, errno.h i string.h).
'
&
$
%
main(int argc, char *argv[]) { /* wersja 2: z wykrywaniem bledow */
FILE *fp1, *fp2; int c; /* funkcji systemowych */
if (argc != 3) {
fprintf(stderr,"%s: wymagane 2 argumenty (podane %d)\n",argv[0],argc-1);
exit(1);}
if ((fp1 = fopen(argv[1], "r")) == NULL) {
fprintf(stderr,"%s: blad otwarcia pliku %s do odczytu\n",argv[0],argv[1]);
exit(2);}
if ((fp2 = fopen(argv[2], "w")) == NULL) {
fprintf(stderr,"%s: blad otwarcia pliku %s do zapisu\n",argv[0],argv[2]);
exit(3);}
while ((c = getc(fp1)) != EOF) if (putc(c, fp2) == EOF) {
fprintf(stderr,"%s: blad zapisu na pliku %s\n",argv[0],argv[2]);
exit(4);}
if (ferror(fp1) != 0) {
fprintf(stderr,"%s: blad czytania z pliku %s\n",argv[0],argv[1]);/*******/
exit(5);} /* pomijamy zamykanie plikow */
exit(0); /* M I E L I S M Y */ /* i bledy z tym zwiazane */
} /*****************************/
'
&
$
%
main(int argc, char *argv[]) { /* wersja 3: wyswietlane */
FILE *fp1, *fp2; int c; /* komunikaty o bledach */
if (argc != 3) {
fprintf(stderr,"%s: wymagane 2 argumenty (podane %d)\n",argv[0],argc-1);
exit(1);}
if ((fp1 = fopen(argv[1], "r")) == NULL) { perror("blad otwarcia pliku do odczytu");
exit(2);}
if ((fp2 = fopen(argv[2], "w")) == NULL) { perror("blad otwarcia pliku do zapisu");
exit(3);}
while ((c = getc(fp1)) != EOF) if (putc(c, fp2) == EOF) {
perror("blad zapisu na pliku");
exit(4);}
if (ferror(fp1) != 0) {
perror("blad czytania z pliku");/****************************************/
exit(5);} /* niby troche krocej, ale nie wszystko */
exit(0); /* M A M Y */ /* wyswietlamy co poprzednio */
} /****************************************/
'
&
$
%
#include <errno.h> /* wersja 4: jawnie formatowane */
int errno; /* komunikaty o bledach */
main(int argc, char *argv[]) { FILE *fp1, *fp2; int c;
if (argc != 3) {
fprintf(stderr, "%s: wymagane 2 argumenty (podane %d)\n",argv[0],argc-1);
exit(1);
}
if ((fp1 = fopen(argv[1], "r")) == NULL) {
fprintf(stderr,"%s: blad otwarcia do odczytu pliku %s, %s",argv[0],argv[1],strerror(errno));
exit(2);
}
if ((fp2 = fopen(argv[2], "w")) == NULL) {
fprintf(stderr,"%s: blad otwarcia do zapisu pliku %s, %s",argv[0],argv[2],strerror(errno));
exit(3);
}
while ((c = getc(fp1)) != EOF) if (putc(c, fp2) == EOF) {
fprintf(stderr, "%s: blad zapisu na pliku %s, %s", argv[0], argv[2], strerror(errno));
exit(4);
}
if (ferror(fp1) != 0) {
fprintf(stderr, "%s: blad czytania z pliku %s, %s", argv[0], argv[1], strerror(errno));
... /* P O L A C Z E N I E D W O C H P O P R Z E D N I C H */
Obsługa błędów – podsumowanie
• Należy dbać o zwracanie sensownych i użytecznych komunikatów i wartości
informujących o pojawiających się błędach.
Obsługa błędów – podsumowanie
• Należy dbać o zwracanie sensownych i użytecznych komunikatów i wartości informujących o pojawiających się błędach.
• Zdecydowanie nie należy dążyć do obsłużenia wszystkich możliwych
błędów.
Obsługa błędów – podsumowanie
• Należy dbać o zwracanie sensownych i użytecznych komunikatów i wartości informujących o pojawiających się błędach.
• Zdecydowanie nie należy dążyć do obsłużenia wszystkich możliwych błędów.
• W przypadku interfejsu do systemu (czytanie plików, komunikacja między
procesami) można ograniczyć obsługę błędów do rzeczy podstawowych –
przy konstruowaniu interfejsu „do” użytkownika (menu, ręcznie wprowadzane
dane) obsługa błędów powinna być bardziej rozbudowana.
Obsługa błędów – podsumowanie
• Należy dbać o zwracanie sensownych i użytecznych komunikatów i wartości informujących o pojawiających się błędach.
• Zdecydowanie nie należy dążyć do obsłużenia wszystkich możliwych błędów.
• W przypadku interfejsu do systemu (czytanie plików, komunikacja między procesami) można ograniczyć obsługę błędów do rzeczy podstawowych – przy konstruowaniu interfejsu „do” użytkownika (menu, ręcznie wprowadzane dane) obsługa błędów powinna być bardziej rozbudowana.
• Porządny projekt programu pozwoli na uniknięcie wielu potknięć już na sa-
mym starcie – w tym zaplanowanie poprawnego systemu obsługi błędów.
Obsługa błędów – podsumowanie
• Należy dbać o zwracanie sensownych i użytecznych komunikatów i wartości informujących o pojawiających się błędach.
• Zdecydowanie nie należy dążyć do obsłużenia wszystkich możliwych błędów.
• W przypadku interfejsu do systemu (czytanie plików, komunikacja między procesami) można ograniczyć obsługę błędów do rzeczy podstawowych – przy konstruowaniu interfejsu „do” użytkownika (menu, ręcznie wprowadzane dane) obsługa błędów powinna być bardziej rozbudowana.
• Porządny projekt programu pozwoli na uniknięcie wielu potknięć już na sa- mym starcie – w tym zaplanowanie poprawnego systemu obsługi błędów.
• Informacje o błędach wysyłać w odpowiednie miejsca (return, exit,
fprintf(stderr,...)).
Obsługa błędów – podsumowanie
• Należy dbać o zwracanie sensownych i użytecznych komunikatów i wartości informujących o pojawiających się błędach.
• Zdecydowanie nie należy dążyć do obsłużenia wszystkich możliwych błędów.
• W przypadku interfejsu do systemu (czytanie plików, komunikacja między procesami) można ograniczyć obsługę błędów do rzeczy podstawowych – przy konstruowaniu interfejsu „do” użytkownika (menu, ręcznie wprowadzane dane) obsługa błędów powinna być bardziej rozbudowana.
• Porządny projekt programu pozwoli na uniknięcie wielu potknięć już na sa- mym starcie – w tym zaplanowanie poprawnego systemu obsługi błędów.
• Informacje o błędach wysyłać w odpowiednie miejsca (return, exit, fprintf(stderr,...)).
• Wykorzystywać dostępne mechanizmy obsługi błędów (perror, strerror),
pamiętając o ich właściwościach
Obsługa błędów – podsumowanie
• Należy dbać o zwracanie sensownych i użytecznych komunikatów i wartości informujących o pojawiających się błędach.
• Zdecydowanie nie należy dążyć do obsłużenia wszystkich możliwych błędów.
• W przypadku interfejsu do systemu (czytanie plików, komunikacja między procesami) można ograniczyć obsługę błędów do rzeczy podstawowych – przy konstruowaniu interfejsu „do” użytkownika (menu, ręcznie wprowadzane dane) obsługa błędów powinna być bardziej rozbudowana.
• Porządny projekt programu pozwoli na uniknięcie wielu potknięć już na sa- mym starcie – w tym zaplanowanie poprawnego systemu obsługi błędów.
• Informacje o błędach wysyłać w odpowiednie miejsca (return, exit, fprintf(stderr,...)).
• Wykorzystywać dostępne mechanizmy obsługi błędów (perror, strerror),
pamiętając o ich właściwościach, np. o tym, że zmienna errno jest ustawia-
na przez funkcje, które wykrywają i sygnalizują sytuacje nienormalne, lecz
nie zmienia wartości, gdy wynik działania funkcji jest poprawny ( zatem war-
tość errno może odnosić się do wcześniejszego niż ostatnie wywołania funkcji, albo do
późniejszego niż to, o które nam chodzi ).
Przeszukiwanie tablic
• liniowe
START Poszukiwanie wzorca wœród elementów tablicy jednowymiarowej
od elementu min do max
STOP Tab[min] = wzorzec
lub min = max?
Nie min = min + 1
Tak
Przeszukiwanie tablic
• liniowe
START Poszukiwanie wzorca wœród elementów tablicy jednowymiarowej
od elementu min do max
STOP Tab[min] = wzorzec
lub min = max?
Nie min = min + 1
Tak
• binarne
START
STOP
Srodek = (min + max) div 2
Tab[Srodek] = wzorzec lub
min=max?
Nie
Poszukiwanie wzorca wœród elementów tablicy jednowymiarowej
od elementu min do max
Tab[Srodek] < wzorzec?
max = Srodek - 1 min = Srodek + 1 Nie
Tak
Tak
Przeszukiwanie liniowe
'
&
$
%
int Przeszukaj(int tab[], int Klucz,
int min,max,domysl)
/* Wyszukuje wartosc Klucz w tablicy tab */
/* pomiedzy indeksami min i max */
/* Zwraca jej ind. lub domysl gdy !znaleziona */
{
int znaleziony;
znaleziony = 0;
while ((!znaleziony) && (min <= max)) {
if (Porownanie(tab[min],Klucz)) znaleziony = 1;
else
min := min + 1;
}
if (znaleziony) return min;
else
return domysl;
} /* Przeszukaj */
Przeszukiwanie liniowe
'
&
$
%
int Przeszukaj(int tab[], int Klucz,
int min,max,domysl)
/* Wyszukuje wartosc Klucz w tablicy tab */
/* pomiedzy indeksami min i max */
/* Zwraca jej ind. lub domysl gdy !znaleziona */
{
int znaleziony;
znaleziony = 0;
while ((!znaleziony) && (min <= max)) {
if (Porownanie(tab[min],Klucz)) znaleziony = 1;
else
min := min + 1;
}
if (znaleziony) return min;
else
return domysl;
} /* Przeszukaj */
Przeszukiwanie binarne
'
&
$
%
/* ... POSORTOWANEJ */
srodek = (min + max) / 2;
switch (Porownanie(tab[srodek],Klucz)) { case 0: znaleziony = 1; break;
case -1: max = srodek - 1; break;
case 1: min = srodek + 1; break;
}
Sortowanie
• przez wstawianie
Sortowanie
• przez wstawianie
? przez proste wstawianie
? przez wstawianie połówkowe
Sortowanie
• przez wstawianie
? przez proste wstawianie
? przez wstawianie połówkowe
• przez wybieranie
Sortowanie
• przez wstawianie
? przez proste wstawianie
? przez wstawianie połówkowe
• przez wybieranie
? drzewiaste
Sortowanie
• przez wstawianie
? przez proste wstawianie
? przez wstawianie połówkowe
• przez wybieranie
? drzewiaste
• przez zamianę
Sortowanie
• przez wstawianie
? przez proste wstawianie
? przez wstawianie połówkowe
• przez wybieranie
? drzewiaste
• przez zamianę
? bąbelkowe
Sortowanie
• przez wstawianie
? przez proste wstawianie
? przez wstawianie połówkowe
• przez wybieranie
? drzewiaste
• przez zamianę
? bąbelkowe
? szybkie
Sortowanie
• przez wstawianie
? przez proste wstawianie
? przez wstawianie połówkowe
• przez wybieranie
? drzewiaste
• przez zamianę
? bąbelkowe
? szybkie
• przez scalanie
Sortowanie
• przez wstawianie
? przez proste wstawianie
? przez wstawianie połówkowe
• przez wybieranie
? drzewiaste
• przez zamianę
? bąbelkowe
? szybkie
• przez scalanie
• hybrydowe
Sortowanie przez proste wstawianie
Sortowanie przez proste wstawianie
5 2 4 6 1 3
2 5 4 6 1 3
2 4 5 6 1 3
2 4 5 6 1 3
1 2 4 5 6 3
1 2 3 4 5 6
START
Sortowanie tablicy przez wstawianieSTOP
Tab[i] > tempi i > 0?
j = 2
temp = Tab[j]
i = j - 1
Tab[i+1] = Tab[i]
i = i - 1
Czy element j-ty jest
ostatni?
Tak
Nie
j = j + 1
Wstaw Tab[j]
w posortowany ci¹g Tab[1..j-1]
Tak
Tab[i+1] = temp
Nie
Sortowanie drzewiaste
START
Sortowanie tablicy drzewiasteSTOP
Przekszta³æ tablicê w drzewo binarne
ObejdŸ drzewo w porz¹dku
"najpierw w lewo"
i wypisz ka¿dy element przy okazji drugich jego odwiedzin
Sortowanie drzewiaste
START
Sortowanie tablicy drzewiasteSTOP
Przekszta³æ tablicê w drzewo binarne
ObejdŸ drzewo w porz¹dku
"najpierw w lewo"
i wypisz ka¿dy element przy okazji drugich jego odwiedzin
5
2
4
6
1
3
5 2 1 1 1 2 4 3 3 3 4 4 2 5 6 6 6
krok I
krok II
5 2 4 6 1 3
5
Sortowanie drzewiaste
START
Sortowanie tablicy drzewiasteSTOP
Przekszta³æ tablicê w drzewo binarne
ObejdŸ drzewo w porz¹dku
"najpierw w lewo"
i wypisz ka¿dy element przy okazji drugich jego odwiedzin
5
2
4
6
1
3
5 2 1 1 1 2 4 3 3 3 4 4 2 5 6 6 6
krok I
krok II
5 2 4 6 1 3
5
Sortowanie bąbelkowe
START
Sortowanie tablicy b¹belkoweSTOP j = 1
Zamieñ Tab[i]
z Tab[i+1]
Tak
j = j + 1
Nie Tak
i = 1
i = i + 1
j < n - 1?
Tab[i] > Tab[i+1]? Tak
Nie
Nie Czy
element i-ty jest przedostatni?
2 4 1 3
5
2 4 1 3 6
6
5
2 4 1 3 5 6
1 3 4 5 6 2
1 2 3 4 5 6
1 2 3 4 5 6
Sortowanie bąbelkowe
START
Sortowanie tablicy b¹belkoweSTOP j = 1
Zamieñ Tab[i]
z Tab[i+1]
Tak
j = j + 1
Nie Tak
i = 1
i = i + 1
j < n - 1?
Tab[i] > Tab[i+1]? Tak
Nie
Nie Czy
element i-ty jest przedostatni?
'
&
$
%
void BubbleSort(int Tab[], int min, max) {
int i, j;
for(j = min; j < max; j++) for(i = min; i < max; i++)
if (Tab[i] > Tab[i+1])
Zamien(Tab[i], Tab[i+1]);
} /* BubbleSort */
Sortowanie szybkie
Szybkie sortowanie elementów l..p tablicy
STOP i = l j = p
x = Tab[(i + j) div 2]
i = i + 1
Tab[i] < x? TakNie
j = j - 1
Tab[j] > x? TakNie
i <= j? Tak
Zamieñ Tab[i] z Tab[j]
i = i + 1; j = j - 1 START
Sortuj (l, p)
Nie
l < j? Tak Nie
i < p? Tak Nie
Sortuj (l, j)
Sortuj (i, p)
STOP
Sortowanie tablicy szybkie
Sortuj (1, n) START
2 4 6 7 8 1 3 5
2 3 1 8 6
5
l p
j i
3 5 6 7 8
1
4 7
2 4
Sortowanie szybkie – funkcja standardowa
W nagłówku stdlib.h znajduje się funkcja
void qsort(void *base, size_t nel, size_t width,
int (*compar)(const void *, const void *));
Sortowanie szybkie – funkcja standardowa
W nagłówku stdlib.h znajduje się funkcja
void qsort(void *base, size_t nel, size_t width, int (*compar)(const void *, const void *));
'
&
$
%
int porownaj_int(const void *p1, const void *p2) { int *i = (int *)p1;
int *j = (int *)p2;
if (*i > *j) return (1);
if (*i < *j) return (-1);
return (0); } /* porownaj_int */
Sortowanie szybkie – funkcja standardowa
W nagłówku stdlib.h znajduje się funkcja
void qsort(void *base, size_t nel, size_t width, int (*compar)(const void *, const void *));
'
&
$
%
int porownaj_int(const void *p1, const void *p2) { int *i = (int *)p1;
int *j = (int *)p2;
if (*i > *j) return (1);
if (*i < *j) return (-1);
return (0); } /* porownaj_int */
#define TSIZE 10
Sortowanie szybkie – funkcja standardowa
W nagłówku stdlib.h znajduje się funkcja
void qsort(void *base, size_t nel, size_t width, int (*compar)(const void *, const void *));
'
&
$
%
int porownaj_int(const void *p1, const void *p2) { int *i = (int *)p1;
int *j = (int *)p2;
if (*i > *j) return (1);
if (*i < *j) return (-1);
return (0); } /* porownaj_int */
#define TSIZE 10 int main() {
int a[TSIZE] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
qsort((void *)a, (size_t) TSIZE, sizeof(int), porownaj_int);
}
Sortowanie przez scalanie
START
Sortowanie tablicy przez scalanieSTOP
Podziel tablicê na dwie czêœci
Posortuj otrzymane tablice u¿ywaj¹c rekurencyjnie sortowania przez
scalanie
Po³¹cz postortowane tablice w posortowan¹ tablicê
5 2 4 6 7 8 1 3
5 2 4 6 7 8 1 3
5 2 4 6 7 8 1 3
... ... ... ...
2 5 4 6 7 8 1 3
2 4 5 6 1 3 7 8
1 2 3 4 5 6 7 8
5
2 4
6 8
7
1 3
2 1
4 3 6 5
7 8
podzial
podzial podzial
scalenie scalenie
scalenie
rekurencyjne wywo³anie sortowania przez scalanie
Sortowanie hybrydowe
• Obserwacja:
Sortowanie szybkie jest szybkie, ale nie dla małych tablic i nie gdy podziały
w kolejnych iteracjach sortowania szybkiego są zdegenerowane
Sortowanie hybrydowe
• Obserwacja:
Sortowanie szybkie jest szybkie, ale nie dla małych tablic i nie gdy podziały w kolejnych iteracjach sortowania szybkiego są zdegenerowane
• Pomysł:
Użyjmy w takich przypadkach innego, efektywniejszego sposobu sortowa-
nia
Sortowanie hybrydowe
• Obserwacja:
Sortowanie szybkie jest szybkie, ale nie dla małych tablic i nie gdy podziały w kolejnych iteracjach sortowania szybkiego są zdegenerowane
• Pomysł:
Użyjmy w takich przypadkach innego, efektywniejszego sposobu sortowa- nia
• Efekt – hybrydowe sortowanie introspektywne:
'
&
$
%
If (poziom rekurencji w sort. szybkim ponizej wyznaczonego)
posortuj przez kopcowanie
Sortowanie hybrydowe
• Obserwacja:
Sortowanie szybkie jest szybkie, ale nie dla małych tablic i nie gdy podziały w kolejnych iteracjach sortowania szybkiego są zdegenerowane
• Pomysł:
Użyjmy w takich przypadkach innego, efektywniejszego sposobu sortowa- nia
• Efekt – hybrydowe sortowanie introspektywne:
'
&
$
%
If (poziom rekurencji w sort. szybkim ponizej wyznaczonego) posortuj przez kopcowanie
podziel tablice w/g alg. sortowania szybkiego
Sortowanie hybrydowe
• Obserwacja:
Sortowanie szybkie jest szybkie, ale nie dla małych tablic i nie gdy podziały w kolejnych iteracjach sortowania szybkiego są zdegenerowane
• Pomysł:
Użyjmy w takich przypadkach innego, efektywniejszego sposobu sortowa- nia
• Efekt – hybrydowe sortowanie introspektywne:
'
&
$
%
If (poziom rekurencji w sort. szybkim ponizej wyznaczonego) posortuj przez kopcowanie
podziel tablice w/g alg. sortowania szybkiego
If (lewa podtablica zawiera conajmniej 9 elementow)
wywolaj rekurencyjnie proc. sortowania szybkiego dla niej
Sortowanie hybrydowe
• Obserwacja:
Sortowanie szybkie jest szybkie, ale nie dla małych tablic i nie gdy podziały w kolejnych iteracjach sortowania szybkiego są zdegenerowane
• Pomysł:
Użyjmy w takich przypadkach innego, efektywniejszego sposobu sortowa- nia
• Efekt – hybrydowe sortowanie introspektywne:
'
&
$
%
If (poziom rekurencji w sort. szybkim ponizej wyznaczonego) posortuj przez kopcowanie
podziel tablice w/g alg. sortowania szybkiego
If (lewa podtablica zawiera conajmniej 9 elementow)
wywolaj rekurencyjnie proc. sortowania szybkiego dla niej If (prawa podtablica zawiera conajmniej 9 elementow)
wywolaj rekurencyjnie proc. sortowania szybkiego dla niej
Sortowanie hybrydowe
• Obserwacja:
Sortowanie szybkie jest szybkie, ale nie dla małych tablic i nie gdy podziały w kolejnych iteracjach sortowania szybkiego są zdegenerowane
• Pomysł:
Użyjmy w takich przypadkach innego, efektywniejszego sposobu sortowa- nia
• Efekt – hybrydowe sortowanie introspektywne:
'
&
$
%