Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00066 005901 13604397 na godz. na dobę w sumie
Programowanie w języku C. Ćwiczenia praktyczne. Wydanie II - książka
Programowanie w języku C. Ćwiczenia praktyczne. Wydanie II - książka
Autor: Liczba stron: 120
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-246-2834-6 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> c - programowanie
Porównaj ceny (książka, ebook, audiobook).

Poznaj w praktyce podstawowe narzędzie pracy profesjonalnych programistów!

Opracowanie języka C było milowym krokiem w historii rozwoju informatyki i choć od czasu jego powstania minęło już niemal czterdzieści lat, nadal jest to jeden z najbardziej popularnych języków programowania na świecie. Zawdzięcza to swojej elastyczności, dużym możliwościom, wysokiej wydajności działania, łatwości tworzenia i konserwacji kodu oraz niezależności od platformy sprzętowej. Nie bez znaczenia jest też fakt, że na jego składni oparte są inne nowoczesne języki wysokiego poziomu, takie jak C++, C# czy Java - i że to właśnie jego poznanie jest często pierwszym krokiem na drodze do kariery profesjonalnego programisty.

Niezależnie od tego, z jakich powodów chcesz nauczyć się języka C, doskonałą pomocą okaże się książka 'Programowanie w języku C. Ćwiczenia praktyczne. Wydanie II '. Poprawiona i uzupełniona edycja ćwiczeń bezboleśnie wprowadzi Cię w świat programowania strukturalnego. Poznasz podstawowe pojęcia związane z językiem C i zasady tworzenia poprawnego kodu, nauczysz się prawidłowo korzystać z różnych typów danych i instrukcji, a także dowiesz się, jak przeprowadzać operacje wejścia-wyjścia. Zgłębisz również tajniki bardziej zaawansowanych technik, takich jak używanie wskaźników, tablic i struktur. Jeśli chcesz zacząć przygodę z programowaniem w C, trafiłeś na idealną książkę!

Nauka języka C jeszcze nigdy nie była tak prosta!

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

