• Nie Znaleziono Wyników

Obsługa błędów, przeszukiwanie i sortowanie tablic

N/A
N/A
Protected

Academic year: 2021

Share "Obsługa błędów, przeszukiwanie i sortowanie tablic"

Copied!
79
0
0

Pełen tekst

(1)

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ą.

(2)

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.

(3)

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.

(4)

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?

(5)

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 ).

(6)

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.

(7)

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.

(8)

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ą.

(9)

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.

(10)

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.

(11)

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

(12)

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

(13)

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.)

(14)

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

(15)

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);

}

(16)

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.

(17)

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:

(18)

'

&

$

%

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 */

} /*****************************/

(19)

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’);

}

(20)

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);

}

(21)

& %

#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);

}

(22)

& %

#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);

}

(23)

& %

#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);

}

(24)

& %

#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);

}

(25)

& %

#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);

}

(26)

& %

#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);

}

(27)

& %

#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);

}

(28)

& %

#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);

}

(29)

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ć).

(30)

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.

(31)

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.

(32)

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).

(33)

'

&

$

%

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 */

} /*****************************/

(34)

'

&

$

%

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 */

} /****************************************/

(35)

'

&

$

%

#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 */

(36)

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.

(37)

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.

(38)

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.

(39)

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.

(40)

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,...)).

(41)

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

(42)

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 ).

(43)

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

(44)

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

(45)

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 */

(46)

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;

}

(47)

Sortowanie

• przez wstawianie

(48)

Sortowanie

• przez wstawianie

? przez proste wstawianie

? przez wstawianie połówkowe

(49)

Sortowanie

• przez wstawianie

? przez proste wstawianie

? przez wstawianie połówkowe

• przez wybieranie

(50)

Sortowanie

• przez wstawianie

? przez proste wstawianie

? przez wstawianie połówkowe

• przez wybieranie

? drzewiaste

(51)

Sortowanie

• przez wstawianie

? przez proste wstawianie

? przez wstawianie połówkowe

• przez wybieranie

? drzewiaste

• przez zamianę

(52)

Sortowanie

• przez wstawianie

? przez proste wstawianie

? przez wstawianie połówkowe

• przez wybieranie

? drzewiaste

• przez zamianę

? bąbelkowe

(53)

Sortowanie

• przez wstawianie

? przez proste wstawianie

? przez wstawianie połówkowe

• przez wybieranie

? drzewiaste

• przez zamianę

? bąbelkowe

? szybkie

(54)

Sortowanie

• przez wstawianie

? przez proste wstawianie

? przez wstawianie połówkowe

• przez wybieranie

? drzewiaste

• przez zamianę

? bąbelkowe

? szybkie

• przez scalanie

(55)

Sortowanie

• przez wstawianie

? przez proste wstawianie

? przez wstawianie połówkowe

• przez wybieranie

? drzewiaste

• przez zamianę

? bąbelkowe

? szybkie

• przez scalanie

• hybrydowe

(56)

Sortowanie przez proste wstawianie

(57)

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 wstawianie

STOP

Tab[i] > temp

i 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

(58)

Sortowanie drzewiaste

START

Sortowanie tablicy drzewiaste

STOP

Przekszta³æ tablicê w drzewo binarne

ObejdŸ drzewo w porz¹dku

"najpierw w lewo"

i wypisz ka¿dy element przy okazji drugich jego odwiedzin

(59)

Sortowanie drzewiaste

START

Sortowanie tablicy drzewiaste

STOP

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

(60)

Sortowanie drzewiaste

START

Sortowanie tablicy drzewiaste

STOP

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

(61)

Sortowanie bąbelkowe

START

Sortowanie tablicy b¹belkowe

STOP 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

(62)

Sortowanie bąbelkowe

START

Sortowanie tablicy b¹belkowe

STOP 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 */

(63)

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? Tak

Nie

j = j - 1

Tab[j] > x? Tak

Nie

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

(64)

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 *));

(65)

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 */

(66)

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

(67)

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);

}

(68)

Sortowanie przez scalanie

START

Sortowanie tablicy przez scalanie

STOP

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

(69)

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

(70)

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

(71)

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

(72)

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

(73)

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

(74)

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

(75)

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

posortuj przez wstawianie

(76)

Podsumowanie

• Zagadnienia podstawowe

1. W jaki sposób można obsługiwać błędy wewnątrz programu?

2. Porównaj wady i zalety scentralizowanego i rozproszonego systemu obsługi błędów w programie.

3. Czy można zapisać dane do standardowego wyjścia błędów, nawet jeśli nie wystąpił żaden błąd w programie?

4. Do czego służą funkcje perror i strerror? Jaka jest ich składnia?

5. Jakie błędy mogą wystąpić w przedstawionej z opisem programu do przetwarzania ob- razów (zobacz tabela z zadaniami na laboratorium) funkcji zapisującej obraz do pliku?

6. Kiedy można stosować liniowe a kiedy binarne przeszukiwanie tablic?

7. Jaka jest różnica złożoności obliczeniowej między przeszukiwaniem prostym a binar- nym?

8. Wymień wady i zalety sortowania bąbelkowego.

9. Czym się różni sortowanie tablic od sortowania plików? Które z metod pokazanych na wykładzie mogą być użyte do sortowania tablic, a które do sortowania plików?

• Zagadnienia rozszerzające

1. W jaki sposób można zwracać błąd wywołania funkcji (np. jak to jest zrobione w bibliotece

stdio)?

(77)

2. Czy zawsze należy stosować sortowanie o najmniejszej złożoności obliczeniowej?

3. Przeanalizuj złożoność algorytmów sortowania tablic; jakie są mocne i słabe strony po- szczególnych algorytmów?

4. Zapoznaj się z algorytmami sortowania przez kopcowanie, Shella, grzebieniowym; po-

równaj z algorytmami przedstawionymi na wykładzie. (wskazówka: http://www.zgapa.pl/zgapedia/Sortowanie.html)

• Zadania

1. Przejrzyj napisane dotychczas programy pod kątem analizy możliwości wystąpienia błę- dów w ich działaniu. Dopisz fragmenty kodu, które zapobiegną ich wystąpieniu. Czy do- pisany kod nie wymaga kolejnych zabiegów wykrywających sytuacje błędne?

2. Przeprowadź ręczną symulację algorytmu sortowania bąbelkowego dla następujących danych:

{1, 3, 56, 4, 90, 67, 3, 8, 12, 90}

{2, 10, 2, 0, -12, 98, 67, 45, 0, 27, -12}

3. Przeprowadź ręczną symulację algorytmu sortowania szybkiego dla następujących da- nych:

{1, 3, 56, 4, 90, 67, 3, 8, 12, 90}

{2, 10, 2, 0, -12, 98, 67, 45, 0, 27, -12}

4. Napisz funkcje implementujące (wybrane) funkcje sortowania tablicy, porównaj czas dzia-

łania funkcji, liczbę porównań i zamian dla tablic wypełnionych losowymi elementami,

a także wstępnie uporządkowanymi we właściwej i odwrotnej kolejności. Sprawdź, jak

(78)

zmieniają się wyniki dla różnych rozmiarów tablic.

5. Utworzyć i napisać program porządkujący ciąg n liczb całkowitych a1, a2 ,...an w ta- ki sposób, aby liczby o wartościach parzystych znajdowały się na najpierw, a liczby o wartościach nieparzystych po nich. Zarówno liczby parzyste jak i nieparzyste mają być uporządkowane w kolejności rosnącej. Podać liczbę elementów nieparzystych.

6. Napisz program do przechowywania danych w postaci (data,wartość) z możliwością sor- towania danych rosnąco lub malejąco, zarówno według daty, jak i wartości; wykorzystaj funkcję qsort().

7. Napisz program, który znajduje najczęściej występujące elementy w ciągu danych. Na wejściu programu podawana jest pewna liczba zestawów danych (co najwyżej 1000).

Każdy z zestawów ma postać: n x1 x2 <85> xn, gdzie n jest liczbą naturalną (z zakre-

su 1-1000), po której następuje n liczb całkowitych x1 x2 <85> xn (z zakresu od -1000

do 1000). Poszczególne liczby w zestawie zostaną rozdzielone znakiem spacji, a po-

szczególne zestawy znakiem nowej linii. Dla każdego z wczytanych zestawów należy

wyznaczyć wartość, która w ciągu x1 x2 <85> xn występuje najczęściej i wypisać ją na

wyjściu programu. Jeżeli takich wartości jest więcej należy je wypisać w kolejności ro-

snącej, rozdzielając liczby znakiem spacji. Wyniki dla poszczególnych zestawów należy

rozdzielić znakiem nowej linii. Przykład:

(79)

Wejście:

5 1 3 11 1 7 6 4 2 1 2 4 3 7 3 5 2 2 2 2 2 6 4 4 4 4 4 4

Wyjście:

1

2 4

2

4

Cytaty

Powiązane dokumenty

Czas działania algorytmu dla konkretnych danych wejściowych jest wyrażony liczbą wykonanych prostych (elementarnych) operacji

(3.2) dopóki, zaczynając od wskazanego prawego elementu ciągu, nie znajdziesz elementu prawego mniejszego lub równego kluczowi, testuj kolejne elementy (3.3) jeśli numer

 czas działania algorytmu dla danych wejściowych o stałym rozmiarze jest stały, stąd przyjmuje się, że czas działania algorytmu da małych rozmiarów danych

1/2 Francja Grecja Albania Egipt Cypr Hiszpania Belgia Dania. 1/3 Francja Albania Grecja Egipt Cypr Hiszpania

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

The aim of the study was to assess the effects of organic fertilization on selected chemical properties of the soil and the activity of dehydrogenase and β-glucosidase in the soil

- wszystkie rzucane wyjątki obsłużyć w klasie Figures, która przechowuje zdefiniowaną powyżej listę

Professor, Institute of European Culture, Adam Mickiewicz University in Poznań.