Programowanie w języku C. Ćwiczenia praktyczne. Wydanie II Autor: Marek Tłuczek ISBN: 978-83-246-2834-6 Format: 140×208, stron: 120 • Poznaj podstawy języka C • Naucz się programowania strukturalnego • Przećwicz swoje umiejętności Poznaj w praktyce podstawowe narzędzie pracy profesjonalnych programistów! Opracowanie języka C było milowym krokiem w historii rozwoju informatyki i choć od czasu jego powstania minęło już niemal czterdzieści lat, nadal jest to jeden z najbardziej popularnych języków programowania na świecie. Zawdzięcza to swojej elastyczności, dużym możliwościom, wysokiej wydajności działania, łatwości tworzenia i konserwacji kodu oraz niezależności od platformy sprzętowej. Nie bez znaczenia jest też fakt, że na jego składni oparte są inne nowoczesne języki wysokiego poziomu, takie jak C++, C# czy Java – i że to właśnie jego poznanie jest często pierwszym krokiem na drodze do kariery profesjonalnego programisty. Niezależnie od tego, z jakich powodów chcesz nauczyć się języka C, doskonałą pomocą okaże się książka „Programowanie w języku C. Ćwiczenia praktyczne. Wydanie II”. Poprawiona i uzupełniona edycja ćwiczeń bezboleśnie wprowadzi Cię w świat programowania strukturalnego. Poznasz podstawowe pojęcia związane z językiem C i zasady tworzenia poprawnego kodu, nauczysz się prawidłowo korzystać z różnych typów danych i instrukcji, a także dowiesz się, jak przeprowadzać operacje wejścia-wyjścia. Zgłębisz również tajniki bardziej zaawansowanych technik, takich jak używanie wskaźników, tablic i struktur. Jeśli chcesz zacząć przygodę z programowaniem w C, trafiłeś na idealną książkę! • Podstawy tworzenia kodu w C • Definiowanie stałych i zmiennych oraz ich używanie • Stosowanie prostych i złożonych typów danych • Używanie instrukcji warunkowych i tworzenie pętli • Korzystanie z funkcji standardowych • Posługiwanie się łańcuchami znakowymi • Operacje związane ze strumieniami wejścia-wyjścia • Definiowanie i używanie wskaźników do danych i funkcji Nauka języka C jeszcze nigdy nie była tak prosta! Idź do • Spis treści • Przykładowy rozdział Katalog książek • Katalog online • Zamów drukowany katalog Twój koszyk • Dodaj do koszyka Cennik i informacje • Zamów informacje o nowościach • Zamów cennik Czytelnia • Fragmenty książek online Kontakt Helion SA ul. Kościuszki 1c 44-100 Gliwice tel. 32 230 98 63 e-mail: helion@helion.pl © Helion 1991–2011 Spis treĂci WstÚp Rozdziaï 1. Podstawy jÚzyka C Tworzenie programu w C printf() — funkcja wyjĂcia Zmienne w jÚzyku C Staïe w C scanf() — funkcja wejĂcia Instrukcja warunkowa if Co powinieneĂ zapamiÚtaÊ z tego cyklu Êwiczeñ? mwiczenia do samodzielnego wykonania Rozdziaï 2. Programowanie strukturalne Funkcje PÚtle w jÚzyku C WstÚp do tablic Rozdziaï 3. Instrukcja switch Co powinieneĂ zapamiÚtaÊ z tego cyklu Êwiczeñ? mwiczenia do samodzielnego wykonania JÚzyk C dla wtajemniczonych Tablice wielowymiarowe Wskaěniki Wskaěniki i tablice Znaki oraz ïañcuchy znaków Znaki ’añcuchy znaków 5 7 7 9 11 15 17 19 25 26 27 28 35 35 42 44 45 47 47 51 52 56 57 58 4 Programowanie w jÚzyku C • mwiczenia praktyczne Zastosowanie wskaěników Przekazywanie przez wskaěnik zmiennej jako argumentu funkcji Dynamiczny przydziaï pamiÚci Operacje arytmetyczne na wskaěnikach Struktury w jÚzyku C Co powinieneĂ zapamiÚtaÊ z tego cyklu Êwiczeñ? mwiczenia do samodzielnego wykonania Rozdziaï 4. Operacje wejĂcia-wyjĂcia Strumienie wejĂcia-wyjĂcia Funkcje wejĂcia Funkcje wyjĂcia Operacje na ïañcuchach znaków Kopiowanie ïañcuchów znaków ’Èczenie ïañcuchów znaków Operacje na plikach Otwieranie, tworzenie i zamykanie plików tekstowych Odczytywanie pliku tekstowego Zapisywanie pliku tekstowego Co powinieneĂ zapamiÚtaÊ z tego cyklu Êwiczeñ? mwiczenia do samodzielnego wykonania JÚzyk C dla guru Struktury ze wskaěnikami Wskaěniki do funkcji Tablice wskaěników do funkcji Preprocesor Sparametryzowane makrodefinicje (makra) Kompilacja warunkowa Co powinieneĂ zapamiÚtaÊ z tego cyklu Êwiczeñ? mwiczenia do samodzielnego wykonania Rozdziaï 5. 65 65 67 68 74 78 80 81 81 82 86 87 88 90 92 92 93 97 101 102 103 103 108 112 113 115 116 118 119 5 JÚzyk C dla guru Drogi Czytelniku, czyĝbyĂ opanowaï caïy materiaï z poprzed- nich czÚĂci ksiÈĝki? RozwiÈzaïeĂ wszystkie Êwiczenia? Nie masz ĝadnych wÈtpliwoĂci? JesteĂ pewien, ĝe nie masz ĝadnych wÈtpliwoĂci? Hm… w takim razie moĝesz przekroczyÊ kolejne wrota fascynujÈcej krainy jÚzyka C i zanurzyÊ siÚ w bezmiernej gïÚbinie wiedzy. PamiÚtaj — stÈd juĝ nie ma powrotu. Z pewnoĂciÈ po lekturze tej ksiÈĝki siÚgniesz po opracowania omawiajÈce zaawansowane pojÚcia zwiÈzane z programowaniem w C (np. programowanie sieciowe) lub rozpoczniesz naukÚ C++. Ale nie mów hop, póki nie przeskoczysz. Najpierw opanuj materiaï zawarty w tym rozdziale. Gotów? JeĂli tak, zapraszam do lektury rozdziaïu 5. BÚdzie w nim mowa o zaawanso- wanym zastosowaniu struktur i wskaěników do definicji struktur danych nazywanych listami, wskaěnikach do funkcji oraz dyrektywach preprocesora. Struktury ze wskaěnikami Nie jest wielkÈ tajemnicÈ, zwïaszcza dla guru, ĝe struktury teĝ mogÈ zawieraÊ wskaěniki jako pola oraz ĝe moĝna tworzyÊ wskaěniki do struktur. Warto jednak o tym wspomnieÊ na poczÈtku rozdziaïu, po- niewaĝ w jÚzyku C wprowadzono operator - , który uïatwia dostÚp do wskaěników do struktur. Najïatwiej zrozumieÊ to na poniĝszym przykïadzie. 104 Programowanie w jÚzyku C • mwiczenia praktyczne m W I C Z E N I E 5.1 Napisz program, w którym zdefiniujesz typ struktury zawie- rajÈcej dowolne wskaěniki jako pola. Zdefiniuj równieĝ zmiennÈ dla tego typu oraz wskaěnik do takiej struktury. Przy- pisz wartoĂci odpowiednim polom oraz wypisz je na ekranie poprzez odwoïanie siÚ zarówno do zwykïej zmiennej, jak i do wskaěnika do struktury. 1: /* Przyklad 5.1 */ 2: /* Przyklad ilustruje skáadnie uĪywana do uzyskania dostepu */ 3: /* do wskaznikow do struktur oraz wskaznikow bedacych */ 4: /* elementami struktury */ 5: #include stdio.h 6: typedef struct { 7: int x; 8: int *y; 9: } struktura; 10:int main() 11:{ 12: struktura *wsk_strukt; 13: struktura strukt; 14: int a = 10; 15: int b = 20; 16: strukt.x = a; 17: strukt.y = b; 18: wsk_strukt = strukt; 19: printf („Wartosc pola x: d ”, strukt.x); 20: printf („Wartosc pola x odwolujac siú przez wskaznik ´do struktury: d ”, wsk_strukt- x ); 21: printf(„Wartosc wskazywana przez pole *y: d ”, *(strukt.y) ); 22: printf(„Wartosc wskazywana przez pole *y w przypadku ´odwolania sie przez wskaznik do struktury: d ”, ´*(wsk_strukt- y) ); 23: return 0; 24:} Program ilustruje cztery przypadki uĝycia wskaěników do struktur oraz struktur ze wskaěnikami. W wierszach 6 – 9 zadeklarowano typ struktury z dwoma polami, z których jedno jest wskaěnikiem, a dru- gie zwykïÈ zmiennÈ typu int. W wierszach 12 oraz 13 zdefiniowano odpowiednio wskaěnik oraz zmiennÈ typu struktura. Pierwszy przypadek (wiersz 19) to najprostsze odwoïanie siÚ do pola x zmiennej typu struktura. Drugi przypadek (wiersz 20) to odwoïanie siÚ do zmiennej x typu int, ale poprzez wskaěnik do struktury zawierajÈcej tÚ zmiennÈ. Tutaj wïa- Rozdziaï 5. • JÚzyk C dla guru 105 Ănie przydaï siÚ wspomniany operator - . W przypadku braku takiego operatora naleĝaïoby siÚ odwoïaÊ do tej zmiennej w nastÚpujÈcy spo- sób — (*wsk_strukt).x — co nie wyglÈdaïoby zbyt czytelnie. W wierszu 21 znajduje siÚ przykïad odwoïania do wskaěnika y wewnÈtrz zwykïej zmiennej typu struktura. Poprzez instrukcjÚ strukt.y uzyskujemy wskazywany adres, a nastÚpnie poprzez (operator *) — wartoĂÊ pod tym adresem. Wiersz 22 demonstruje najtrudniejszy z przypadków — czyli odwo- ïanie siÚ do wartoĂci pola *y bÚdÈcego wskaěnikiem poprzez wskaěnik do struktury go zawierajÈcej. Najpierw, podobnie jak w drugim przy- padku, uzyskujemy dostÚp do wskaěnika y poprzez wskaěnik do struk- tury — wsk_strukt- y. Jednak w ten sposób uzyskany zostaï tylko adres — w celu uzyskania wartoĂci pod tym adresem naleĝy uĝyÊ ope- ratora * — *(wsk_strukt- y). Gdyby nie byïo operatora - , trzeba by uĝyÊ nastÚpujÈcej instrukcji: *((*wsk_strukt).y). PoznaïeĂ zatem juĝ wszystkie sekrety zwiÈzane ze skïadniÈ. Czas na wyjaĂnienie, komu i do czego moĝe siÚ to przydaÊ. JeĂli chodzi o same wskaěniki do struktur — na pewno moĝna je prze- kazaÊ przez wskaěnik jako parametr do funkcji. Moĝna teĝ definiowaÊ dynamiczne tablice struktur. Podobne zastosowania moĝliwe sÈ takĝe w odniesieniu do wskaěników uĝytych jako pola struktur — zawsze moĝna sobie zdefiniowaÊ dynamiczne tablice jako czÚĂÊ struktury, choÊ pewnie nie jest to zbyt pospolite dziaïanie. Najciekawszym zastosowa- niem, zarazem chyba najbardziej praktycznym i popularnym, jest uĝycie wskaěników do struktur wewnÈtrz struktur. Co ciekawe, najczÚĂciej typ wskaěnika bÚdÈcy polem struktury jest taki sam jak typ tej struktury. Czyĝby takie pole wskazywaïo na strukturÚ, w której siÚ znajduje? Odpowiedě brzmi: teĝ, niekoniecznie jednak tylko na siebie, a przede wszystkim na inne elementy takiego samego typu. W ten sposób tworzy siÚ tzw. listÚ. Lista to ciÈg poïÈczonych elementów. PoïÈczone sÈ one w taki sposób, ĝe kaĝdy element wskazuje na kolejny element po nim. Przykïadem listy — jeĂli odwoïaÊ siÚ do ĝycia realnego — jest pociÈg, elementami listy sÈ poszczególne wagony. Do kaĝdego wagonu doïÈ- czony jest kolejny (poza lokomotywÈ, która stanowi szczególny ele- ment takiej listy). Lista jest alternatywÈ dla tablicy i w wielu zastoso- waniach okazuje siÚ lepszym rozwiÈzaniem. Jest szczególnie efektywna w przypadku zarzÈdzania pamiÚciÈ. 106 Programowanie w jÚzyku C • mwiczenia praktyczne KontynuujÈc wczeĂniejszÈ analogiÚ listy do pociÈgu, tablicÚ moĝna porównaÊ do wagonu. Elementami takiej tablicy sÈ poszczególne, ponumerowane miejsca w wagonie. Do tablicy szybciej moĝna siÚ dostaÊ — wystarczy podaÊ indeks elementu (numer miejsca) i juĝ wiadomo, co siÚ w danym polu znajduje. JeĂli chodzi o listÚ, moĝna polegaÊ jedynie na wskaěnikach — trzeba szukaÊ danego elementu, przeskakujÈc z jednego do drugiego za pomocÈ wskaěnika do sÈsiada. Lista jest jednak lepsza, jeĂli chodzi o zarzÈdzanie pamiÚciÈ dla doda- wanych i usuwanych dynamicznie elementów. JeĂli usuwany jest ele- ment z listy — lub wagon z pociÈgu — trzeba tylko zmieniÊ jeden wskaěnik poprzedniego elementu tak, aby wskazywaï na kolejny ele- ment za tym usuniÚtym. Podobnie po usuniÚciu wagonu z pociÈgu spina siÚ tylko sÈsiadujÈce z nim wagony. W przypadku tablicy po usuniÚciu elementu zostaje puste, nieuĝywane pole i pamiÚÊ nie moĝe byÊ zwolniona. Nie moĝna skurczyÊ wagonu, jeĂli potrzeba 20 miejsc, nie moĝna usunÈÊ poïowy miejsc z wagonu 40-osobowego. Moĝliwa jest oczywiĂcie realokacja pamiÚci dla tablicy dynamicznej, ale wiÈza- ïoby siÚ to z koniecznoĂciÈ podstawienia nowego wagonu (np. 50-oso- bowego) i przestawienia do niego wszystkich pasaĝerów. Lepszym rozwiÈzaniem jest dopiÚcie 10-osobowego wagonu na koniec pociÈgu. Moĝe nieco przesadziïem z tÈ analogiÈ — PKP wszystkie wagony ma bardzo podobne i nowoczesne zarzÈdzanie miejscami w pociÈgu chyba nie ma zbyt duĝego sensu, przecieĝ pasaĝer moĝe sobie postaÊ w sÈsiedztwie komfortowej toalety przez 8 godzin… ale to juĝ temat na inne przykïady. Przejděmy wiÚc do praktycznej implementacji takiego pociÈgu PKP: struct { struct wagon *nastepny; int *miejsca_w_wagonie; } wagon; Tak moĝe wyglÈdaÊ typ struktury dla wagonu PKP. Przy definicji kaĝdego elementu naleĝy zaczÈÊ od lokomotywy — ustawiÊ wskaěnik nastepny jako NULL (czyli brak kolejnego elementu). NastÚpnie trzeba utworzyÊ kolejny element i ustawiÊ wskaěnik nastepny tak, aby wska- zywaï na lokomotywÚ (np. wagon1.nastepny = lokomotywa). Przy defini- cji kaĝdego elementu trzeba teĝ udostÚpniÊ odpowiedniÈ iloĂÊ pamiÚci na miejsca w danym wagonie (stosujÈc funkcjÚ malloc()). MyĂlÚ, ĝe jesteĂ juĝ gotów na wykonanie kolejnego praktycznego Êwiczenia. m W I C Z E N I E 5.2 Rozdziaï 5. • JÚzyk C dla guru 107 Napisz program, który utworzy dynamicznie listÚ reprezen- tujÈcÈ pociÈg zïoĝony z 3 wagonów (w tym warsu) i loko- motywy; wykorzystaj przy tym strukturÚ typu wagon. PamiÚtaj o udostÚpnieniu odpowiedniej iloĂci miejsca dla pasaĝerów w kolejnych wagonach — w przypadku PKP bÚdzie to 60 miejsc dla kaĝdego wagonu oraz 20 miejsc dla wagonu wars. NastÚpnie usuñ wagon1, tak aby zwolniÊ zuĝytÈ pamiÚÊ, i podepnij wagon2 do warsu. 1: /* Przyklad 5.2 */ 2: /* Program tworzacy liste reprezentujaca pociag PKP */ 3: #include studio.h 4: #include stdlib.h 5: struct wagon{ 6: struct wagon *nastepny; 7: int *miejsca_w_wagonie; 8: } ; 9: typedef struct wagon wagon_PKP; 10:int main() 11:{ 12: wagon_PKP *lokomotywa, *wars, *wagon1, *wagon2; 13: lokomotywa = (wagon_PKP *)malloc(sizeof(wagon_PKP)); 14: wars = (wagon_PKP *)malloc(sizeof(wagon_PKP)); 15: wagon1 = (wagon_PKP *)malloc(sizeof(wagon_PKP)); 16: wagon2 = (wagon_PKP *)malloc(sizeof(wagon_PKP)); 17: if !(wars lokomotywa wagon1 wagon2) return -1; 18: lokomotywa- nastepny = NULL; 19: lokomotywa- miejsca_w_wagonie = NULL; 20: wars- nastepny = lokomotywa; 21: wars- miejsca_w_wagonie = (int *)malloc(20*sizeof(int)); 22: wagon1- nastepny = wars; 23: wagon1- miejsca_w_wagonie = (int *)malloc(60*sizeof(int)); 24: wagon2- nastepny = wagon1; 25: wagon2- miejsca_w_wagonie = (int *)malloc(60*sizeof(int)); 26: printf(“Usuwamy wagon1 i podpinamy wagon2 do wagonu WARS ”); 27: wagon2- nastepny = wars; 28: free(wagon1); 29: return 0; 30: } Wiersze 5 – 9: tworzysz strukturÚ wagonu PKP i definiujesz odpowiedni alias — wagon_PKP — reprezentujÈcy nowy typ. Wiersz 12: definiujesz wskaěniki do odpowiednich struktur. Gdyby zostaïy zdefiniowane zwykïe zmienne zamiast wskaěników, nie moĝna by dynamicznie zwalniaÊ i udostÚpniaÊ pamiÚci. 108 Programowanie w jÚzyku C • mwiczenia praktyczne Wiersze 14 – 17: udostÚpniasz odpowiedniÈ iloĂÊ pamiÚci dla elemen- tów. JeĂli operacja siÚ nie powiedzie, wychodzisz z programu. Wiersze 18 – 19: inicjalizujesz elementy lokomotywy — oba wskaěniki ustawiasz na NULL, poniewaĝ ĝaden wagon nie jest doïÈczony przed lokomotywÈ ani nie ma tam miejsc dla pasaĝerów. Wiersze 20 – 25: zawierajÈ inicjalizacjÚ pól innych wagonów, wagon wars jest podïÈczony bezpoĂrednio do lokomotywy, wiÚc ustawiasz odpowiedni wskaěnik tak, aby na niÈ wskazywaï. UdostÚpniasz pamiÚÊ dla miejsc pasaĝerskich poprzez proste i znane Ci juĝ dobrze wywoïa- nie funkcji malloc(). AnalogicznÈ operacjÚ przeprowadzasz dla pozo- staïych wagonów. Wiersze 27 – 28: usuwanie wagonu nr 1 jest bardzo proste — prze- stawiasz odpowiedni wskaěnik z wagonu wagon2, tak aby wskazywaï na wagon wars, a nastÚpnie zwalniasz pamiÚÊ zajmowanÈ przez wagon1 za pomocÈ funkcji free(). Dynamiczna alokacja pamiÚci za pomocÈ list jest bezcenna, poniewaĝ w przeciwieñstwie do tablicy nie marnuje siÚ miejsce po usuniÚciu wybranych elementów. Wskaěniki do funkcji Wskaěniki do funkcji to konstrukcje jÚzyka C stosowane przez naj- wiÚkszych guru. Nie sÈ najpopularniejszymi mechanizmami, ale na pewno przydajÈ siÚ w zastosowaniach opisanych w dalszej czÚĂci tej sekcji. Wskaěniki definiuje siÚ, aby wskazywaÊ nie tylko na dane w pamiÚci, ale takĝe na funkcje, które przecieĝ teĝ majÈ swoje adresy. Wskaěniki do funkcji deklaruje siÚ w poniĝszy sposób: int (*wsk_do_funkcji)(int) Nawiasy sÈ konieczne, poniewaĝ w przypadku deklaracji: int *wsk_do_funkcji(int) zadeklarowana zostaïaby funkcja o nazwie wsk_do_funkcji, która zwra- caïaby wskaěnik do zmiennej typu *int. Rozdziaï 5. • JÚzyk C dla guru 109 Po zadeklarowaniu wskaěnika naleĝy go zdefiniowaÊ, przypisujÈc mu adres jakiejĂ funkcji, np. funkcji o prototypie: int funkcja(int) Trzeba pamiÚtaÊ, ĝe typ wartoĂci zwracanej i typ parametrów wskaě- nika i wskazywanej funkcji muszÈ byÊ identyczne. Przypisanie adresu funkcji do wskaěnika jest bardzo proste: wsk_do_funkcji = funkcja; Nazwa funkcji jest teĝ jednoczeĂnie jej adresem. Wywoïanie funkcji poprzez wskaěnik okazuje siÚ równieĝ bardzo proste, np.: int a; int b = 1; a = wsk_do_funkcji(b); Wystarczyïo tylko zamieniÊ nazwÚ funkcji na nazwÚ wskaěnika, który na niÈ wskazuje. Tyle wiedzy teoretycznej o skïadni wskaěników do funkcji. Teraz przydaïoby siÚ przedstawiÊ dla nich jakieĂ zastosowanie. ¥wietnym przykïadem jest mechanizm obsïugi zdarzeñ. Wyobraě sobie, ĝe musisz napisaÊ program, który bÚdzie sïuĝyï do przetwarzania danych z czuj- ników (np. poziomu cieczy w zbiorniku) w pewnej fabryce. Czujnik wysyïa dane do komputera, zwykle z pewnÈ czÚstotliwoĂciÈ, ale moĝe teĝ wykrywaÊ pewne krytyczne zdarzenia. Po przesïaniu takiego sygnaïu i danych na ten temat do komputera potrzebny jest program, który dokïadniej przeanalizuje takie niskopoziomowe dane i zadecyduje, jak zareagowaÊ na takie zdarzenia. Do tego wïaĂnie sïuĝÈ specjalne funkcje, które zajmujÈ siÚ obsïugÈ tego typu zdarzeñ. W programo- waniu obiektowym, które byÊ moĝe poznasz przy okazji nauki jÚzyka C++, stosuje siÚ pojÚcia zdarzenia (z ang. events) i obsïugi zdarzeñ (z ang. event handlers). m W I C Z E N I E 5.3 Napisz program, który pozwoli na obsïugÚ menu uĝytkow- nika. Zaleĝnie od wyboru uĝytkownika program uruchomi odpowiedniÈ funkcjÚ. Zastosuj wskaěniki do funkcji. 1: /* Przyklad 5.3 */ 2: /* Napisz program, ktory zapewni obsáuge przekroczenia poziomu */ 3: /* cieczy w zbiorniku. Zastosuj wskazniki do funkcji */ 4: #include stdio.h 110 Programowanie w jÚzyku C • mwiczenia praktyczne 5: void handler1(float); 6: void handler2(float); 7: void przekroczony_poziom(float, void (*handler)(float)); 8: int main() 9: { 10: float pomiar = 123.58; 11: void (*wsk_do_obslugi)(float); 12: wsk_do_obslugi = handler1; 13: przekroczony_poziom(pomiar, wsk_do_obslugi); 14: wsk_do_obslugi = handler2; 15: przekroczony_poziom(pomiar, wsk_do_obslugi); 16: return 0; 17: } 18: void przekroczony_poziom(float pomiar, void (*handler)(float x)) 19: { 20: printf( Wskazanie czujnika jest podejrzane, uruchamiam obsluge ´zdarzenia ); 21: handler(pomiar); 22: } 23: void handler1(float x) 24: { 25: if (x 100) 26: { 27: printf( Wskazanie czujnika jest na granicach normy. ); 28: printf( Zalecane sprawdzenie czujnika w terminie do 7 ´dni. ); 29: } 30: else printf( Wystapila awaria, zalecana natychmiastowa wymiana ´czujnika ); 31: } 32: void handler2(float x) 33: { 34: if (x 200) 35: { 36: printf ( Wskazanie czujnika jest na granicach normy. ); 37: printf ( Zalecane sprawdzenie czujnika w terminie do 7 ´dni ); 38: } 39: else printf( Wystapila awaria, zalecana natychmiastowa wymiana ´czujnika ); 40: } Wiersze 5 – 7: deklarowane sÈ funkcje uĝywane w programie. Funkcja przekroczony_poziom() jest wywoïywana za kaĝdym razem, gdy wystÈpi odpowiednie zdarzenie — czyli gdy do programu przesïane zostanÈ dane z czujnika. Funkcje handler1() oraz handler2() sïuĝÈ do obsïugi tego zdarzenia. Rozdziaï 5. • JÚzyk C dla guru 111 Wiersz 11: nastÚpuje zdefiniowanie wskaěnika do funkcji. Waĝne jest, aby wstawiÊ nawiasy tam, gdzie trzeba, tak by nie skoñczyïo siÚ na definicji funkcji zwracajÈcej wskaěnik. Parametry oraz wartoĂÊ zwra- cana muszÈ byÊ tego samego typu co funkcje, na które taki wskaěnik bÚdzie wskazywaï. Wiersz 12: przypisanie adresu funkcji do wskaěnika jest bardzo proste, poniewaĝ nazwa funkcji jest jednoczeĂnie jej adresem. Wiersz 13: wywoïywana jest funkcja, która odpowiada wystÈpieniu zdarzenia. W tym przypadku jest to oczywiĂcie duĝe uproszczenie rzeczywistoĂci. Takie funkcje sÈ zwykle automatycznie wywoïywane przez czÚĂÊ programu, która odpowiada komunikacji z urzÈdzeniem (czujnikiem/sterownikiem) np. poprzez port szeregowy. Funkcji prze- kazywany jest parametr (czyli dane odczytane przez czujnik) oraz wskaěnik do funkcji obsïugujÈcej zdarzenie. Wiersze14 – 15: wskaěnikowi do funkcji przypisywany jest adres do innej funkcji, w której zmieniony zostaï sposób obsïugi zdarzenia. DziÚki temu przy kolejnym wystÈpieniu zdarzenia uruchomiona zostaje juĝ inna funkcja obsïugi. Wiersz 21: wywoïywana jest funkcja obsïugujÈca zdarzenie poprzez wskaěnik przekazany jako parametr do funkcji przekroczony_poziom(). Wiersze 23 – 40: definiowane sÈ funkcje obsïugujÈce zdarzenie. Mam nadziejÚ, ĝe wszyscy Czytelnicy zrozumieli zalety takiego pro- gramu. W powyĝszym przykïadzie warto zauwaĝyÊ, ĝe gdy konieczna jest zmiana obsïugi zdarzenia, wystarczy dodaÊ definicjÚ nowej funk- cji obsïugi (bez koniecznoĂci zmiany czy usuwania juĝ istniejÈcych) i zmieniÊ dwa miejsca w kodzie — czyli przypisanie adresu nowej funkcji do wskaěnika i wywoïanie zdarzenia przekroczony_poziom() (wiersze 14 – 15). Moĝe niektórzy Czytelnicy pomyĂleli, ĝe przecieĝ moĝna wykorzystaÊ instrukcjÚ switch, by niepotrzebnie nie bawiÊ siÚ jakimiĂ dziwnymi wskaěnikami do funkcji. Ale co, jeĂli mamy 20 rodzajów obsïugi zdarzenia, co chwilÚ coĂ siÚ zmienia i dodawane sÈ nowe funkcje i mechanizmy? Programy, które pisze siÚ dla profesjonal- nych zastosowañ, nie sÈ statyczne. WciÈĝ coĂ siÚ modyfikuje, popra- wia, usuwa i dodaje. Trzeba zatem zadbaÊ, aby zmieniaÊ tylko to, co jest konieczne. W przeciwieñstwie do amatorskich instrukcji switch nasz kod wyglÈda przejrzyĂcie i profesjonalnie. Programowanie w jÚzyku C • mwiczenia praktyczne 112 Tablice wskaěników do funkcji Tablice wskaěników do funkcji mogÈ sïuĝyÊ do lepszego zarzÈdzania programami podobnymi do tego z Êwiczenia 5.3. Aby zdefiniowaÊ tablicÚ wskaěników do funkcji, które zarówno nie pobierajÈ ĝadnych elementów, jak i nie zwracajÈ ĝadnych wartoĂci, naleĝy jÈ zadeklarowaÊ i zdefiniowaÊ w poniĝszy sposób: void (*wskaļniki_do_funkcji[])(void) = {funkcja1, funkcja2, funkcja3}; Jest to najprostszy przykïad jednoczesnej definicji i deklaracji. Moĝna teĝ oddzielnie deklarowaÊ i definiowaÊ, ale wtedy trzeba siÚ mÚczyÊ z rÚcznym przydziaïem pamiÚci dla takich wskaěników do funkcji za pomocÈ funkcji malloc(). Aby zatem program byï czytelny, zalecam najpierw przypisaÊ jakieĂ wskaěniki (chociaĝby NULL), a ewentualnie póěniej podmieniÊ je na inne. Trzeba jednak pamiÚtaÊ, ĝe kaĝda funk- cja w tablicy wskaěników musi mieÊ takie same parametry i wartoĂÊ zwracanÈ. m W I C Z E N I E 5.4 Napisz program, w którym zdefiniujesz tablicÚ wskaěników do funkcji wykonujÈcych podstawowe operacje arytmetyczne. NastÚpnie wywoïaj je wszystkie w pÚtli, odwoïujÈc siÚ do poszczególnych elementów tablicy. 1: /* Przyklad 5.4 */ 2: /* Przyklad demonstruje uzycie tablicy wskaznikow do funkcji */ 3: #include stdio.h 4: float mnozenie(float, float); 5: float dzielenie(float, float); 6: float dodawanie(float, float); 7: float odejmowanie(float, float); 8: int main() 9: { 10: int i; 11: float x, y; 12: float (*wsk_do_funkcji[])(float, float) = {dodawanie, ´odejmowanie,mnozenie, dzielenie}; 14: printf( Podaj dwie liczby: ); 15: scanf( f , x); 16: scanf( f , y); 17: for (i = 0; i 4; i++) 18: printf( Wynik: f , wsk_do_funkcji[i](x,y)); Rozdziaï 5. • JÚzyk C dla guru 113 19: return 0; 20:} 21:float mnozenie(float a, float b) 22:{ 23: printf( Mnozenie ); 24: return a*b; 25:} 26:float dzielenie(float a, float b) 27:{ 28: printf( Dzielenie ); 29: return a/b; 30:} 31:float dodawanie(float a, float b) 32:{ 33: printf( Dodawanie ); 34: return a+b; 35:} 36:float odejmowanie(float a, float b) 37:{ 38: printf( Odejmowanie ); 39: return a-b; 40:} Wiersz 12 zawiera definicjÚ tablicy wskaěników do funkcji pobiera- jÈcych jako parametry dwie zmienne typu float oraz zwracajÈcych wartoĂÊ równieĝ typu float. Do tablicy przypisane sÈ od razu wskaě- niki do funkcji zadeklarowanych na poczÈtku i zdefiniowanych na koñcu programu. Wiersze 17 – 19 zawierajÈ wywoïanie w pÚtli wszystkich funkcji w tablicy. Wywoïanie funkcji odbywa siÚ prawie tak samo jak przy pojedynczych wskaěnikach do funkcji. Funkcje wywoïujemy poprzez ich nazwÚ, ale dodajemy tylko odpowiedni indeks tablicy przed para- metrami w nawiasach. Wiersze 21 – 40 zawierajÈ tylko proste definicje funkcji wykonujÈcych podstawowe dziaïania arytmetyczne. Preprocesor W przykïadowych programach zamieszczonych w poprzednich roz- dziaïach uĝywane byïy zapisy typu #include oraz #define. SÈ to tzw. dyrektywy preprocesora. Preprocesor to program, który przetwarza 114 Programowanie w jÚzyku C • mwiczenia praktyczne tekst programu, zastÚpujÈc niektóre instrukcje innymi. W praktyce jest on czÚĂciÈ kompilatora, ale przetwarzanie tekstu przez preprocesor nastÚpuje przed samym procesem kompilacji. Preprocesor, analizujÈc program, wyszukuje róĝne dyrektywy (rozpo- czynajÈce siÚ znakiem #) i w zaleĝnoĂci od ich typu zastÚpuje tekst programu w sposób przez nie zdefiniowany. Przykïadowo dyrektywa #include stdio.h nakazuje preprocesorowi wïÈczyÊ do tekstu programu zawartoĂÊ pliku nagïówkowego stdio.h. Natomiast dyrektywa #define PI 3.14, sïuĝÈca do definiowania staïych symbolicznych, instruuje pro- cesor, aby zamieniï wszystkie wystÈpienia sïowa PI w programie liczbÈ 3.14. Przeanalizujmy przykïad programu z Êwiczenia 1.9: 1: /* Przyklad 1.9 */ 2: /* Oblicza pole kuli */ 3: #include stdio.h 4: #define PI 3.14 5: float PoleKuli; 6: const int R = 5; 7: main() 8: { 9: PoleKuli = 4*PI*R*R; 10: printf( Pole Kuli wynosi f , PoleKuli); 11: return 0; 12: } Po przetworzeniu przez preprocesor program bÚdzie wyglÈdaï nastÚ- pujÈco: 1: /* Przyklad 1.9 */ 2: /* Oblicza pole kuli */ 3: Zawartosc pliku stdio.h 4: Pusta linia 5: float PoleKuli; 6: const int R = 5; 7: main() 8: { 9: PoleKuli = 4*3.14*R*R; 10: printf( Pole Kuli wynosi f , PoleKuli); 11: return 0; 12: } W miejscu wiersza 3 pojawi siÚ zawartoĂÊ pliku nagïówkowego stdio.h, wiersz 4 z dyrektywÈ define zniknie, poniewaĝ kompilator nie bÚdzie potrzebowaï tych informacji, natomiast w wierszu 9 symbol PI zosta- nie zastÈpiony wartoĂciÈ staïej symbolicznej — 3.14. Rozdziaï 5. • JÚzyk C dla guru 115 Sparametryzowane makrodefinicje (makra) Dyrektywa #define daje wiÚksze moĝliwoĂci niĝ definicja prostej staïej symbolicznej. Moĝna równieĝ tworzyÊ za jej pomocÈ tzw. sparame- tryzowane makrodefinicje (dalej bÚdÈ zwane po prostu makrami), które sÈ szczególnego rodzaju funkcjami. Prostym przykïadem jest poniĝsze makro funkcji obliczajÈcej maksimum dwóch liczb: #define MAX(x,y) ( (x) (y) ? (x) : (y) ) Ta dziwnie wyglÈdajÈca instrukcja ze znakami ? oraz : to nic innego, jak zwykïa instrukcja warunkowa zapisana w odmienny sposób. Przykïa- dowo nastÚpujÈcy zapis: wynik_operacji = x y ? x : y odczytujemy jako: if (x y) wynik_operacji = x; else wynik_operacji = y; Preprocesor po napotkaniu takiej dyrektywy zamieni wszystkie wystÈ- pienia MAX(x,y) w programie ciÈgiem instrukcji ( (x) (y) ? (x) : (y) ). Moĝna to nazwaÊ takim bezmyĂlnym podstawianiem tekstu w miej- sce innego i porównaÊ z zachowaniem czÚsto obserwowanym wĂród leniwych uczniów lub studentów, które nazywa siÚ copy-paste (kopiuj- -wklej). Preprocesor to wïaĂnie taki leniwy student. Kiedy napotyka ciÈg znaków MAX(x,y), zmazuje go i w jego miejsce bezmyĂlnie wstawia ( (x) (y) ? (x) : (y) ) — niewaĝne, w jakim kontekĂcie MAX(x,y) wystÈpi. Dlatego tak istotne sÈ nawiasy — ich nadmiar nigdy nikomu nie zaszkodziï, warto je wstawiaÊ wszÚdzie tam, gdzie nie ma pewno- Ăci, czy wystÈpi np. prawidïowa kolejnoĂÊ operacji arytmetycznych. Wyobraě sobie nastÚpujÈcy przykïad: #define MAX(x,y) ( x y ? x : y ) if (MAX(a,b) == b) { Dowolny ciæg operacji } Po przetworzeniu instrukcja warunkowa wyglÈdaïaby tak: if ( a b ? a : b == a) 116 Programowanie w jÚzyku C • mwiczenia praktyczne Co by siÚ staïo? Przede wszystkim instrukcja warunkowa nie zwra- caïaby poprawnych wartoĂci — np. gdyby obie zmienne byïy wiÚksze od zera, byïaby prawdziwa. Równieĝ instrukcja a == b dawaïaby nie- poĝÈdane wyniki. Trudno jest jednak przewidzieÊ, jakie rezultaty mogÈ daÊ bezmyĂlne podstawienia tekstu wykonywane przez preprocesor. Warto uĝywaÊ sparametryzowanych makrodefinicji, ale na pewno trzeba zachowaÊ umiar. Z pewnoĂciÈ dobrym zastosowaniem sÈ takie proste funkcje, jak maksimum dwóch liczb. m W I C Z E N I E 5.5 Napisz program, który zdefinuje makro sïuĝÈce do przy- dzielania pamiÚci dla tablicy typu int o sparametryzowanej liczbie elementów. Wypisz na ekranie liczbÚ elementów tablicy po udanym przydziale pamiÚci. 1: /* Przyklad 5.5 */ 2: /* Demonstruje zastosowanie sparametryzowanych makrodefinicji */ 3: /* w celu prostego, dynamicznego przydzialu pamieci */ 4: #include stdio.h 5: #define NEWINT(n) ((int *)malloc(sizeof(int)*(n))) 6: int main() 7: { 8: int *tablica; 9: if (tablica = NEWINT(10)) 10: printf(“Pomyslnie zaalokowano pamiec ”); 11: else 12: return -1; 13: return 0; 14:} W wierszu 5 zawarta jest sparametryzowana makrodefinicja NEWINT(n) definiujÈca funkcjÚ malloc przydzielajÈcÈ pamiÚÊ n elementom typu int. Wykorzystana zostaje ona w wierszu 9, gdzie nastÚpuje zamiana ciÈgu znaków NEWINT(10) na int *)malloc(sizeof(int)*(10))). Kompilacja warunkowa Kompilacja warunkowa to inaczej wybór odpowiednich instrukcji w pliku programu, które faktycznie zostanÈ poddane procesowi kompi- lacji. DziÚki preprocesorowi i jego dyrektywom mamy wiÚc moĝliwoĂÊ stworzenia elastycznego programu, który zmienia siÚ w zaleĝnoĂci Rozdziaï 5. • JÚzyk C dla guru 117 od róĝnych okolicznoĂci przed kompilacjÈ. Najlepszym przykïadem jest tryb debuggowania programu. Debuggowanie to proces testowania programu w poszukiwaniu potencjalnych bïÚdów. W przypadku gdy nie moĝna skorzystaÊ z mechanizmów oferowanych przez róĝne Ărodo- wiska programistyczne (z ang. IDE — Integrated Development Envi- ronment), trzeba polegaÊ na prostych rozwiÈzaniach — np. wypisywa- niu wartoĂci zmiennych za pomocÈ instrukcji printf. Do przeprowadzenia kompilacji warunkowej moĝna zastosowaÊ dyrek- tywy kompilatora #ifdef oraz #infdef. Najlepiej zilustruje to poniĝszy fragment kodu: #define DEBUG int main() { ..... #ifdef DEBUG printf(“Wartosc zmiennej x: d ”, x); #endif ..... } W powyĝszym przykïadzie zdefiniowana zostaïa staïa symboliczna DEBUG — nie musi ona mieÊ ĝadnej wartoĂci. DyrektywÚ #ifdef DEBUG naleĝy odczytaÊ w nastÚpujÈcy sposób: „jeĂli zostaïa zdefiniowana staïa symboliczna DEBUG, to...”. Dyrektywa #endif koñczy dyrektywÚ sïuĝÈcÈ do kompilacji warunkowej. W zwiÈzku z tym, jeĂli zdefiniowana jest staïa DEBUG, kompilacji poddany zostanie fragment kodu wypisujÈcy na ekranie wartoĂÊ zmiennej x. Naleĝy równieĝ zauwaĝyÊ, ĝe dyrek- tywy preprocesora moĝna stosowaÊ takĝe wewnÈtrz funkcji main() — nie tylko na poczÈtku programu. Uwaĝny Czytelnik zauwaĝy pewnie, ĝe kiepski poĝytek z takiej kom- pilacji warunkowej, skoro za kaĝdym razem i tak trzeba edytowaÊ plik programu. Moĝna by tak samo wstawiÊ komentarz przy instrukcji printf(), a ĝeby wyĂwietliÊ wartoĂÊ zmiennych w programie, trzeba by prostu ten komentarz usunÈÊ. Kompilatory jÚzyka C pozwalajÈ na ustawienie odpowiedniej opcji poprzez wywoïanie kompilacji programu, np. w poniĝszy sposób: gcc –D DEBUG plik.c Nie trzeba w tym przypadku uĝywaÊ w programie dyrektywy #define DEBUG. 118 Programowanie w jÚzyku C • mwiczenia praktyczne DyrektywÚ #ifndef stosuje siÚ natomiast najczÚĂciej na samym poczÈtku plików nagïówkowych w poniĝszy sposób: #ifndef MOJ_PLIK_NAGLOWKOWY #define MOJ_PLIK_NAGLOWKOWY ,,,,, Zawartosc pliku nagđowkowego .... #endif W powyĝszy sposób moĝna uniknÈÊ dwukrotnego doïÈczenia do pro- gramu tego samego pliku nagïówkowego. Co powinieneĂ zapamiÚtaÊ z tego cyklu Êwiczeñ? T Co to sÈ struktury ze wskaěnikami i jak je definiowaÊ? T Jakie sÈ zastosowania struktur ze wskaěnikami? T Jak utworzyÊ strukturÚ typu lista i do czego ona sïuĝy? T Jak usuwaÊ i dodawaÊ elementy listy? T Co to sÈ wskaěniki do funkcji i jak je definiowaÊ? T Jak utworzyÊ tablice wskaěników do funkcji? T Jakie jest zastosowanie wskaěników do funkcji? T Co to jest obsïuga zdarzeñ? T Co to sÈ dyrektywy preprocesora? T Jakie znasz dyrektywy preprocesora? T Co to sÈ sparametryzowane makrodefinicje i do czego sïuĝÈ? T W jakim celu uĝywa siÚ dyrektywy #ifndef? Rozdziaï 5. • JÚzyk C dla guru 119 mwiczenia do samodzielnego wykonania m W I C Z E N I E 1. m W I C Z E N I E 2. m W I C Z E N I E 3. m W I C Z E N I E 4. m W I C Z E N I E 5. Rozszerz program z Êwiczenia 5.2, tak aby dodawanie i usu- wanie wagonów moĝna byïo wykonywaÊ poprzez wywoïanie oddzielnych funkcji. Zmodyfikuj program z Êwiczenia 5.2 tak, aby struktura wagon posiadaïa dodatkowy wskaěnik na poprzedni wagon w pociÈgu. Lista, która powstanie w rezultacie tej modyfikacji, jest nazy- wana listÈ dwukierunkowÈ. Dodaj obsïugÚ nowego zdarzenia do programu z Êwiczenia 5.3. Zdefiniuj nowe funkcje do obsïugi zdarzeñ. PamiÚtaj, ĝeby wywoïywaÊ je poprzez wskaěniki do funkcji. Utwórz plik nagïówkowy — naglowkowy.h — i zdefiniuj w nim dwie staïe — TRUE oraz FALSE — reprezentujÈce odpowiednie wartoĂci logiczne. PamiÚtaj o zastosowaniu dyrektyw preprocesora: #ifndef, #define i #endif. Napisz sparametryzowanÈ makrodefinicjÚ obliczajÈcÈ pier- wiastek kwadratowy podanego parametru. Makrodefinicja powinna byÊ wywoïywana np. w ten sposób: SQRT(4), jeĂli chciaïbyĂ obliczyÊ pierwiastek kwadratowy z liczby 4.
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Programowanie w języku C. Ćwiczenia praktyczne. Wydanie II
Autor:

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: