str 1 Adam K. Majczak C~ ~- w 48 godzin INTERSOFTLAND WARSZAWA 1993 str 2 ~ Copyright by Adam K. Majczak Wszelkie prawa zastrzeżone Żadna częœć tej pracy nie może być powielana, czy rozpowszechniana w jakiejkolwiek formie i w jakikolwiek sposób, bšdŸ elektroniczny, bšdŸ mechaniczny, włšcznie z fotokopiowaniem, nagrywaniem na taœmy lub przy użyciu innych systemów, bez pisemnej zgody wydawcy. ISBN 83-85515-33-X ~ by INTERSOFTLAND WARSZAWA 1993 r. Podpisano do druku w kwietniu I993 r Druk ukończono w maju 1493 r. Łamanie: STAND's, tel. 665-58-33 Druk i oprawa: Papcr & Tinta, Warszawa, ul. Klaudyny 32 str 3 Goršce podzigkowania dla Nonny, bez pomocy i cierpliwoœci której niniejsza ksišżka nigdyby nie powstała. Adam Majczak str 4 Zastrzeżone znaki towarowe i handlowe: Turbo C, Turbo C + + , Turbo Pascal, TASM, BASM, Turbo Vision, ObjectVision, ObjectWindows sš zastrzeżonymi znakami firmy Borland International Inc. SCO C+ + jest zastrzeżonym znakiem handlowym firmy The Santa Cruz Operation. Windows i Microsoft C jest zastrzeżonymi znakami handlowym firmy Microsoft Corp str 5 Spis treœci Wprowadzenie . . 6 OD AUTORA czyli dla kogo i po co jest ta ksišżka Konwencja zastosowana w niniejszej ksišżce . Jak korzystać z niniejszej ksišżki . . . . Czgœć I. Klasyczny jgzyk C . . . 9 LEKCJA 1. Co o "C" każdy wiedzieć powinien . 11 LEKCJA 2. Główne menu i inne elementy IDE . . 19 LEKCJA 3. Jeszcze o IDE Turbo C+ + . . 26 LEKCJA 4. Z czego składa się program . . . LEKCJA 5. Jakich słów kluczowych używa Turbo C+ + LEKCJA 6 Jakie operatory stosuje Turbo C+ + . LEKCJA 7. Jak deklarować zmienne. Co to jest pointer? . LEKCJA 8. Pointery i tablice w "C' LEKCJA 9. Jak tworzyć w programie pętle i rozgałęzienia LEKCJA 10. Jak tworzyć i stosować struktury 104 LEKCJA 11. Jak posługiwać się funkcjami . . 114 Czgœć II. OOP - Programowanie obiektowe w Turbo C+ + . . 132 LEKCJA 12. Operacje plikowe i wstęp do strumieni danych 135 LEKCJA 13. Poczštek programowania obiektowego - klasy i obiekty . . 147 LEKCJA 14. Manipulujemy obiektami . . . 157 LEKCJA 15. Funkcje wirtualne i polimorfizm . 168 LEKCJA 16. Jak zobaczyć obiekt . . . 181 DODATEK A. Trochę praktycznych porad 190 DODATEK B. Dla zaawansowanych i majšcych ambitne plany . . 197 Spis literatury dodatkowej, szczególnie zalecanej . . 207 B.W Kerninghan, D.M. Ritchie - "Język C", WNT 1987 R. Wacławek - "Turbo C v.2", Intersoftland 1990 J . Bielecki: "Od C do C+ + programowanie obiektowe w języku C", WNT 1990 "Turbo C dla programistów" WKiŁ 1989 5 str 6 Wprowadzenie OD AUTORA czyG dla kogo i po co jest ta ksišżka Szanowny Czytelniku! Wiem z własnego doœwiadczenia, że przedmowy, wstępy, zakończenia, epilogi itp. sš często opuszczane (bo kto traciłby czas na takie dyrdymały?). Skoro jednak tu zajrzałeœ, to zapewne uczyniłeœ tak z co najmniej dwu powodów: - Po pierwsze - aby upewnić się, czy wybrałeœ odpowiedni dla siebie podręcznik. - Po drugie - aby uzyskać dodatkowe wskazówki, w jaki sposób szybko i skutecznie, korzystajšc z ksišżki, stać się wprawnym programistš, posługujšcym się biegle językiem C. Spróbuję więc odpowiedzieć Ci na tak właœnie sformułowane pytania. Być może dziœ właœnie przyszedłeœ do pracy i zastałeœ na swoim komputerze zainstalowany nowy kompilator języka C. Twój szef chce, by Twoje programy były bardziej eleganckie i profesjonalne, bardziej skuteczne. Możejesteœ studentem, którego za kilka miesięcy czeka zaliczenie lub egzamin z informatyki (o kolokwiach po drodze nie wspomnę). Może znasz już BASIC i PASCAL i chcesz wiedzieć dlaczego większoœć profesjonalnych programistów na całym œwiecie od kilku lat posługuje się właœnie CiC++. Z całš pewnoœciš zechcesz: * Szybko zaznajomić się z działaniem kompilatora Turbo C + + . * Okreœlić, jakie własnoœci języka C pozwolš Ci tworzyć programy szybsze i bardziej skuteczne. Będziesz potrzebował krótkiego, dobrego podręcznika, który w prosty sposób * wyjaœni Ci zasady tworzenia programów w języku C, * uchroni Cię przed "odkrywaniem Ameryki", zapoznajšc Cię z gotowymi funkcjami, które masz do dyspozycji jako użytkownik Turbo C + + . * pomoże Ci zrozumieć angielskš terminologię, angielsko-języczne nazwy i komunikaty. Krótko mówišc, potrzebna Cijest WŁAŒNIE TA KSIĽŻKA: "C + + W 48 godzin!". "C+ + W 48 godzin!" - co to oznacza? Niniejsza ksišżka przeznaczona jest dla czytelników pragnšcych w szybki i pcosty sposób opanować podstawy programowania w języku C + + . W trakcie lekcji, z których żadna nie powinna zajšć Ci wi‡cej niż 60 minut, szybko i łatwo opanujesz podstawowe umiejętnoœci potrzebne Ci do tworzenia w języku C + + programów wykonujšcych wszystkie typowe działania, poczšwszy od prostych obliczeń i manipulowania tekstami, aż do programowania obiektowego. Jeœli po maksimum godzinnej lekcji, drugš godzinę poœwięcisz na zadania, własne eksperymenty i dociekania szczegółów (diabeł podobno siedzi właœnie w szczegółach), a trzeciš godzinę na dokładne przemyœlenie zagadnienia, to spędzisz nad tym kursem właœnie wymienione w tytule 48 godzin. 6 str 7 W ksišżce opisano sposoby rozwišzania wszystkich podstawowych i typowych zadań. Opracowane w prosty i przystępny sposób lekcje pozwolš Ci uzyskać potrzebne umiejętnoœci bez potrzeby długich i ucišżliwych ćwiczeń a także bez koniecznoœci studiowania dokumentacji. Konwencja zastosowana w niniejszej ksišżce Ksišżka składa się z 16 lekcji. Każda zawarta w niniejszej ksišżce lekcja zawiera dokładnš instrukcję, jak zredagować, skompilować i wykonać okreœlone programy przykładowe ilustrujšce różne własnoœci języka C. Instrukcja jest opracowana metodš KROK po KROKU, czyli STEP-by-STEP. W końcowej częœci lekcji znajdziesz kilka zadań do samodzielnego wykonania, które powinny pomóc Ci nabrać wprawy w tworze- niu programów w języku C. Aby ułatwić Ci orientację, zastosowano następujšce oznaczenia, sygnalizujšce w treœci ksišżki niektóre specyficzne fragmenty tekstu: - Wprowadzenie i wyjaœnienie, - Słowniczek, czyli wyjaœnienie pojęć i terminów, - Program przykładowy ilustrujšcy dany mechanizm, wraz z komen- tarzem, - Zadanie/a do samodzielnego wykonania. - typowe błędy i kłopotliwe sytuacje - komentarz i rady - Praktyczne rady, m.in. jak zrobić to samo szybciej; str 8 Dodatkowo, niektóre fragmenty tekstu wyróżniono przy pomocy innego kształtu czcionek: Komunikaty na ekranie Tekst pojawiajšcy się na ekranie będzie podawany w oryginal- nym brzmieniu angielskim a w nawiasie obok znajdziesz thimaczenie. Abort, Retry, Fail? (Zrezygnować, Jeszcze raz, Awaria ?). Klawisze Kombinacje klawiszy służšce do wyboru opcji lub rozkazu z menu bšdŸ z okienka dialogowego ujęto w nawiasy kwad- ratowe. [Ctrl]-[Alt]-[Del). Jak korzystać z niniejszej ksišżki Autor radzi Ci przerabiać lekcje kolejno i w całoœci, aż do uzyskania biegłoœci i swobody w poshxgiwaniu się językiem C + + . Jeœli niektóre zagadnienia sš Ci już znane, możesz oczywiœcie pominšć kilka poczštkowyćh lekcji. Jakkolwiek postšpisz, zwróć uwagę na DODATEK A, gdzie znajdziesz instrukcję, jak zainstalować kompilator Turbo C + + na Twoim komputerze i odpowiedzi na najbardziej typowe pytania. Jeœli zechcesz wiedzieć więcej... Jeœli zechcesz wiedzieć więcej (mam nadzieję, że ta ksišżka stanie się dla Ciebie poczštkiem wielkiej przygody z językiem C) możesz sięgnšć do podręczników podanych w spisie literatury. Jeœli jesteœ "starym" użytkownikiem PC, to wymienione tam nazwiska Petera Nortona, Denisa Ritchie czy Jana Bieleckiego nie sš Ci zapewne już całkiem obce. str 9 Czgœć I. Klasyczny jgzyk C str 10 str 11 Lekcja 1 Co o C każdy wiedzieć powinien W trakcie tej lekcji poznasz sposoby rozwišzania typowych problemów występujšcych przy uruchomieniu Turbo C+ ~- . Język C jest uniwersalnym, nowoczesnym językiem programowania opracowanym przez Denisa M. Ritchie i Kena Thompsona dla systemu operacyjnego UNIX kom- puterów PDP 11 Grmy Digital Equipment Corp. (USA). Za oficjalnego ojcajęzyka C+ + uważany jest Bjarne Stroustrup a za datę urodzin i nadania nazwy czyli "chrztu" C + + uznaje się rok 1983. Wieloletnie ograniczenia COCOMu w dostępie do nowoczesnej technolog sprawiły m. in., że popularnoœć UNIXa i C jest w Polsce do dziœ nieproporcjonalnie mała, a Basica, Pascala i DOSa nieproporcjonalnie duża. Ale chyba już pora zaczšć odrabiać te straty. Już widzę Twojš minę, Czytelniku i słyszę oburzenie (A co mnie obchodzi historia "komputerologii" i koligacyjki!). Otóż obchodzi, bo wynikajš z niej pewne "grzechy pierworodne" języka C, a dla Ciebie, szanowny Czytelniku - pewne wnioski praktyczne. Grzech Pierwszy: * Kompilatorjęzyka Cjest standardowym wyposażeniem systemu operacyjnego UNIX. Skutki praktyczne: Każdy PC jest w momenc;ie zakupu (co często wchodzi w cenę zakupu komputera) wyposażany w system operacyjny DOS - np. MS DOS 3.30 lub MS DOS 5.0. Standardowo w zestaw systemu MS DOS wchodzi interpreter języka BASIC (BASICA, GWBASIC, Quick Basic itp.). Możesz więc być pewien, że jeœli jest DOS, to musi być i BASIC. Podobnie rzecz ma się z C. Jeœli jest na komputerze system UNIX/XENIX (za wyjštkiem najuboższych wersji systemu XENIX pozbawionych kompilatora C), to masz tam do dyspozycji kompilator C, za to BASICA prawie na pewno tam nie ma. Grzech drugi: * Język C powstał jeszcze zanim wymyœlono PC, DOS, GUI (Graficzny Interfejs Użytkownika), Windows i inne tym podobne. Skutki praktyczne: I. W założeniach twórców język C miał być szybki (i jest) i zajmować mało miejsca w pamięci (bo ówczesne komputery miały jej bardzo mało!). Zawiera więc różne, niezrozumiałe dla nas z dzisiejszego punktu widzenia skróty. Np. to co w Pascalu czy Basicu wyglšda zrozumiale: i: = i + 1; (Pascal) 11 str 12 10 I = I + 1 lub inaczej N EXT I (Basic) to w języku C wyglšda dziwacznie: i + + ; albo jeszeze dziwniej + + i; Tym niemniej zwróć uwagę, że w Pascalu zajmuje to 7 znaków, w Basicu - 8 znaków (spacja to też znak!), a w C tylko 4. Inny przykład: X = X + 5 (Basic, 5 znaków), X: = X + 5 (Pascal, 6 znaków), X + = 5 (C + + , tylko 4 znaki). Z takiej właœnie filozofii wynika i sama nazwa - najkrótsza z możliwych. Jeœli bowiem i + + miało znaczyć mniej więcej tyle samo co NEXT I (następne I) to C + + znaczy mniej więcej tyle samo co "NASTĘPNA WERSJA C". II. Jak zapewne wiesz, nie ma nic za darmo. W języku C, podobnie jak w samochodzie wyœcigowym formuły I, za szybkoœć i skutecznoœć płaci się komfortem użytkownika. Konstrukcje stosowane w języku C sš bardziej dostosowane do wygody komputera, niż do wygody programisty. Co to oznacza w praktyce przekonasz się w trakcie opracowywa- nia programów. Grzech Trzeci (i chyba najcięższy): * Jest najlepszy. Ostrożniej - jest najchętniej stosowanym narzędziem profesjonalnych programistów. Najpierw uzasadnienie. Aby nie wdawać się w rozważania "O wyższoœci œwišt Wielkiej Nocy nad œwiętami Bożego Narodzenia" (przepraszam profesora J. T. Stanisławskiego za nieautoryzowane zapożyczenie) przytoczę jako argument tylko jeden fakt. Od kilku już lat na całym œwiecie najwięcej programów pisanych jest właœnie w języku C. Skutki praktyczne: Nauczywszy się języka C możesz nie bać się ani systemu UNIX/XENIX, ani komputerów RISC i stacji roboczych, ani dużych komputerów klasy mainframe. Język C dosłużył się bowiem ogromnej iloœci tzw. implementacji czyli swoich odmian, przeznaczonych dla różnych komputerów i dla różnych systemów operacyjnych. Z Grzechu Trzeciego (choć nie tylko) wynika także poœrednio Grzech Czwarty. Języka C Grzech Czwarty - ANSI C, C, C + + , czy Turbo C, czyli mała wieża BABEL. Nie jestem pewien, czy "wieża BABEL" jest okreœleniem trafniejszym niż "kamień filozoficzny", bšdŸ "perpetuum mobile". To co w cišgu ostatnich lat stało się zjęzykiem C ma coœ wspólnego z każdym z tych utopijnych symboli. A w dużym uproszczeniu było to tak. Podobnie, jak mechanikom od zarania dziejów marzyło się perpetuum mobile, tak informatykom zawsze marzyło się stworzenie jednego SUPER-UNIWERSALNEGO języka programowania. Takiego, który Myłby zupełnie niezależny od sprzętu tzn., aby program napisany w takim języku mógł być przeniesiony BEZ ŻADNYCH ZMIAN na dowolny komputer I DZIAŁAŁ. Do takiej roli pretendowały kolejno FORTRAN, Algol a potem przyœzła pora i na C. Gdyby informatycy nie okazali się zbyt zachłanni, może coœ by z tego wyszło. Ale, jak to w życiu, programiœci (podobnie jak żona rybaka z bajki O rybaku i złotej rybce) chcieli wszystkiego naraz: l2 str 13 * żeby program dał się przenieœć na komputer innego typu i działał, * żeby działał szybko i optymalnie wykorzystywał sprzęt, * żeby umiał wszystko, co w informatyce tylko wymyœlono (tj. i grafika i obiekty i rekordy i obsługa peryferii i...). I stało się. W pomyœlanym jako uniwersalny języku zaczęły powstawać odmiany, dialekty, mutacje, wersje itp. itd. Jeœli C nie jest Twoim pierwszym językiem, to z pewnoœciš zauważyłeœ Czytelniku, że pomiędzy GW Basic a Quick Basic sš pewne drobne różnice. Podobnie Turbo Pascal 6.0 trochę różni się od Turbo Pascala 5.0. Mimo to przykład poniżej pewnie Cię trochę zaskoczy. Dla zilustrowania skali problemu przedstawiam poniżej dwie wersje TEGO SAMEGO PROGRAMU napisanego w dwu różnych wersjach TEGO SAMEGO JĘZYKA C. Program pierwszy (1) pochodzi z ksišżki [2] Kerninghana i Ritchiego "Język C" natomiast drugi (2) z artykułu "Nowe metody programowania obiektowego..." [10]. Obydwa programy robiš dokładnie to samo. Majš za zadanie wypisać na ekranie napis "HELLO WORLD" (czyli "czeœć œwiecie!"). Program ( 1 ) main() printf("HELLO WORLD\n"); Program (2) /*UWAGA: Turbo C+ + tego "nie strawi!" nie ma OI/oi.H !*/ ~include < OI/oi.H > void main (int argc, char **argv) OI connection *conp; OI app~window *wp; OI static text *tp; if(conp=OI init( &argc, argv, "Test")) { wp=oi create app~window( "main",200,100,"Main"); wp->set layout( OI layout row ); tp = oicreatestatictext( "text", HELLO WORLD ); tp- > layout associated object( wp,l ,1,OI ACTIVE ); wp->set associated object( wp->root(), OI~DEF~LOC, OI DEF LOC, OI ACTIVE ); OI begin interaction(); oi fini(); Cóż za uderzajšce podobieństwo, prawda? Ale żarty na bok. Jeœli zaistniejejakiœ problem, to zawsze mamy co najmniej trzy wyjœcia. Możemy: 1. Udawać, że go nie ma. Tak postępuje wielu autorów podręczników na temat C. 2. Krzyczeć, że nam się to nie podoba, bo to BEEE. Mamy pełne prawo obrazić się i wrócić do Basica. 13 str 14 3. Spróbować poruszać się w tym gšszczu. Wyjœcie trzecie ma jednš wadę - jest najtrudniejsze, ale i efekty takiego wyboru sš najbardziej obiecujšce. Skoro wybrałeœ wyjœcie trzecie, spróbujmy zrobić pierwszy krok w tej "dżungli". Wyjaœnijmy kilka nazw, pojęć i zasad gry obowišzujšcych w tym obszarze. W businesie komputerowym, jak i wszędzie gdzie indziej chodzi o pienišdze. Dopóki pieniędzmi płaci się za sprzęt (czyli po angielsku "HARDWARE") - wszystko jest oczywiste. Ale w komputerach stosunkowo mało warta jest miedŸ, stal, plastik, a nieproporcjonalnie dużo - MYŒL TECHNICZNA. Ta myœl nazywa się różnie - SOFTWARE, KNOW HOW, bardziej swojsko - licencja, patent, wynalazek, czy własnoœć intelektualna. Finansowo-prawnš formš ochrony sš patenty, prawa autorskie, znaki handlowe i towarowe. Wykaz zastrzeżonych znaków i nazw towarowych i hand- lowych, na które się powołuję, wypisałem na poczštku ksišżki. Z lektury takiego spisu wynika np. kto liczy się w danej branży i o czym będzie ksišżka. Być może nie zwróciłeœ na to uwagi, więc przytoczę niektóre nazwy wraz z krótkim wyjaœnieniem jeszcze raz. SCO C + + Kompilator języka C wzbogacony o możliwoœci programowania obiektowego, prze- znaczony dla systemu UNIX. Producent: The Santa Cruz Operation. ANSI C Standardjęzyka C opracowany w Amerykańskim Narodowym Instytucie Standardów (w skrócie ANSI). To ten od ANSI.SYS, poznajesz? Microsoft C Kompilator języka C dla komputerów PC opracowany przez firmę Microsoft Corp., nazywany czasem w skrócie MS C. Borland Turbo C Popularny kompilator języka C dla PC pracujšcych w systemie DOS. Szczególnie popularna w Polsce jest wersja Turbo C 2.0. Borland Turbo C + + Kompilator języka C firmy Borland Int. wzbogacony o możliwoœci programowania obiektowego. Jak pewnie zauważyłeœ, tak się jakoœ przyjęło, że C+ + oznacza te wersje kom- pilatorów, które posiadajš możliwoœci programowania obiektowego. Nie jest to jedyna różnica. C + + wyposażony jest w możliwoœć nadawania nowych znaczeń operatorom i funkcjom (ang. overloading), co umożliwia rozbudowę istniejšcych starszych pro- gramów pisanych w "zwykłym" C. C+ + był poczštkowo nazywany "C z klasami". Dla ujednolicenia języka C decydujšce znaczenie ma istnienie standardu ANSI C, z którym zarówno Borland Turbo C + + , jak i Microsoft C sš w bardzo dużym stopniu godne (po wybraniu odpowiedniej opcji programu konfigurujšcego można uzyskać 100 /o zgodnoœci ze standardem ANSI C). Bjarne Stroustrup i ANSI w 1990 roku wprowadzili standard ANSI C + + 2.1 z którym zgodne sš najnowsze wersje Turbo C + + 3.0 oraz Microsoft C + + 7. Ponieważ jednak ta ksišżka ma w założeniu autora pretendować raczej do roli "elementarza C+ + " niż "encykloped C+ + ", ograniczę się tu tylko do zasygnalizowania tego zagadnienia bardziej dociekliwym Czytelnikom. W niniejszej ksišżce zajmiemy się kompilatorem Borland Turbo C + + w jego wresji podstawowej 1.0, jest to bowiem najpopularniejszy (obok starszej wersji Borland Turbo 14 str 15 C 1.5/2.0) w Polsce kompilatorjęzyka C przeznaczony dla komputerów IBM PC. Nie bez znaczenia dla tej decyzji był także fakt, że Turbo C i Turbo C+ + bez konfliktów współpracujš z pakietami: * Turbo Pascal 6.0; * Turbo Assembler 2.0; * Turbo Debugger; * Turbo Profiler, Turbo Vision, ObjectVision i in. produktami ze stajni Borlanda popularnymi wœród polskich programistów. Jeœli Twój szef wybrał Microsoft C/C + + , nie martw się. Po dokonaniu kosmetycznych zmian, wszystkie programy dadzš się uruchomić. I jeszcze jeden czynnik, który może stać się Twoim, czytelniku atutem. Jeœli znasz już Turbo Pascal firmy Borland Int. to zwróć uwagę, że wiele funkcji zaimplementowanych w Turbo Pascal 5.5-7.0 ma swoje odpowiedniki w Turbo C + + . Odpowiedniki te zwykle działajš dokładnie tak samo, a różniš się najczęœciej nieznacznie pisowniš nazwy funkcji. Wynika to z "lenistwa". Firmie Borland Int. nie chciało się wymyœlać od nowa tego, cojuż sprawdziło się wczeœniej i do czego przyzwyczaili się klienci! I odwrotnie. Poznawszy Turbo C+ + z łatwoœciš zauważysz te same funkcje w Turbo Pascalu. A teraz do roboty. Zaczynamy Jak korzystać z Turbo C~- ~- UWAGA: Z A N I M rozpoczniesz pracę z dyskietkš dołšczonš do niniejszej ksišżki radzimy Ci przy pomocy rozkazu DISKCOPY, np. DISKCOPY A: A: lub DISKCOPY B: B: SPORZĽDZIĆ ZAPASOWĽ KOPIĘ DYSKIETKI. Unikniesz dzięki temu być może wielu kłopotów, których może Ci narobić np. przypadkowy wirus. Dyskietka jest Typu DSDD -- o pojemnoœci 360 KB. URUCHOMIENIE TURBO C+ + . Aby uruchomić Turbo C+ + powinieneœ w linii rozkazu po DOS'owskim znaku zachęty (zwykle C > lub C:\ > ) wydać polecenie: TC i nacisnšć [Enter]. Jeœli Twój komputer odpowiedział na to: Bad command or file name to: * na Twoim komputerze nie ma Turbo C + + ; ROZWIĽZANIE: Zainstaluj Turbo C+ + (patrz DODATEK). * w pliku AUTOEXEC.BAT nie ma œcieżki dostępu do katalogu, w którym za- instalowany jest Turbo C + + . 15 str 16 ROZWIĽZANIE: 1. Zmienić bieżšcy katalog (i ewentualnie dysk) na odpowiedni, np.: D: [Enter] CD D:\TC\BIN[Enter]. Albo 2. Ustawić œcieżkę dostępu przy pomocy rozkazu: PATH C:\TC\ B I N (lub D:\TC\BINstosownie do rozmieszczenia plików na Twoim komputerze; najlepiej zasięgnij rady lokalnego eksperta). NIE CHCE USTAWIĆ ŒCIEŻKI? Musisz pozbyć się "na chwilę" programu NC. Naciœnij [F 10] - Quit i potwierdŸ przez [Y) lub [Enter]. Po ustawieniu œcieżek możesz powtórnie uruchomić NC. Albo 3. Dodać do pliku AUTOEXEC.BAT dodatkowš œcieżkę. Jest to wyjœcie najlepsze. Na końcu linii ustawiajšcej œcieżki - np.: PATH C:\; C:\DOS; C:\NC; dodaj œcieżkę do Turbo C + + , np. : PATH C:\; C:\DOS; C:\NC; D:\TC\BIN; Załatwi to problem "raz na zawsze". Po uruchomieniu komputera œcieżka będzie odtšd zawsze ustawiana automatycznie. Jeœli wybrałeœ wariant trzeci, wykonaj przeładowanie systemu [Ctrl]-[Alt]-[Del]. Teraz możesz wydać rozkaz TC [Enter) Mam nadzieję, że tym razem się udało. I oto jesteœmy w IDE Turbo C+ + . Jeœli nie jesteœ jedynym użytkownikiem Turbo C + + , na ekranie rozwinie się cała kaskada okienek roboczych. Skonsultuj z właœcicielem, które z nich można pozamykać a które pliki można skasować lub przenieœć na dyskietkę. Pamiętaj primo non nocere - przede wszystkim nie szkodzić! IDE = Integrated Development Environment, czyli Zintegrowane Œrodowisko Uruchomieniowe. Bardziej prozaicznie - połšczony EDY- TOR i KOMPILATOR. Zapewne znasz już coœ podobnego z Pascala lub Quick Basica. Od dziœ będzie to Twoje œrodowisko pracy, w którym będziesz pisać, uruchamiać i modyfikować swoje programy. 16 str 17 DISK FULL! Co robić, jeœli przy próbie uruchomienia Turbo C + + odpowiedział Ci: Disk fulll Not enough swap space. Program TC.EXE jest bardzo długi. Jeœli wydasz rozkaz DIR TC.EXE uzyskasz odpowiedŸ, jak poniżej: Volume in drive E has no label Volume Serial Number is 3F22-OFEB Directory of E:\TC\BIN TC EXE 87648005-04-90 1:OOa 1 file(s) 876480 bytes 1658880 bytes free Ponieważ plik TC.EXE nie mieœci się w 640 K pamięci musi dokonywać tzw. SWAPOWANIA i tworzy na dysku dodatkowy plik tymczasowy (ang. swap file). Na dysku roboczym Turbo C+ + musi pozostać najmniej 300 KB wolnego miejsca. Jeœli możesz, pozostaw na tym dysku nie mniej niż 1 MB wolnego miejsca. Ułatwi to i przyspieszy pracę. Tworzony tymczasowo plik roboczy wyglšda tak: Volume in drive D has no label Directory of D:\SIERRA TCOOOA SWP 26214412-13-92 5:42p (13-XII to dziœl) 1 file(s) 262144 bytes 696320 bytes free UWAGA: Turbo C + + będzie próbował tworzyć plik tymczasowy zawsze w bieżšcym katalogu, tzn. tym, z którego wydałeœ rozkaz TC Wnioski praktyczne: * Lepiej nie uruchamiać Turbo C+ + siedzšc na dyskietce, ponieważ może mu tam zabraknšć miejsca na plik tymczasowy. * Dla użytkowników Novella: Uruchamiajcie TC tylko we własnych katalogach - do innych możecie nie mieć praw zapisu. Plik TCOOOA.SWP jest tworzony tylko podczas sesji z Turbo C++ i usuwany natychmiast po jej zakończeniu. Możesz go zobaczyć tylko wychodzšc "na chwilę" do systemu DOS przy pomocy rozkazu DOS Shell (menu File). 17 str 18 SWAP - Zamiana. Jeœli wszystkie dane, potrzebne do pracy programu nie mieszczš się jednoczeœnie w pamięci operacyjnej komputera, to program - "właœciciel", lub system operacyjny może dokonać tzw. SWAPOWANIA. Polega to na usunięciu z pamięci operacyjnej i zapisa- niu na dysk zbędnej w tym momencie częœci danych, a na ich miejsce wpisaniu odczytanej z dysku innej częœci danych, zwykle takich, które sš programowi (systemowi) pilnie potrzebne do pracy właœnie teraz. 1. I SprawdŸ ile bajtów ma plik TC.EXE w tej wersji Turbo C + + , której używasz. 1.2. Posługujšc się rozkazem DOS Shell z menu File sprawdŸ gdzie znajduje się ijakiejjest wielkoœci plik tymczasowy Twojego TC.EXE. Ile masz wolnego miejsca na dysku? 18 str 19 Lekcja 2 Główne menu i inne elementy IDE Wtrakcie tej lekcji dowiesz sięjak poruszać się w zintegrowanym œrodowisku (IDE) Turbo C-H + . Najważniejszš rzeczš w œrodowisku IDE jest GŁÓWNE MENU (ang. MENU BAR), czyli pasek, który widzisz w górnej częœci ekranu. Działa to podobnie, jak główne menu w programie Norton Commander (dostępne tam przez klawisz [F9]). KRÓTKI PRZEGLĽD GŁÓWNEGO MENU Przyciœnij klawisz [F10]. Główne menu stało się aktywne. Teraz przy pomocy klawiszy kursora (ze strzałkami [ < -], [- > ]) możesz poruszać się po menu i wybrać tę grupę poleceń, która jest Ci potrzebna. A oto nazwy poszczególnych grup: GRUPY POLECEŃ - NAZWY POSZCZEGÓLNYCH "ROZWIJA- NYCH" MENU. Bez nazwy (menu systemowe). FILE Operacje na plikach. EDIT Edycja plików z tekstami Ÿródłowymi programów. SEARCH Przeszukiwanie. RUN Uruchomienie programu. COMPILE Kompilacja programu. DEBUG Odpluskwianie, czyli wyszukiwanie błędów w programie. PROJECT Tworzenie dużych, wielomodułowych programów. OPTIONS Opcje, warianty IDE i kompilatora. WINDOW Okna (te na ekranie). HELP Pomoc, niestety po angielsku. ROZWIJAMY MENU Z takiego kręcenia się w kółko po pasku (a propos, czy zauważyłeœ, że pasek podœwietlenia może być "przewijany w kółko"?) jeszcze niewiele wynika. Robimy więc następny krok. 19 str 20 Wskaż w menu głównym nazwę "FILE" i naciœnij [Enter]. Rozwinęło się menu File zawierajšce listę rozkazów dotyczšcych operacji na plikach. Po tym menu też możesz się poruszać przy pomocy klawiszy kursora ze strzałkami górę lub w dół. Masz do wyboru dwie grupy rozkazów rozdzielone poziomš liniš: OPEN - Otwórz istniejšcy już plik z programem (np. w celu dopisania czegoœ nowego) lub nowy plik. NEW - Utwórz nowy plik (zaczynamy tworzyć nowy pro- gram). ~' SAVE - Zapisz bieżšcy program na dysk. Pamiętaj: Pliki z dysku nie znikajš po wyłšczeniu komputera. Zawsze lepiej mieć o jednš kopię za dużo niż o jednš za mało. oraz PRINT - Wydrukuj program. GET INFO - Wyœwietl informacie o stanie IDE. DOS SHELL - Wyjœcie "na chwilę" do systemu DOS z możliwoœciš powrotu do IDE przez rozkaz EXIT. QUIT - Wyjœcie z IDE Turbo C+ + i powrót do DOSa. Inaczej - KONIEC PRACY. Skoro już wiemy jak rozpoczšć pracę nad nowym programem, zacznijmy przygotowa- nie do uruchomienia naszego pierwszego programu. Wybierz z menu File rozkaz OPEN... (otwórz plik). Ponieważ rozkaz taki jest niejednoznaczny, wymaga przed wykonaniem podania dodatkowych informacji. Gdyby Twój komputer mówił, zapytałby w tym momencie "który plik mam otworzyć?". Na razie niestety nie mówi, a pytanie zadać musi, będzie więc prowadził dialog z Tobš przy pomocy tzw. OKIENEK DIALOGOWYCH. Jeœli wybrałeœ z menu rozkaz OPEN i nacisnšłeœ [Enter], to masz właœnie na ekranie takie okienko dialogowe. Okienko składa się z kilku charakterystycznych elementów: OKIENKO TEKSTOWE - (ang. Text Box lub Input Box) w którym możesz pisać (klawisz Back Space [ < -] pozwoli Ci skaso- wać wprowadzony tekst, jeœli się rozmyœlisz). Okienko to zawiera tekst "*.C". OKIENKO Z LISTĽ - (ang. List Box) zawiera listę plików, z której możesz wybrać plik z programem. KLAWISZE OPCJI/POLECEŃ - (ang. Command Button) kiedy już dokonasz wyboru, to możesz wskazujšc taki klawisz np. potwierdzić [OK], zrezygnować [Cancel], otwo- rzyć plik [Open] itp. Pomiędzy elementami okienka dialogowego możesz poruszać się przy pomocy klawiszy kursora i klawisza [Tab] lub kombinacji klawiszy [Shift]-[Tab] (spróbuj!). Więcej o okienkach i menu dowiesz się z następnych lekcji, a na razie wróćmy do naszego podstawowego zadania - tworzenia pierwszego programu. Zanim zaczniemy tworzyć program włóż do kieszeni napędu A: dyskietkę dołšczonš do niniejszej ksišżki. Powinna ona stać się Twojš dyskietkš roboczš i pomocniczš zarazem na okres tego kursu. 20 str 21 Wpisz do okienka tekstowego nazwę A:\PIERWSZY. Rozszerzeniem możesz się nie przejmować - zostanie nadane automatycznie. Plik roboczy z Twoim programem zostanie utworzony na dyskietce w napędzie A:. Wskaż klawisz [Open) w okienku dialogowym i naciœnij [Enter] na klawiaturze. UWAGA! Dopóki manipulujesz okienkiem tekstowym i okienkiem z listš klawisz polecenia [Open] jest wyróżniony (podœwietlony) i traktowany jako tzw. OPCJA DOMYŒLNA (ang. default). W tym stadium aby wybrać [Open] WYSTARCZY NACISNĽĆ [Enter]. Wróciliœmy do IDE. zmieniło się tyle, że w nagłówku okna edytora zamiast napisu "NONAMEOO.C" (ang. no mame - bez nazwy) jest teraz nazwa Twojego programu - PIERWSZY.C. Kursor miga w lewym górnym rogu okna edytora. Możemy zaczynać. WPISUJENIY PROGRAM PIERWSZY.C Wpisz następujšcy tekst programu: #include < stdio.h > main() printf("Autor: ... ... .."); /'tu wpisz imie Twojel"/ printf("TO JA, TWOJ PROGRAM - PIERWSZY.C"); printf("...ahoj III"); I już. Jak widzisz nie jest to aż takie straszne. Gdyby nie to, że zamiast znajomego PRINT"TO JA...", albo writeln('..'); jest printf("...")" byłoby prawie całkiem zro- zumiałe. Ale najpierw sprawdzimy czy program działa. Tam, gdzie sš kropki wpisz Twoje imię - np. Ewa, Marian, Marcin. Pamiętaj o postawieniu na końcu znaków cudzysłowu ("), zamknięciu nawiasu i œredniku (;) na końcu linii. Naciœnij kombinację klawiszy [Alt)-[R). Jest to inny, niż opisano poprzednio sposób dostępu do menu. Kombinacja klawiszy [Alt)-[Litera] powoduje uaktywnienie tego menu, którego nazwa zaczyna się na podanš literę. Przy takiej konwencji litera nie musi być zawsze pierwszš literš nazwy opcji. Może to być także litera wyróżniona w nazwie przez podkreœlenie lub wyœwietlenie np. w innym kolorze. I tak: [Alt]-[F]menu File; [Alt)-[C]menu Compile; [Alt)-[W]menu Window itd., itd.. Kombinacja [Alt]-[R) wybiera więc menu RUN (uruchomienie programu). Menu Run daje Ci do wyboru następujšce polecenia: 21 str 22 RUN - Uruchomienie programu. PROGRAM RESET - Wyzerowanie zmiennych programu. GO TO CURSOR - Wykonanie programu do miejsca wskazanego kursorem w tekœcie. TRACE INTO - Uruchom œledzenie programu. STEP OVER - Sledzenie programu z możliwoœciš pominięcia funkcji. ARGUMENTS - Uruchom program z zadanymi ar- gumentami. Wybierz "RUN". Jeœli nie zrobiłeœ żadnego błędu, program powinien się skompilować z komentarzem "Success" i wykonać (kompilacja zakończona sukcesem; napis mignie tak szybko, że możesz tego nie zauważyć). Jeœli chcesz spokojnie obejrzeć wyniki działania swojego programu powinieneœ wykonać następujšce czynnoœci: 1. Rozwiń menu Window naciskajšc klawisze [Alt]-[W]. 2. Wybierz z menu rozkaz User screen (ekran użytkownika). Możesz wykonać to samo bez rozwijania menu naciskajšc kombinację klawiszy [Alt]-[F5). 3. Po przejrzeniu wydruku naciœnij [Enter]. Wrócisz do okna edytora. Jeœli zrobiłeœ błędy - kompilacja się nie uda i program nie zostanie wykonany, w okienku natomiast pojawi się napis "Errors" (czyli "Błędy"). Jeœli tak się stało naciœnij [Enter] dwukrotnie. Popraw ewentualne niezgodnoœci i spróbuj jeszcze raz. Z rachunku prawdopodobieństwa i z kilkuletniego doœwiadczenia autora wynika, że błędów zwykle bywa nie więcej niż dwa. Najczęœciej jest to brak lub przekłamanie którejœ litery (w słowie main lub printf) i brak œrednika na końcu linii. Jeœli sens i znaczenie napisów: Undfefined symbol ... in module (niezdefiniowany symbol ... w programie) Character constant too long (stała znakowa zbyt długa - tu: brak cudzysłowu na końcu tekstu) Unterminated string or character constant (brak zakończenia łańcucha znaków lub stałej znakowej) Function call missing ) (nie zamknięty nawias przy wywołaniu funkcji) Statement missing ; (pominięty œrednik) okaże się zrozumiały, uruchomienie pierwszych programów powinno iœć Ci sprawnie. CZEGO ON JESZCZE CHCE? Nawet po usunięciu wszystkich błędów Turbo C++ nie "uspokoi się" całkiem i będzie wyœwietlał cišgle komunikat ostrzegawczy: * w OKIENKU KOMPILACJI 22 str 23 Errors: 0 ( Błędy 0) Warnings: 1 (Ostrzeżenia:1 ) * W OKIENKU KOMUNIKATÓW - (Messages - tym w dolnej częœci ekranu): WARNING A:\PIERWSZY.C4: Function should return a value in function main (Uwaga: Funkcja main powinna zwrócić wartoœć.) Na razie zadowolimy się spostrzeżeniem, że: * Błędy UNIEMOŻLIWIAJĽ KOMPILACJĘ i powodujš komunikat ERRORS. * Ostrzeżenia NIE WSTRZYMUJĽ KOMPILACJI i powodujš komunikat WAR- NINGS. Jaki jest sens powyższego ostrzeżenia i jak go uniknšć dowiesz się z następnych lekcji. GDYBY TO NIE BYŁ C... Gdyby to nie był C a Pascal lub Basic, program wypisujšcy na ekranie tekst wyglšdałby mniej więcej tak: TT C PASCAL # include < stdio.h > uses Crt; main() /* poczštek */ program AHOJ; {poczštek} begin printf("Autor"); write('Autor'); printf("TO JA"); write('TO JA'); printf("ahoj"); write('ahoj'); end. a w BASICU: 10 PRINT"Autor":REM poczštek 20 PRINT"TO JA" 30 PRINT"ahoj" 40 END UWAGA: Zwróć uwagę, że działanie funkcji: print (Basic), printf (C), write i writeln (Pascal) nie jest identyczne, a TYLKO PODOBNE. JAK STĽD WYJŒĆ? Pozostaje nam w ramach tej lekcji: * Zapisać Twój pierwszy program na dysku i * Wyjœć z IDE Turbo C+ + . Aby zapisać plik PIERWSZY.C z Twoim programem na dysk należy wykonać następujšce czynnoœci: 1. Naciœnij klawisz [F10]. W głównym menu pojawi się pasek wyróżnienia sygnalizujšc, że menu stało się aktywne. 2. Naciœnij klawisz [F]. 23 str 24 Pasek wyróżnienia przesunie się podœwietlajšc menu File (operacje na plikach). Rozwinie się menu File. 3. Naciœnij klawisz [S] - wybierz polecenie Save. Tekst Twojego programu został zapisany w katalogu głównym dyskietki A: pod nazwš A:\PIERWSZY.C. Teraz możemy wyjœć z Turbo C + + . Aby to zrobić, wykonaj następujšce czynnoœci: I. Naciœnij klawisz [F10]. Uaktywni się główne menu. 2. Rozwiń menu File naciskajšc klawisz [F]. 3. Wybierz z menu polecenie "Quit" i naciœnij [Enter] SAVE szybciej. Zwróć uwagę, że zamiast rozwijać kolejne menu, możesz korzystać z kombinacji klawiszy, które pozwalajš Ci wydać rozkaz bez rozwijania menu. Takie kombinacje klawiszy (ang. hot keys lub shortcut keys) znajdziesz w menu obok rozkazu, np.: [Alt]-[X] - QUIT [F2] - SAVE [F3] - OPEN [Alt]-[F5] - User screen (Podglšdanie działania programu) itp. Z pewnoœciš nasunęło Ci się kilka wštpliwoœci i pytań. Przed nami jeszcze 14 lekcji, w trakcie których spróbuję odpowiedzieć na te i następne pytania, a na razie spróbuj: 2.1. Napisać i uruchomić kilka własnych programów wypisujšcych różne napisy. W swoich programach zastosuj funkcję printf według następujšcego wzoru: printf("....tu wpisz napis do wydrukowania..."); zastosuj znaki nowego wiersza według wzoru: printf("...napis...\n"); porównaj działanie. Swoim programom staraj się nadawać łatwe do rozpoznania nazwy typu PIERWSZY, DRUGI, ADAM1, PRZYKLAD itp. NIE CHCE DZIAŁAĆ? Pamiętaj, że dla języka C (w przeciwieństwie np. do Basica) PRINTF i printf to nie to samo! Słowa kluczowe i nazwy standardowych funkcji MUSZĽ BYĆ PISANE MAŁYMI LITERAMI!!! 24 str 25 GDZIE MOJE PROGRAMY? BšdŸ spokojny. Zapisz wersje Ÿródłowe programów na dyskietkę (dysk). Swoje programy skompilowane do wykonywalnej wersji *.EXE znajdziesz w katalogu głównym tego dysku, na którym zainstalowany został Turbo C + + . Jeœli ich tam nie ma, zachowaj zimnš krew i przeczytaj uważnie kilka następnych stron. PAMIĘTAJ: Jeœli masz oryginalny tekst programu, nazywany WERSJĽ RÓDŁOWĽ PRO- GRAMU, to zawsze możesz uzyskać ten program w wersji "roboczej", tzn. skompilować go na plik wykonywalny typu *.EXE (ang. EXEcutable - wykonywalny). printf PRINTing Function - Funkcja DRUKujšca na ekranie (dokładniej - na standardowym urzšdzeniu wyjœcia). Od- powiednik PRINT w Basicu lub write w Pascalu. A JEŒLI NIE MA TURBO C + + ??? W przeciwieństwie do INTER- PRETERÓW GWBasic, czy QBasic, które muszš być obecne, by program zadziałał, KOMPILATORY tworzš wersje wykonywalne pro- gramów, które mogš pracować niezależnie. W katalogu głównym tego dysku, na którym jest zainstalowany Turbo C+ + znajdziesz swoje programy PIERWSZY.EXE, DRUGI.EXE itp. Aby te programy uru- chomić nie musisz uruchamiać Turbo C + + . Wystarczy: l. Przejœć na odpowiedni dysk przy pomocy polecenia: D: (E: lub F:) 2. Przejœć do katalogu głównego: CD \ 3. Wydać polecenie: PI ERWSZY[ Enter] UWAGA: Jeœli nie jesteœ jedynym użytkownikiem Turbo C + + i na tym samym komputerze pracuje jeszcze ktoœ inny, sprawdŸ, czy inny użytkownik nie ustawił inaczej katalogu wyjœciowego. Katalog wyjœciowy (ang. output directory) to ten katalog, w którym Turbo C + + zapisuje pliki *.EXE po wykonaniu kompilacji. Jeœli jesteœ skazany na własne siły - patrz LEKCJA 3. 25 str 26 Lekcja 3 Jeszcze o IDE Turbo C~- -~- w rraKcie te~ lekcji: 1. Dowiesz się więcej o menu i okienkach w œrodowisku IDE. 2. Napiszesz i uruchomisz swój drugi program. Najprawdopodobniej program TC.EXE (bo to ten właœnie program uruchamiasz) nie jest pierwszym programem typu MENU DRIVEN (sterowanym przy pomocy MENU), z jakim się zetknšłeœ. Działanie całego systemu menu w Turbo C i w Turbo Pascalujest niemal identyczne. Bez względu jednak na Twojš dotychczasowš znajomoœć tych zagadnień niektóre elementy systemu menu Turbo C + + warte sš dokładniejszego omówienia. W górnej częœci ekranu roboczego wyœwietlany jest stale pasek głównego menu (choć nie stale menu to jest aktywne). Korzystałeœ już z tego menu w trakcie poprzedniej lekcji. W dolnej częœci ekranu jest podobny pasek, niemniej ważny, choć o trochę innym przeznaczeniu. Pasek ten jest to tzw. WIERSZ STATUSOWY (ang. Status Line). Jak wynika z nazwy w tym wierszu wyœwietlane sš informacje dotyczšce bieżšcego stanu (i bieżšcych możliwoœci) œrodowiska IDE. Napisy zawarte w tym wierszu majš dla użytkownika charakter informacyjny. Parafrazujšc znane powiedzenie o rysunkach w podręczniku zaryzykuję tezę, że często jeden prosty, własny eksperyment może być więcej wart niż wiele stron opisów. Jest to zresztš myœl przewodnia, o którš oparłem całš koncepcję tej ksišżki. Poeksperymentujmy zatem chwilę z wierszem statusowym. Najpierw oczywiœcie musisz uruchomić Turbo C + + . Włóż do napędu A: dyskietkę dołšczonš do niniejszej ksišżki (zaraz przekonasz się do czego to jest potrzebne), wydaj znany Ci już rozkaz: TC i naciœnij [Enter]. NIE CHCE SIĘ URUCHOMIĆ`??? Jeœli przy starcie Turbo C+ + nastšpi komunikat: System Message Disk is not ready in drive A Retry Cancel (Komunikat systemu Turbo C + + : Dyskietka w napędzie A nie gotowa do odczytu; Jeszcze raz? Zrezygnować?) 26 str 27 to znaczy, że Turbo C+ + nie może odtworzyć ostatniego ekranu roboczego, ponieważ nie udostępniłeœ mu dyskietki z programami, nad którymi ostatnio pracowałeœ. Po uruchomieniu, ZANIM podejmiesz jakiekolwiek działanie, rzuć okiem na wiersz statusowy. W tym momencie wiersz tenjest bardzo podobny do tego, który znasz zapewne z programu Norton Commander. W wierszu statusowym wyjaœnione jest działanie klawiszy funkcyjnych F1, F2, itd. Powinien tam być napis: F1 Help F2 Save F3 Load AItF9 Compile F9 Make F10 Menu znaczy to: [F1] - Pomoc [F2] - Zapamiętanie bieżšcego pliku na dysku pod bieżšcš nazwš (nawet jeœli tš nazwš jest NONAMEO1.C, tzn. została nadana automatycznie i znaczy - o ironio - "BEZNAZWYO1.C") i w bieżšcym katalogu. [F3] - Załadowanie do okienka edycyjnego nowego pliku tekstowego (np. nowego programu). [Alt]-[F9] - Kompilacja w trybie "Compile". [F9] - Kompilacja w trybie "Make" (jednoczesnej kompilacji i konsolidacji). [F10] - Uaktywnienie głównego menu. JAK ZROBIĆ PORZĽDEK??? W trakcie uruchamiania program TC.EXE korzysta z plików zewnętrznych. Turbo C stara się być USER FRIENDLY (przyjazny wobec użytkownika) i odtworzyć taki stan ekranu, w jakim ostatnio przerwałeœ pracę, co nie zawsze jednak jest korzystne. W wierszu statusowym pojawiajš się napisy informujšce o tym (np. Loading Desktop File ładuję plik zawierajšcy konfigurację ostatniego ekranu roboczego...). Jeœli chcesz by na poczštku sesji z Turbo C ekran był "dziewiczo" czysty, powinieneœ: * zmienić nazwę pliku [D:]\TC\BIN\TCDEF.DSK na dowolnš innš, np. STARY.DSK lub STARYl.DSK, stosujšc polece- nie systemu DOS RENAME. [D:] oznacza odpowiedni dla Twojego komputera dysk. Turbo C wystartuje wtedy z czystym ekranem i utworzy nowy plik TCDEF.DSK. * Plików TCDEF nie należy usuwać! Kiedy nabierzesz trochę wprawy pliki te znacznie przyspieszš i ułatwiš Ci pracę z TC. Aby zamknšć zbędne okna możesz zastosować również rozkaz CLOSE (ang. Close - zamknij) z menu Window (okna). Zwróć uwagę, że polecenie Close odnosi się do bieżšcego okna wyróżnionego przy pomocy podwójnej ramki. Aby zamknšć bieżšce okno, powinieneœ: 1. Nacisnšć klawisze [Alt]-[W] Rozwinie się menu Windows. 2. Wybrać z menu rozkaz Close - [C]. Może pojawić się okienko z ostrzeżeniem: 27 str 28 WARNING: A:\PIERWSZY.C not saved. Save? (UWAGA: plik A:\PIERWSZY.C nie zapisany na dysku. Zapisać?). ZNIKNĽŁ PROGRAM??? W ten sposób Turbo C+ + chce Cię uchronić przed utratš programu, ale uważaj! Jeœli odpowiesz Yes - Tak ([Y] lub [Enter]), to nowa wersja programu zostanie nadpisana na starš! UWAGA: BšdŸ ostrożny podejmujšc decyzję o zapisie wersji programu na dysk. Okienko z ostrzeżeniem pojawi się za każdym razem przed zamknięciem okna edycyjnego z tekstem programu. Jeœli przy zamykaniu okna nie pojawi się ostrzeżenie, to znaczy, że program w tej wersji, którš widzisz na ekranie został już zapisany na dysk. A JEŒLI NIE CHCĘ ZAMYKAĆ OKIEN??? W porzšdku, nie musisz. W menu Window ([Alt]-[W]) masz do dyspozycji rozkaz Next (następne okno). Możesz go wybrać albo naciskajšc klawisz [N], albo przy pomocy klawiszy kursora. Każde z okien na Twoim roboczym ekranie ma nazwę - nagłówek - np. NONAMEOO.C, PIERWSZY.C, ale nie tylko. Pierwsze dziesięć okien ma również swoje numery - podane blisko prawego - górnego rogu okna w nawiasach kwadratowych - np. [1], [2] itd. Posługujšc się tym rozkazem możesz przechodzić od okna do okna nie zamykajšc żadnego z okien. Spróbuj! Jest jeszcze inny sposób przejœcia od okna do okna. Jeœli chcesz przejœć do okna o numerze np. [1], [2], [5] itp. powinieneœ nacisnšć kombinację klawiszy [Alt]-[1], [Alt]-[5) itp. Możesz korzystać z listy okien (Window List) lub klawisza funkcyjnego [F6]. ZAMYKANIE OKIEN. Możesz szybciej zamknšć okno naciskajšc kombinację klawiszy [Alt]-[F3]. ACTIVE WINDOW - AKTYWNE OKNO. Na ekranie może się znajdować jednoczeœnie wiele okien, ale w danym momencie tylko jedno z nich może być AKTYWNE. Aktywne okno, to to, w którym miga kursor i w którym aktualnie pracujesz. Aktywne okno jest dodatkowo wyróżnione podwójnš ramkš. Rozwiń teraz menu Options (opcje). Możesz to zrobić na wiele sposobów. Najszybciej chyba naciskajšc kombinację klawiszy [Alt]-[O). Rozwinęło się menu, udostępniajšc Ci następujšcš listę poleceń: FULL MENUs - Pełne Menu ("s" oznacza, że chodzi o "te" menu w liczbie mnogiej, a nie o pojedyncze menu). 28 str 29 COMPILER - Kompilator. MAKE... - dosł. "ZRÓB", dotyczy tworzenia "projektów" (zwróć uwagę na wielo- kropek [. . .]). DIRECTORIES... - KATALOGI (znów wielokropek!). ENVIRONMENT... - OTOCZENIE lub inaczej ŒRODOWISKO. SAVE - ZAPAMIĘTAJ (UWAGA: To jest zupełnie inne SAVE niż w menu File. Nie wolno Ci pomylić tych poleceń. Pomyłka grozi Ci utratš tekstu programu!). A teraz popatrz, proszę, na linię statusowš. Jeœli będziesz poruszać się po menu Option, podœwietlajšc kolejne rozkazy, to w wierszu statusowym będzie wyœwietlany krótki opis działania wskazanego rozkazu. I tak, powinieneœ zobaczyć kolejno następujšce napisy: FULL MENUS [Off/On] - Use or dont use full set of menu commands. (Stosuj lub nie stosuj pełnego zestawu rozkazów w menu - domyœlnie przyjmowane jest Off/Nie). COMPILER - Set compiler defaults for code generation, error messages and names. (Ustaw domyœlne parametry pracy kompilatora dotyczšce generowania kodu programu, komunikatów o błędach i nazw). MAKE... - Set condition for project-makes. (Ustawianie warunków do tworzenia projektu). DIRECTORIES... - Set path for compile, link and executable files. (Wybierz katalogi i ustaw œcieżki dostępu dla kompilacji, konsolidacji i WSKAŻ MIEJSCE - GDZIE ZAPISAĆ PLIK TYPU *.EXE po kompilacji. - podkreœlenie moje - A.M.). ENVIRONMENT... - Make environment wide settings (eg, mouse settings). (Ustawienie parametrów rozszerzonego otoczenia, np. parametrów pracy myszki). ZWRÓĆ UWAGĘ i na ten rozkaz. MOŻESZ KORZYSTAĆ Z MYSZKI. SAVE - Save all the settings you've made in the Options menu. (Powoduje zapamiętanie na dysku wszystkich zmian parametrów roboczych IDE, które ustawiłeœ, korzystajšc z rozkazów dostgpnych za poœrednictwem menu Options.). Ten rozkaz pozwala Ci ustawić konfigurację IDE "raz na zawsze". Spróbujmy praktycznie zastosować to czego dowiedzieliœmy się dzięki wierszowi statusowemu. Przygotujmy się do powtórzenia kompilacji programu PIERWSZY.C, Jeœli masz na ekranie rozwinięte menu Options (jeœli nie - popatrz wyżej, jak się do niego dobrać), wybierz z menu polecenie Directories... . 1. Wskaż w menu polecenie Directories i naciœnij [Enter]. Po poleceniu umieszczonyjest wielokropek. Znaczy to, że rozkaz nie zostanie wykonany, zanim komputer nie uzyska od Ciebie pewnych dodatkowych informacji. Wiesz już, że praktycznie oznacza to dla Ciebie koniecznoœć "wypełnienia" okienka dialogowego. Po wybraniu polecenia Directories ukazało się okienko dialogowe już "wstępnie wypeł- nione". Takie "wstępne wypełnienie" okienka daje Ci pewne dodatkowe informacje. 29 str 30 Wynika z niego mianowicie JAKIE PARAMETRY SĽ PRZYJMOWANE DOMYŒL- NIE (default). W okienku dialogowym masz trzy okienka tekstowe: * Include Directories (Katalog zawierajšcy pliki nagłówkowe, np. STDIO.H, CO- NIO.H, GRAPHICS.H itp. dołšczane do programów). * Library Directories (Katalog zawierajšcy gotowe biblioteki, zawarte w plikach typu *.LIB,). * Output Directory (Katalog wyjœciowy, w którym po kompilacji będš umieszczane Twoje programy w wersji *.EXE). Pierwsze dwa zostawimy w spokoju. 2. Naciœnij dwukrotnie klawisz [Tab). Kursor wskazuje teraz okienko tekstowe Output Directory. 3. Wpisz do okienka tekstowego Output Directory: A:\ znaczy to że od teraz po wykonaniu kompilacji i utworzeniu pliku wykonywalnego typu *.EXE, plik taki zostanie zapisany na dyskietce A: w jej katalogu głównym. 4. Naciœnij [Enter). Spróbuj teraz, znanš z poprzedniej lekcji metodš, wczytać do okienka edytora Twój pierwszy program. Musisz wykonać następujšce czynnoœci: 1. Włóż do napędu A: dyskietkę z programem PIERWSZY.C (jeœli jeszcze jej tam nie ma). 2. Rozwiń menu File, naciskajšc kombinację klawiszy [Alt)-[F]. 3. Wybierz z menu rozkaz Open, naciskajšc klawisz [O]. Pojawi się znane Ci okienko dialogowe. Zwróć uwagę na wiersz statusowy. Napis: Enter directory path and file mask znaczy: Wpisz œcieżkę dostępu do katalogu i "wzorzec" nazwy pliku. Użyte słowo "wzorzec" oznacza, że wolno Ci wpisać do okienka tekstowego także nazwy wieloznaczne, zawierajšce znaki "*" i "?", np.: '.C A:\???.C D:\TC\SOURCE\P'.* itp. (Spróbuj! zawsze możesz się wycofać lub zmienić zdanie, posługujšc się klawiszami [BackSpace], [Shift), [Tab] i [Esc].). Klawisz [Tab] umożliwia Ci skok od okienka do okienka "do przodu", a [Shift]-[Tab] - "do tyłu". Zgodnie z nazwš (ang. ESCape - uciekać), klawisz [Esc) pozwala Ci wycofać się z niewygodnych sytuacji - np. zamknšć okienko dialogowe lub zwinšć rozwinięte menu bez żadnej akcji. Jeœli wpiszesz wzorzec nazwy, to w okienku z listš zobaczysz wszystkie pliki wybrane z podanego dysku i z podanego katalogu według zadanego wzorca. Aby wybrać plik z listy należy klawiszem [Tab] przejœć do okienka z listš, klawiszami kursora wskazać potrzebny plik i nacisnšć [Enter]. str 31 4. Wpisz do okienka tekstowego A:PIERWSZY.C 5. Naciœnij [Enter]. FAST START - SZYBKI START. Jeœli chcesz by Turbo C+ + automatycznie wczytał Twój program do okienka edytora, to możesz zadać nazwę pliku z tekstem programu jako parametr w wierszu polecenia, uruchamiajšc Turbo C np. tak: TC A: PI ERWSZY Jeœli korzystasz z programu Norton Commander, to możesz dodać do pliku NC.EXT następujšcy wiersz: C: TC ! . ! wówczas wystarczy tylko wskazać odpowiedni plik typu *.C z tekstem programu i nacisnšć [Enter). Dokonaj w swoim programie następujšcych zmian: main() printf("\n"); printf("Autor: np. Antoni Kowalski\n"); printf("program PIERWSZY.C - wersja II"); getc h ( ) ; Dzięki dodaniu do tekstu programu funkcji getch(), program nie powinien już tak szybko mignšć na ekranie i zniknšć. Zatrzyma się teraz i zaczeka na przyciœnięcie klawisza. Funkcja getch(), działa podobnie do: 10 IF INKEYS= GOTO 10 w Basicu. Nazwa pochodzi od GET CHaracter (POBIERZ ZNak, z klawiatury). Skompiluj program PIERWSZY.C. Aby to zrobić, powinieneœ: 1. Rozwinšć menu Compile - [Alt)-[C). 2. Wybrać z menu rozkaz Compile - [C]. Ostrzeżenie WARNING na razie ignorujemy. Wykonaj kompilację programu powtórnie przy pomocy rozkazu Run z menu Run Naciœnij kolejno klawisze: [Alt]-[R), [R) lub [Alt]-[R], [Enter) Ten sam efekt uzyskasz naciskajšc kombinację klawiszy [Ctrl)-[F9]. 31 str 32 Uruchom program powtórnie naciskajšc kombinację klawiszy [Alt]-[R), [R]. Zwróć uwagę, że teraz kompilacja nastšpi znacznie szybciej. Tak naprawdę TC stwierdzi tylko, że od ostatniej kompilacji nie dokonano żadnych zmian w programie i odstšpi od zbędnej kompilacji. Takie właœnie znaczenie ma komunikat "Checking dependences" (spraw- dzam zależnoœci, który mignie w okienku kompilacji. Po korekcie programu napisy wyglšdajš znacznie przyzwoiciej, prawda? Po obejrzeniu napisów naciœnij [Enter). Możemy teraz wyjœć z programu TC.EXE. Rozwiń menu File naciskajšc klawisze [Alt]-[F] i wybierz z menu rozkaz Quit. Pojawi się okienko z ostrzeżeniem: WARNING: A:\PIERWSZY.C not saved. Save? (UWAGA: plik A:\PIERWSZY.Cnie zapisany na dysku. Zapisać?). W ten sposób Turbo C + + ZNOWU chce Cię uchronić przed utratš programu, ale uważaj! Jeœli odpowiesz Tak ([Y] lub (Enter)), to nowa wersja programu zostanie nadpisana na starš! Jeœli odpowiesz Nie [N], to na dysku pozostanie stara wersja programu a nowa zniknie. Po wyjœciu z Turbo C + + znajdziesz się wjego katalogu roboczym, lub w tym katalogu bieżšcym, z którego wydałeœ rozkaz TC Aby uruchomić swój program musisz zatem wydać następujšcy rozkaz: A:\PI ERWSZY. EXE lub krócej A:\PIERWSZY a jeœli chcesz się przekonać, czy Twój program jest tam, gdzie powinien być, możesz go zobaczyć. Napisz rozkaz DIR A:\ lub DIR A:\".EXE Żeby upewnić się całkowicie, że to właœnie ten program, zwróć uwagę na datę i czas utworzenia pliku. Jeœli masz prawidłowo ustawiony zegar w swoim komputerze, data powinna być dzisiejsza a czas - kilka minut temu. Jeœli coœ jest nie tak, to musisz przy pomocy rozkazów systemu DOS DATE - (ustawianie daty) i TI M E - (ustawianie czasu ) zrobić porzšdek w swoim systemie. O takich drobiazgach warto pamiętać. Pozwoli Ci to w przyszłoœci odróżnić nowsze i starsze wersje programów, uniknšć pomyłek i zaoszczę- dzić wiele pracy. A teraz zajrzyjmy do œrodka do pliku PIERWSZY.EXE. Jeœli korzystasz z programu 32 str 33 Norton Commander, to masz do dyspozycji opcje [F3] - View (przeglšdanie) i [F4] - Edit (edycja). Jeœli nie korzystasz z NC, musisz wydać następujšcy rozkaz: TYPE A:\PIERWSZY.EXE I C:\DOS\MORE lub C:\DOS\EDIT A:\PI ERWSZY.C Jak widzisz na ekranie, napisy zawarte w programie pozostały czytelne, ale to co widać dookoła nie wyglšda najlepiej. Na podstawie tego co widzisz, można (na razie ostrożnie) wysnuć wniosek, że ani Viewer (przeglšdarka), ani Edytor, które doskonale spisujš się przy obróbce plików tekstowych, nie nadajš się do analizy i obróbki programów w wersji *.EXE. Narzędziami, które będziemy musieli stosować, mogš być programy typu DEBUGGER, PROFILER, LINKER (konsolidator), kompilator i in.. Mam nadzieję, że czujesz się w œrodowisku IDE już trochę swobodniej, aby więc nie była to tylko klasyczna sztuka dla sztuki, bierzemy się za drugi program. DRUGI PROGRAM - WRESZCIE COŒ POŻYTECZNEGO. Aby rozpoczšć pracę nad drugim programem, powinieneœ wykonać następujšce czynnoœci : 1. Zrób porzšdek na ekranie. Zamknij rozkazem Close z menu Window zbędne okna (możesz posłużyć się kombinacjš [Alt]-[F3]). 2. Rozwiń menu File. 3. Wybierz z menu rozkaz Open... 4. Wpisz do okienka tekstowego: A:\DRUGI.C 5. Naciœnij [Enter]. 6. Wpisz do okienka edytora tekst programu: /* Program przykładowy: DRUGI.C */ ~include /'" zwróć uwagę, że tu NIE MA [;] ! "/ ~include < stdio.h > int main() /~ tu tez nie ma œrednika [;] ! '"/ float dzielna, dzielnik; float ulamek; clrscr(); printf("Zamieniam ulamki zwykle na dziesietne\n"); printf("Podaj licznik ulamka:"); scanf(""/of", &dzielna); /* pobiera liczbę z klawiatury `/ printf("Podaj mianownik ulamka:"); scanf(""/of", &dzielnik); 33 str 34 ułamek = dzielna / dzielnik; /* tu wykonuje sie dzielenie */ printf("\n "/of / "/of = "/of, dzielna, dzielnik, ułamek); printf("\n nacisnij dowolny klawisz...\n"); getch(); /* program czeka na nacisniecie klawisza. */ return 0; //To też jest komentarz UWAGA: * Komentarze ujęte w [/*.....*/] możesz pominšć. Komentarz jest przeznaczony dla człowieka. Kompilator ignoruje całkowicie komentarze i traktuje komentarzjak puste miejsce, a dokładniej - tak samo jak pojedynczš spację. Komentarz w Turbo C+ + może mieć dwie formy: /* Tekst komentarza */ // Tekst komentarza w drugim przypadku ogranicznikiem pola komentarza jest koniec wiersza. * Spacjami i TABami możesz operować dowolnie. Kompilator ignoruje także puste miejsca w tekœcie. Nie należy natomiast stosować spacji w obrębie słów kluczowych i identyfikatorów. 7. Skompiluj program [Alt]-[C], [M] lub [Enter]. (Kompilacja szybciej - [F9]). 8. Popraw ewentualne błędy. 9. Uruchom program rozkazem Run, naciskajšc [Alt]-[R], [R]. 10. Zapisz wersję Ÿródłowš programu DRUGI.C na dyskietkę A: stosujšc tym razem SHORTCUT KEY - klawisz [F2]. * scanf - SCANing Function - Funkcja SKANujšca. Funkcja pobiera ze standardowego urzšdzenia wejœcia- zwykle z klawiatury podanš przez użytkownika liczbę lub inny cišg znaków. Działa podobnie do funkcji INPUT w Basicu, czy readln w Pascalu. * áoat - do Floating Point - "Pływajšcy" - zmienny przecinek. Słowo kluczowe służšce do tzw. DEKLARACJI TYPU ZMIENNEJ lub funkcji. Oznacza liczbę rzeczywistš np.: 3,14. * INT - od Integer - całkowity. Słowo kluczowe służšce do deklaracji typu zmiennej lub funkcji. Oznacza liczbę całkowitš np.: 768. * ~ include - Włšcz. Dyrektywa włšczajšca cały zewnętrzny plik tekstowy. W tym przypad- ku włšczony został tzw. plik nagłówkowy CONIO.H. * conio - CONsole Input/Output. Plik nagłówkowy zawierajšcy tzw. Prototypy funkcji potrzebnych do obsługi standardowego Wejœcia/Wyjœcia na/z konsoli. Plik zawiera między innymi prototyp funkcji clrscr(), potrzebnej nam do czyszczenia ekranu. * return - Powrót, zwrot. str 35 Po wykonaniu programu liczba 0 (tak kazaliœmy programowi roz- kazem return 0;) jest zwracana do systemu operacyjnego, w naszym przypadku do DOSa. Zwróć uwagę, że nie pojawiło się tym razem ostrzeżenie WARNING podczas kompilacji. Jak widzisz, pojawiło się trochę nowych elementów. Nie uda się więc uniknšć odrobiny nudnej teorii, ale to w trakcie następnych lekcji. Teraz wykonaj z programem DRUGI.C kilka eksperymentów. 3.1. Zamień operator dzielenia na operator mnożenia [*]: ulamek=dzielna*dzielnik; /* tu wykonuje sie mnożenie */ i napis w pierwszej funkcji printf na np. taki: printf( Wykonuję mnożenie liczb ); (Uwaga: ę i ż brak) Uruchom program. SprawdŸ poprawnoœć działania programu w szero- kim zakresie liczb. Przy jakiej wielkoœci liczb pojawiajš się błędy? 3.2. Zmień nazwy zmiennych ulamek, dzielna, dzielnik na inne, np.: to,jest liczba~pierwsza, to,jest liczba druga, itp. Czy Turbo C + + poprawnie rozpoznaje i rozróżnia takie długie nazwy? Kiedy zaczynajš się kłopoty? Czy można w nazwie zmiennej użyć spacji? PRZEPADŁ PROGRAM ??? Nie przejmuj się. Wersja poczštkowa programu DRUGI.C jest na dyskietce dołšczonej do niniejszej ksišżki. Zwróć uwagę, że Turbo C + + tworzy automatycznie kopie zapasowe plików Ÿródłowych z programami i nadaje im standardowe rozszerzenie *.BAK. Zanim zatem zaczniesz się denerwować, sprawdŸ, czy kopia np. DRUGI.BAK niejest właœnie tš wersjš programu, która Ci "przepadła". PROGRAM DO ZABAWY. Istniejš dwie szkoły nauki programowania: 1. Tworzenie coraz bardziej ambitnych własnych programów. 2. Analizowanie gotowych programów. Tym razem zastosujemy drugš metodę. Aby nie zanudzić Cię, szanowny Czytelniku, podaję Ci program do zabawy. Program ma doœć typowš dla języka C strukturę. Jego serce stanowi instrukcja for. Porównaj składnię z Pascalem i Basickiem. FOR I=1 TO 8*80 STEP 1: REM BASIC N EXT I for i=1 to 8*80 do {Pascal} 35 str 36 for (i =1; i = 8*80; i + + ) /* C */ Przyjrzyj się programikowi. Zmieniajac w tym programie wartoœci START i STOP w zakresie 32-255 możesz uzyskaE różne obrazki. include int i,j,START,5T0 P; void main() START=176; STO P =179; for (i=1; i=8'80; i++) for(j=START; j=STOP; j++) printf(%c,j); Miłej zabawy str 37 Lekcja 4 Z czego składa sig program W trakcie tej lekcji: * Dowiesz się co robić, jeœli tęsknisz za Pascalem. * Zapoznasz się wstępnie z preproeesorem języka C. * Poznasz dokladniej niektóre elementy języka C. Zanim zajmiemy sig niezbędnš, niestety, teoriš języka C, dla zilust- rowania mechanizmu działania dyrektyw w języku C popełnimy żart programistyczny. Nie ma nic gorszego niż spalić dobry żart, upewnijmy się więc najpierw, czy nasza "czarodziejska kula" jest gotowa do magicznych sztuczek. SprawdŸ, czy na dyskietce znajdujš się pliki A:\PASCAL.H A:\PO LTEKST. H Jeœli nie, to przed zabawš w magiczne sztuczki programistyczne musisz odtworzyć te pliki z zapasowej kop dyskietki, którš sporzšdziłeœ przed rozpoczęciem LEKCJI 1. Jeœli masz już oba pliki, wykonaj następujšce czynnoœci: 1. Włóż do napędu A: dyskietkę z plikami PASCAL.H i POLTEKST.H. 2. Uruchom Turbo C + -ł- rozkazem: TC UWAGA: Staraj się nie uruchamiać TC "siedzšc" na dyskietce! , TWORZYMY PROGRAM HOKUS.C Po uruchomieniu TC wykonaj następujšce czynnoœci: 1. Zrób porzšdek na ekranie - pozamykaj zbędne okna. 2. Naciœnij klawisz [F3). Pojawi się znajome okienko dialogowe "Open". 3. Wpisz do okienka tekstowego nazwę nowego programu: A:HOKUS.C i naciœnij [Enter]. 4. Wpisz następujšcy tekst programu: 37 str 38 ~ include < a:\pascal.h > program begin write("Ten program jest podobny"); write("do PASCALA"); write("tak tez mozna pisac w Turbo C++ I"); readln; end. 5. Uruchom program [Ctrl)-[F9]. Jeœli wystšpiš błędy, skoryguj ewentualne niezgodnoœci z oryginałem. Ostrzeżenie WARNING możesz zignorować. UWAGA: MUSI ZOSTAĆ ZACHOWANA IDEALNA ZGODNOŒĆ z tekstem [P-4]! 6. Uruchom program rozkazem Run [Alt]-[R], [Enter]. Zwróć uwagę, że powtórna kompilacja przebiega szybciej, jeœli w międzyezasie nie dokonałeœ zmian w programie. 7. Zamknij okno edytora rozkazem Close (z menu Window). Zapisz program HOKUS.C w wersji Ÿródłowej na dyskietkę A:. A teraz następna sztuczka, na którš pozwala Turbo C+ + . Utworzymy następny program POKUS.C. 1. Wykonaj czynnoœci z pp. 1 i 2 z przykładu [P-4]. 2. Otwórz okienko Open klawiszem [F3] i wpisz nazwę programu A:POKUS.C 3. Naciœnij [Enter]. 4. Wpisz tekst programu: ~ include < a:\poltekst.h > program poczatek czysty ekran drukuj ("Ten program - POKUS.C"); drukuj ("Jest napisany po polsku"); drukuj ("a mimo to Turbo C++ go rozumiel"); czekaj koniec 5. Uruchom program [Alt]-[R], [R]. Jeœli wystšpiš błędy, skoryguj ewentualne niezgod- noœci z oryginałem. Ostrzeżenie "WARNING" możesz zignorować. UWAGA: MUSI ZOSTAĆ ZACHOWANA IDEALNA ZGODNOŒĆ! 6. Zamknij okno edytora rozkazem Close (z menu Window). Zapisz program HOKUS.C w wersji Ÿródłowej na dyskietkę A:. str 39 PREPROCESOR JĘZYKA C PO RAZ PIERWSZY. A teraz wyjaœnienie działania sztuczek. Jeœli jesteœ niecierpliwy, na pewno już sam zajrzałeœ do plików PASCAL.H i POLTEKST.H, bojest chyba oczywiste od poczštku, że to tam właœnie musi ukrywać się to wszystko, co pozwala nam robić nasze hokus-pokus. Skorzystaliœmy z pewnej nie występujšcej ani w Pascalu, ani w Basicu umiejętnoœci języka C - a mianowicie z PREPROCESORA języka C. Najczęœciej stosowanymi dyrektywami preprocesora sš: # include - włšcz # define - zdefiniuj Do rozpoznania dyrektyw preprocesora służy znak (~ ) - HASH. Działanie preprocesora (czyli wstępne przetwarzanie tekstu programu jeszcze przed przystšpieniem do kompilacji) polega na zastšpieniu w tekœcie programu jednych łańcuchów znaków przez inne. Takie pary możemy "zadać" preprocesorowi właœnie dyrektywš ~ define. Nasze nagłówki wyglšdajš następujšco: PASCAL. H : # include < stdio.h > # define program main() # define begin { # define writeln printf # define readln getch() # define end. } POLTEKST. H : # include # define program main() # define poczatek { # define koniec } # define czysty ekran clrscr(); # define drukuj printf # define czekaj getch(); Zwróć uwagę, że warunkiem poprawnego zadziałania preprocesora jest zrezygnowanie ze spacji wewnštrz łańcuchów znakowych, spacje bowiem w preprocesorze rozdzielajš dwa łańcuchy znaków - np. "drukuj" - ten ZA KTÓRY CHCEMY COŒ POD- STAWIĆ oraz np. "printf" - ten, KTÓRY NALEŻY PODSTAWIAĆ. Często w programach zauważysz łańcuchy znaków pisane w doœć specjalny sposób: napisy w-których unika się spacji. NIEKTÓRE ELEMENTY JĘZYKA C - TROCHĘ TEORII. Uogólniajšc, program w języku C + + składa się z następujšcych elementów: 39 str 40 I. Dyrektyw preprocesora. Przykład: ~ define drukuj printf Działanie: W tekœcie programu PONIŻEJ niniejszej dyrektywy zastšp wszystkie łańcuchy znaków "drukuj" łańcuchami znaków "printf'. ~ include < D:\KATALOG\nazwa.roz> Działanie: W to miejsce pliku wstaw zawartoœć pliku tekstowego NAZWA.ROZ z katalogu KATALOG na dysku D:. II. Komentarzy. Przykład: // Tu obliczamy sumę lub /"To jest komentarz"/ III . Deklaracji . Przykład: KAŻDY PROGRAM musi zawierać deklarację funkcji main (ang. main - główna). Funkcja ta często jest bezparametrowa, co można zaakcentować wpisujšc w nawiasy słowo kluczowe void: main(void) ~ lub piszšc puste nawiasy: main() VOID. void - pusty, wolny, nieokreœlony. avoid - unikać. main - główny, główna. return - powrót, zwrot. exit - wyjœcie. Po nazwie funkcji main() NIE NALEŻY stawiać œrednika (;). Od funkcji main rozpoczyna się wykonanie programu. Przy pomocy tej funkcji program kontaktuje się z systemem operacyjnym. Parametry funkcji main, to te same parametry z którymi uruchamiamy nasz program w systemie DOS. Np. rozkaz FO R MAT A: oznacza, że do programu przekazujemy parametr A:. Ponieważ w każdym programie oprócz nagłówka funkcji: main (void) podajemy również tzw. ciało funkcji, np.: printf("wydrukuj cokolwiek") to jest to jednoczeœnie DEFINICJA FUNKCJI main. Zwróć uwagę, że funkcja PRINTF nie jest w powyższym przykładzie w żaden sposób ani deklarowana ani definiowana. Linia: printf("pisz!"); stanowi WYWOŁANIE funkcji printf z parametrem 'pisz!'. Jako łańcuchem znaków, który należy wydrukować. str 41 awetjeœli nawiasy sš puste - muszš być obecne. Poprawne wywołanie funkcji wjęzyku + + może mieć następujšcš formę: ~zwa fu n kcj i ( ) ; łzwa funkcji(par1, par2, par3, .....); nienna=nazwa funkcji(parl, par2, ...); ~!w Funkcja w momencie jej wywołania uzyskuje przekazane jej parametry. S~~ to tzw. ~~RGUMENTY FUNKCJI. Język C operuje wyłšcznie pojęciem FUNKCJI. W C nie ma podziahz na FUNKCJE i PROCEDURY. Każda funkcja może być w programie wywoływana wielokrotnie. Każde wywołanie funkcji może następować z innymi argumentami. Funkcja może w wyniku swojego działania zmieniać wartoœć jakiejœ zmiennej występujšcej w programie. Mówimy wtedy, żefunkcja ZWRACA wartoœć do programu. Funkcja mainjest funkcjš szczególnš, która zwraca wartoœć do systemu operacyjnego, w którym pracuje program. Zapis: int main() lub int main() return 5; exit(5); oznacza: 1. Funkcja main jest bezparametrowa (nie przyjmuje żadnych argumentów z ze- wnštrz) . 2. Funkcja main zwraca jako wynik swojego działania liczbę całkowiiš typu int (ang. INTeger - całkowita). 3. Funkcja zwróci do systemu DOS wartoœć 5. IV. Instrukcji. Przykład: i++; Działanie: Dokonaj inkrementacji zmiennej i, tzn. wykonaj operację i: = i + 1 Ponieważ nasz kurs języka C+ + rozpoczęliœmy od programu z funkcjš printf() i zapewne będzie nam ona towarzyszyć do ostatniego programu, pora poœwięcić iej trochę uwagi . FUNKCJA printf(). Jest to funkcja FORMATOWANEGO wyjœcia na standardowe urzšdzenie wyjœcia (ang. stdout - STandarD OUTput). Definicja - œciœlej tzw. PROTOTYP tej funkcji znajduje się w pliku nagłówkowym STDIO.H. Wniosek praktyczny: Każdy program korzystajšcy z funkcji printf() powinien zawierać dyrektywę preprocesora: ~ include zanim nastšpi wywołanie funkcji printf(). 41 str 42 A JEŒLI ZAPOMNIAŁEM O STDIO.H '?'?'? Nie przejmuj się. W trak- eie kompilacji i konsolidacji Turbo C++ naprawi Twoj błšd. Na obecnym etapie nauki wolno Ci jeszcze popełniać od czasu do czasu takie błędy. Niemniej jednak pamiętanie o takich "drobiazgach" jest "w dobrym stylu" i wyrabia dobre nawyki na przyszłoœć. Jeœli zapomnisz dołšczyć odpowiedni plik nagłciwkowy może pojawić się komunikat: Error: Function printf() should have a prototype in function main (Funkcja printf() powinna mieć prototyp) Więcej o zawartoœci i znaczeniu plików nagłówkowych *.h dowiesz się z następnych lekcji. Na razie postaraj się pomiętać o dołšczeniu wskazanego w przykładzie pliku. Funkeja printf() zwraca wartoœć całkowitš typu int: * liczbę bajtów przesłanych na standardowe urzšdzenie wyjœcia; * w przypadku wystšpienia błędu - kod znaku EOF. EOF - End Of File - znak końca pliku. EOL - End Of Line - znak końca linii. Indicator - znak, wskażnik (nie mylić z pointerem !) SKĽD TO WIADOMO ? To, że nie musisz się zastanawiać ile to właœciwiejest EOF (zero '? czy -I `?) zawdzięczamy też dołšczanym plikom typu *.H, w których np. przy użyciu dyrektywy ~ define zostały PREDEFINIOWANE (zdeftniowane wstępnie) niektóre stałe. Jeœlijesteœ bardzo dociekliwy, zajrzyj do wnętrza pliku STDIO.H (view, edit, type). Znajdziesz tam między innymi: ~ define EOF (-1 ) //End of file indicator Składnia (ang. syntax): int printf(eonst char *format [arg1, arg2`.....)); lub trochę proœciej (bo znak * może zostać pominięty): printf(format` arg1, arg2`.....argn); Liczba argumentów może być zmienna. Turbo C+ + oferuje Ci wiele funkcji o podobnym działaniu - np.: cprintf` fprintf` sprintf` vprintf` vsprintf` itp. Ponieważ FORMAT brzmi może trochę obco, nazwijmy go WZORCEM. Jak zapewne wiesz, wszystkie informacje przechowywane sš w pamięci komputera jako cišgi zer i jedynek. Jest to forma trochę niewygodna dla człowieka, więc zanim informacja trafi na ekran musi zostać zamieniona na postać dla Ciebie wygodniejszš - np. na cyfry dziesiętne, litery itp.. Taki proces nazywany jest KONWERSJĽ, a podany w funkcji printf FORMAT - WZORZEC to upraszczajšc, rozkaz dokonania takiej właœnie konwersii. Możesz więc zarzšdać przedstawienia liczby na ekranie w postaci np. SZESNASTKOWEJ lub DZIESIĘTNEJ - tak, jak Ci wygodniej. Wzorce konwersji w najprostszym przypadku majš postać o/os, o/ad, o/~f, itp.: 42 str 43 I tak: o/os - wyprowadŸ łańcuch znaków (s - String - łańcuch) Przykład : printf("o/os","jakis napis"); Przykład: printf("o/o39s","jakis napis"); spowoduje uzupełnienie napisu spacjami do zadanej długoœci 39 znaków (SprawdŸ!). Funkcja printf operuje tzw. POLEM WYJŒCIOWYM. Długoœć pola wyjœciowego możemy okreœlić przy pomocy liczb wpisanych pomiędzy znaki o/o oraz typ - np. s. Możemy także okreœlić iloœć cyfr przed i po przecinku. o/oc - wyprowadŸ pojedynczy znak (c - Character - znak) Przykład: printf("o/oc",'X'); o/od - wyprowadŸ liczbę całkowitš typu int w postaci dziesiętnej (d - Decimal - dziesiętny). Przykład: printf("o/od",1993); o/of - wyprowadŸ liczbę rzeczywistš typu float w postaci dziesiętnej (f - Floating point - zmienny przecinek). Przykład: printf("o/of", 3.1416); printf("o/of3.2", 3.14159); [UWAGA: SprawdŸ działanie.] o/oo - wyprowadŸ liczbę całkowitš typu int w postaci ósemkowej (o - Octal - ósemkowa). Przykład: printf("o/oo", 255); o/ox - wyprowadŸ liczbę całkowitš typu int w postaci szesnastkowej (x - heXadecimal - szesnastkowa). o/ox lub o/oX - cyfry szesnastkowe a,b,c,d,e,f lub A,B,C,D,E,F. o/old - liczba całkowita "długa" - long int. o/oLf - liczba rzeczywista poczwórnej precyzji typu long double float. o/oe - liczba w formacie wykładniczym typu 1.23e-OS (0.0000123) o/og - automatyczny wybór formatu o/of albo o/oe. 43 str 44 Po przytoczeniu przykładów uogólnijmy sposób zastosowania wzorca formatu: "/o[przełaczniki] [szer.~pola] [.precyzja) [rozmiar]Typ Przykład: printf(""/o2.3lf", X); Posługujšc się różnymi sposobami formatowania liczb możemy zażšdać wydrukowa- nia liczb w najwygodniejszej dla nas formie. W programie przykładowym [P-6) dokonujemy zamiany liczb dziesiętnych na szesnastkowe. // Program przykladowy lOnal6.C ~ include int liczba; int main() clrscr(); printf("Podaj liczbe dziesietna całkowita ? \n"); scanf(""/od", &liczba); printf("Szesnastkowo to wynosi:\n"); printf("%x",liczba); getch ( ) ; return 0; Ten program pozwoli Ci zamienić dziesiętne liczby całkowite na liczby szesnastkowe (Spróbuj!). Zakres dostępnych liczb wynika z zadeklarowanego typu int. Więcej na ten temat dowiesz się z następnych lekcji. Spróbujmy odwrotnie: // Program przykladowy lónal0.C int liczba; int main() clrscr(); printf("Podaj liczbe SZESNASTKOWA-np. AF - DUZE LITERY: \n"); scanf(""/oX", &liczba); printf(""/os",Dziesietnie to wynosi:"); printf("%d",liczba); str 45 getch ( ) ; return 0; Myœlę, że program l6NAl O.C można pozostawić bez dodatkowego komentarza. Zwróć uwagę, że funkcja scanf() "formatuje" dane wejœciowe bardzo podobnie do funkcji printf(). Pewnie dziwi C~ię trochę "dualny" zapis: liczba i &liczba. Zagadka zostanie niebawem wyjaœniona. W trakcie Lekcji 7 zajmiemy się dokładniej zmiennymi, i ich rozmieszczeniem w pamięci a na razie wracamy do funkcji printf(). Jako się rzekło wczeœniej - funkcja printfmoże mieć wiele argumentów. Pozwala nam to przy pomocy jednego wywołania funkcji wyprowadzać złożone napisy. Przykład: printf("lloczyn 3 "/oc 5 "/o8s "/od", , "wynosi" ,15); Działanie: "lloczyn 3~ - wyprowadŸ jako łańcuch znaków. "/oc - tu wyprowadŸ pojedynczy znak - '*'. 5~ - wyprowadŸ jako łańcuch znaków. "/o8s - wyprowadŸ łańcuch "wynosi " uzupełniajšc go z przodu spacjami do długoœci 8 znaków. "/od - wyprowadŸ 15 jako liczbę dziesiętnš. UWAGA: " " oznacza spację, spacja to też znak. Przykład : printf("lloczyn 3 "/oc 5 "/o9s %f", 'x', "wynosi", 3*5); Zwróć uwagę, że tym razem kazaliœmy komputerowi samemu policzyć ile wynosi nasz iloczyn, tzn. zastosowaliœmy jako argument funkcji printf nie stałš, a WYRAŻENIE. Działanie możesz przeœledzić przy pomocy programu przykładowego: // Program WYRAZ.C ~ include int main() clrscr(); printf("Skomplikowany napis:\n"); printf("lloczyn 3 "/oc 5 "/o8s %d", , "wyniosi",15); getch ( ) ; printf("\nWyrazenie jako argument:\n"); printf("lloczyn 3 "/oc 5 "/o9s "/od", 'x', "wynosi", 3*5); printf("\n\n\n"); printf("Przyjrzyj sie i nacisnij klawisz..."); 45 str 46 getch(); return 0; Ponieważ podobno nie od razu Kraków zbudowano, więc i my zapewne nie zgłębimy całej filozofii języka C+ + w eišgu tej lekcji. Wyjaœnijmy na razie jeszcze jedno "dziwactwo" - znaki sterujšce rozmieszczeniem napisów na ekranie. Dokładniej zajmiemy się tym zagadnieniem w trakcie Lekcji 5 a na razie poprzestańmy na stwierdzeniu, że: \n - oznacza przejœcie do nowego wiersza (LF - Line Feed); \t - tabulacja pozioma (HT - Horizontal Tab); \v - tabulacja pionowa (VT - Vertical Tab); \b - cofnięcie o jeden znak (BS - Back Space). Ponieważ wiesz już teraz znacznie więcej o funkcji printf(), spróbuj samodzielnie: 4.1. Napisać i uruchomić program wykonujšcy konwersję liczb ósem- kowych na dziesiętne i odwrotnie. 4.2. Przy pomocy pojedynczego wywołania funkcji printf() wydrukować kilka złożonych napisów typu: * suma 2 + 4 to 6 * działanie 5*7*27+6-873 daje wynik...( właœnie, ile?). 4.3. SprawdŸ działanie tabulacji pionowej \v. Ile to wierszy`? DYSKIETKA NIE JEST Z GUMY !!! Jeœli podczas kompilacji progcamów w okienku będzie się uporczywie, bez widocznego powodu pojawiał napis "Errors" - błędy, a w okienku komunikatów "Message" pojawi się napis: Fatal A:\PROGRAM.C: Error writing output file (Fatalny błšd podczas kompilacji pliku A:\PROGRAM.C: Błšd przy zapisie pliku wyjœciowego), to znak, że na dyskietce zabrakło miejsca. Pora zmienić katalog wyjœciowy Turbo C + + Aby to zrobić należy: I. Rozwinšć menu Option - [Alt]-[O]. 2. Wybrać rozkaz Directories... - [D]. 3. Przejœć do okienka "Output Directory" - 2 razy [Tab]. 4. Wpisać do okienka katalog z dysku stałego, np.: C:\ 5. Nacisnšć [Enter]. 6. Powtórzyć kompilację programu, przy której nastšpiło przepełnienie dyskietki. 7. Usunšć z dyskietki A: zbędne pliki *.EXE (TYLKO *.EXE !!!). str 47 Lekcja 5 Jakich słów kłuczowych używa Turbo C+ + W trakcie tej lekcji dowiesz się: * Jakie znaczenie majš slowa kluczowe języka C~- -~ . * Trochę więcej o literalach. * Jak zmusić komputer do wykonania pętli programowej. Każdy język musi operować tzw. słownikiem - zestawem słów zro- zumiałych w danym języku. Jak wiesz z doœwiadczenia, komputer jest pedantem i wymaga dodatkowo (my, ludzie, tego nie wymagamy), aby znaczenie słów było absolutnie jednoznaczne i precyzyjne. Aluzje, kalambury i zabawne niedomówienia sš na razie w dialogu z komputerem niedopuszczalne. Pamięci asocjatywne (oparte na skojarzeniach), sieci neuronowe (neural networks), tworzone bardzo często (o ironio!) przy pomocy C+ + systemy expertowe, systemy z tolerancjš błędów - np. OCR - systemy optycznego rozpoznawania pisma i inne pomysły informatyków rozpoczęły już proces "humanizowania" komputerowego myœlenia, ale to temat na oddzielnš ksišżkę. Na razie traktujemy nasz komputer jako automat cyfrowy pozbawiony całkowicie wyobraŸni i poczucia humoru, a język Turbo C + + , jako œrodek porozumiewania się z tym "ponurakiem". Podobnie do słów języka naturalnego (rzeczowników, czasowników) i słowa języka programowania można podzielić na kilka grup różnišcych się przeznaczeniem.. Takie niby - słowa czasem nazywa się również tokenami lub JEDNOSTKAMI LEKSYKAL- NYMI (leksykon - inaczej słownik). Słownik języka C składa się z: * Słów kluczowych * Identyfikatorów * Stałych liczbowych i znakowych * Stałych tekstowych (łańcuchów znaków - napisów) * Operatorów (umownych znaków operacji) * Znaków interpunkcyjnych * Odstgpów UWAGA: Zarówno pojedyncza spacja czy cišg spacji, tabulator poziomy, znak nowej lin, jak i komentarz dowolnej długoœci (!) sš traktowane przez kompilator jak pojedyncza spacja. Od zarania dziejów informatyki twórcy uniwersalnych języków programowania starali 47 str 48 się upodobnić słowa tychjęzyków do zrozumiałych dla człowieka słówjęzyka naturalnego - niestety - angielskiego (swojš drogš, może to i lepiej, że Turbo C+ + nie wymyœlili Japończycy...). Najważniejszš częœciš słownika sš tzw. SŁOWA KLUCZOWE (key- words). SŁOWA KLUCZOWE w Turbo C + + . Oto pełna lista słów kluczowych Turbo C + + v 1.0 z krótkim wyjaœnieniem ich znaczenia. Krótkie wyjaœnienie - jak to krótkie wyjaœnienie - pewnie nie wyjaœni wszystkiego od razu, ale na pewno pomoże Ci zrozumieć znaczenie słów klnczowych. asm - Pozwala wstawić kod w ASEMBLERZE bezpoœrednio do programu . Przykład 14: asm { pop ax; mov ds,ax; auto - zmienna lokalna. Przyjmowane domyœlnie. break - przerwij. case - w przypadku. cdecl - spec. konwencja nazewnictwa/przekazania parametrów zgodna ze standardem jęz. C. char - znak. class - klasa. const - stała, konstanta. continue - kontynuuj. default - przyjmij domyœlnie. delete - skasuj obiekt. do - wykonaj. double - podwójna (długoœć/precyzja). else - w przeciwnym wypadku. enum - wylicz kolejno. export - dotyczy tylko OS/2, ignorowany. extern - zewnętrzna. far - dalekie. WskaŸnik - podwójne słowo (w zakresie do 1 MB). float - zmiennoprzecinkowy, rzeczywisty. for - dla (wskazanie zmiennej roboczej w pętli). friend - zaprzyjaŸniona funkcja z dostępem do prywatnych i chronionych członków danej klasy. goto - skocz do (skok bezwarunkowy). huge - daleki, podobnie do far. if - jeżeli (pod warunkiem, że...). inline - definiuje często używane funkcje w ich klasie. 48 str 49 ~iat - całkowity. ~terrupt - przerwanie. loadds - podobne do huge, ustawia rejestr DS (Data Segment). long - długi. near - bliski, wskaŸnik o dł. 1 słowa. Obszar max. 64 K. new - nowy, utwórz nowy obiekt. operator - operator. pascal - deklar. funkcji zgodnej ze standardem przekazywania parametrów przyjętym w Pascalu. private - prywatna, wewnętrzna, niedostępna z zewnštrz. protected - chroniona. public - publiczna, dostępna z zewnštrz. register - zmiennš przechwaj nie w pamięci a w rejestrze CPU. return - powrót, zwrot wartoœci. saveregs - save registers, zachowaj zawartoœć rejestrów a następnie odtwórz rejestry przed powrotem. seg - segment. short - krótka (mała iloœć cyfr). signed - ze znakiem ( + /-). unsigned - bez znaku ( + /-). sizeof - podaj wielkoœć. static - statyczna. struct - struktura. switch - przełšcz. template - zastrzeżone na przyszłoœć. this - ten, przekazanie ukrytego argumentu (C + + ). typedef - definicja typu. union - unia. virtual - wirtualna, pozorna. void - nieokreœlona. volatile - ulotna. while - dopóki. Panuje mnienanie, że język C posługuje się stosunkowo skromnym zestawem słów kluczowych. To prawda, ale nie cała prawda o języku C. Zauważyłeœ zapewne, że nie ma tu: define, include, printf i innych znanych Ci już słów. To po prostu jeszcze nie cały słownik języka. Zdajšc sobie sprawę z nieprecyzyjnoœci tego porównania możesz przyjšć, że to coœ na kształt listy czasowników. A sš przecież jeszcze i inne słowa - o innej roli i przeznaczeniu. A GDZIE SIĘ PODZIAŁY REJESTRY ??? W opisie Borland Turbo C 2.0 w spisie słów kluczowych sš jeszcze nazwy rejestrów mikro- procesora Intel 80X86: AX AL AH SI CS BX BL BH SP DS CX CL CH BP ~ES _DX _DL DH DI SS FLAGS 49 str 50 Takie oznaczenia wynikajš z architektury konkretnej rodziny mikroprocesorów, nie mogš stanowić uniwersalnego standardu języka C. Podobnie inne zjawisko wynikajšce z grzechu pierworodnego mikroprocesorów 8086 przekazanego dziedzicznie na IBM PC/DOS - szesnastobitowej szyny, co spowodowało w efekcie: * segmentację pamięci po 64 K; * ograniczenie adresu do 16/20 bitów; * podziału adresu na SEGMENT i OFFSET; * ograniczenie standardowej pamięci do 640 K, tworzenie EMS i XMS. Efekt dostosowania Turbo C + + do IBM PC to np. odnoszšce się do tak widzianego modelu pamięci słowa kluczowe NEAR, FAR i HUGE. Wymóg zgodnoœci ze standardem ANSI C spowodował, że w Turbo C+ + nazwy rejestrów pozostajš nazwami o zastrzeżonym znaczeniu, ale nazywajš się PSEUDO- ZMIENNYMI REJESTROWYMI (ang.: Register Pseudovariables). PAMIĘTAJ ! Próba użycia słowa o zastrzeżonym znaczeniu w jakiej- kolwiek innej roli (np. jako nazwa Twojej zmiennej) może spowodować wadliwe działanie programu lub uniemożliwić kompilację. Unikaj przy- padkowego zastosowania słów o zastrzeżonym znaczeniu! A SKĽD MAM WIEDZIEC ? Listę nazw, które majšjuż nadane œciœle okreœlone znaczenie w Turbo C + + znajdziesz w Help. Dostęp do spisu uzyskasz przez: * Rozwinięcie menu Help [Alt)-[H]; * Wybranie z menu Help rozkazu Index (spis). Wrócić do edytora Turbo C+ + możesz przez [Esc). Powołujšc się na nasze wczeœniejsze porównanie (NIE TRAKTUJ GO ZBYT DOSŁOWNIE!),zajmiemy się teraz czymœ, co trochę przypomina rzeczowniki w normalnym języku. IDENTYFIKATORY Identyfikatorami (nazwami) mogš być słowa, a dokładniej cišgi liter, cyfr i znaków podkreœlenia rozpoczynajšce się od litery lub znaku podkreœlenia (). Za wyjštkiem słów kluczowych, które MUSZĽ ZAWSZE BYĆ PISANE MAŁYMI LITERAMI, można stosować i małe i duże litery. Litery duże i małe sš rozróżniane. Przykład: # include < stdio.h > float PI =3.14159; float r; 50 str 51 int main(void) clrscr(); ~~ printf("Podaj promien ?\n"); scanf("%f", &r) ; printf("\nPole wynosi P = "/of", PI"r'r ); getch ( ) ; return 0; * Użyte w programie słowa kluczowe: int, float, void, return. * Identyfikatory - nazwy funkcji (zastrzeżone): main, printf, scanf, getch, clrscr. - nazwy zmiennych (dowolne): PI, r. * Dyrektywy preprocesora: ~ include Zwróć uwagę, że w wierszu: float PI=3.14159; nie tylko DEKLARUJEMY, zmiennš PI jako zmiennoprzecinkowš, ale także od razu nadajemy liczbie PI jej wartoœć. Jest to tzw. ZAINICJOWANIE zmiennej. 5.1. Uruchom program [P-9]. Spróbuj zamienić identyfikator zmiennej PI na pisane małymi literami pi. Powinien wystšpić błšd. LITERAŁY Literałem nazywamy reprezentujšcy danš NAPIS, na podstawie którego można jednoznacznie zidentyfikować danš, jej typ, wartoœć i inne atrybuty. W języku C+ + literałami mogš być: * łańcuchy znaków - np. "Napis"; * pojedyncze znaki - np. 'X', '?'; * liczby - np. 255, 3.14 Uwaga: BARDZO WAŻNE !!! * Rolę przecinka dziesiętnego spełnia kropka. Zapis Pi = 3,14 jest nieprawidłowy. * Próba zastosowania przecinka w tej roli SPOWODUJE BŁĘDY ! Liczby całkowite mogš być: * Dziesiętne (przyjmowane domyœlnie - default); * Ósemkowe - zapisywane z zerem na poczštku: 017 = 1 *8 + 7 = 15 (dziesiętnie); 51 str 52 * Szesnastkowe - zapisywane z Ox na poczštku: Ox17 = 1 '16 + 7 = 23 (dziesiętnie); Ox100 = 162 + 0 + 0 = 256. Liczby rzeczywiste mogš zawierać częœć ułamkowš lub być zapisane w postaci wykładniczej (ang. scientifc format) z literš "e" poprzedzajšcš wykładnik potęgi. Przykład: Zapis liczby Wartoœć dziesiętna .0123 0.0123 123e4 123 * 10 4 = 1 230 000 1.23e3 1.23 * 10 3 = 1230 123e-4 0.0123 Literały składajšce się z pojedynczych znaków majš jednš z trzech postaci: * 'z' - gdzie z oznacza znak "we własnej osobie"; * '\n' - symboliczne oznaczenie znaku specjalnego - np. sterujšcego - tu: znak nowej lin; * '\13' - nr znaku w kodzie ASCII. UWAGA: '\24' - oznacza kod ÓSEMKOWY (czyli dziesiętnie 20) a '\x24' - kod SZESNASTKOWY ! (dziesiętnie 36) SLASH, BACKSLASH. Kreska "/" nazywa się SLASH (czyt. "slasz") - łamane, ukoœnik zwykły. Kreska "\"nazywa się BACKSLASH (czyt. "bekslasz") - ukoœnik odwrotny. Uzupełnimy teraz listę symboli znaków z poprzedniej lekcji Znak ÓSEMKOWO ASCII (10) ZNACZENIE \a '\7' 7 - sygn. dŸwiękowy BEL \n '\ 12' 10 - nowy wiersz LF \t '\11' 9 - tabulacja pozioma HT \v '\ 13' 11 - tabulacja pionowa VT \b '\10' 8 - cofnięcie kursora o I znak BS \r '\ 15' 13 - powrót do poczštku linii CR \f '\14' 12 - nowa strona (form feed) FF \\ '\134' 92 - poprostu znak backslash "\" \' '\47' 39 - apostrof ""' \" '\42' 34 - cudzysłów (") \0 '\0' 0 - NUL (znak pusty) Komputer przechowuje znak w swojej pamięci jako "krótkš", bo zajmujšcš tylko jeden bajt liczbę całkowitš (kod ASCII znaku). Na tych liczbach wolno Ci wykonywać operacje arytmetyczne! (Od czego mamy komputer?) Przekonaj się o tym uruchamiajšc na- stępujšcy program. 52 str 53 ~ include //prototypy printf() i scanf() ~ include //prototypy clrscr() i getch() short int liczba; int main (void) clrscr(); printf("Wydrukuje A jako \nLiteral znakowy:\tKod ASCII:\n"); printf(""/oc", 'A'); printf("\t\t\t\t"/od", 'A'); printf("\npodaj mi liczbe ? "); scanf(""/od", &liczba); printf("\n"/oc\t\t\t\t"/od\n",'Ž'+liczba, 'A'+Iiczba); scanf(""/od", &liczba) ; pi intf("\n"/oc\t\t\t\t"/od", 'A' + liczba, 'A' + liczba) ; getch(); return 0; Uruchom program kilkakrotnie podajšc różne liczby całkowite z zakresu od 1 do 100. Zwróć uwagę na deklarację zmiennej liczba: short int liczba; (krótka całkowita). Przyjrzyj się sposobowi formatowania wyjœcia: %c, "/od, \t, \n Jeœli pamiętasz, że kody ASCII kolejnych liter A,B,C... i kolejnych cyfr 1, 2, 3 sš kolejnymi liczbami, to zauważ, że wyrażenia: '5' + 1 = '6' oraz 'A' + 2 = 'C' (czytaj: kod ASCII "5" + 1 = kod ASCII "6") sš poprawne. Dla relaksu spróbujemy namówić naszego "ponuraka", żeby wydał dŸwięk (niech szef i koledzy słyszš, że pracujesz). Wpisz i uruchom program (zapisywać na dysk chyba nie warto). ~ include ~ include // Tam jest prototyp funkcji clrscr() // Program BZZZ.C int main (void) printf("\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a") ; getch(); printf("\7\7\7\7\7\7\7\7\7\7\7\7\7\7\7\7\7\7\7\7' return 0; Abyœ piszšc programy w C+ + nie zapominał, że cały czas "siedzisz" w œrodowisku DOSa, a TC (czasami w sposób niezauważalny) często korzysta z przerwań i funkcji DOSa i BIOSa, wykonaj prosty eksperyment. WyjdŸ z TC - [Alt]-[X] i napisz ten sam program jako plik wsadowy *.BAT. Wykonaj następujšce czynnoœci: 53 str 54 1. Utwórz plik BZZZ.BAT. COPY_ CON BZZZ.BAT[E_nter] G G G G G ~ G G G G G G G G G G G G G Z[Enter] System DOS odpowie Ci: 1 file(s) copied (skopiowałem 1 plik(i)) Napis G G G G G G G G G najłatwiej uzyskać naciskajšc [Ctrl]-[G] i trzymajšc aż do skutku. Komputer wykona "automatycznš repetycję". 2. Uruchom program rozkazem BZZZ[Enter] Przypomniałem Ci o obecnoœci DOS'a "w tle" nie bez powodu. Za chwilę, przy próbach zapętlania okaże się to bardzo istotne. Twoja intuicja programisty z pewnoœciš podpowiada Ci, że gdyby zmusić komputer do pracy w pętli, to nie musiałbyœ przykładowych programów uruchamiać wielokrotnie. Spróbujmy nakazać programowi [P-10] chodzić "w kółko". To proste - dodamy do programu: * na końcu rozkaz skoku bezwarunkowego GOTO (idŸ do...), * a żeby wiedział dokšd ma chodzić - na poczštku programu zaznaczymy miejsce przy pomocy umownego znaku - ETYKIETY. Zwróć uwagę, że piszšc pliki wsadowe typu *.BAT w języku BPL (Batch Programming Language - język programowania wsadowego) stawiasz dwukropek zawsze na poczštku etykiety: :ETYKIETA (BPL) a w języku C zawsze na końcu etykiety: ETYKIETA: (C/C++) Od tej zasady nie ma wyjštków w C. Przystępujemy do opracowania programu. 1. Rozwiń menu File [F10], [F]. 2. Wybierz z menu polecenie Open [O]. (jeœli wolisz, możesz uzyskać ten sam efekt naciskajšc [F3]) 3. Wpisz do okienka tekstowego nazwę programu: A:\GOTOTEST.C 4. Naciœnij [Enter]. Otwarło się okno edytora zatytułowane A:\GOTOTEST.C 5. Wpisz program [P-10] w nowej wersji: ~ include short int liczba; int main(void) 54 str 55 clrscr(); printf("Wydrukuje A jako \nLiteral znakowy:\tKod ASCII:\n") printf("o/oc", 'A'); printf("\t\t\t\to/od", 'A'); etykieta: printf("\npodaj mi liczbe ? "); scanf("o/od", &liczba); printf("\no/oc\t\t\t\to/od\n",'A'+liczba, 'A'+liczba); goto etykieta; return 0; 6. Skompiluj program do wersji *.EXE: Compile ~ Make (rozkazem Make EXE file z menu Compile). Musisz nacisnšć następujšce klawisze: [Alt]-[C], [M]. (lub [F9]) * Jeœli wystšpiły błędy, popraw i powtórz próbę kompilacji. * Uruchom program [Alt]-[R], [R] (lub [Ctrl]-[F9]). * Podaj kilka liczb: np. 1,2,5,7,8 itp. * Przerwij działanie programu naciskajšc kombinację klawiszy [Ctrl]-[C]. * SprawdŸ, jaki jest katalog wyjœciowy dla TC. - Rozwiń menu Options [Alt]-[O], - Otwórz okienko Directories... [D], - SprawdŸ zawartoœć okienka tekstowego Output Directory. Teraz wiesz już gdzie szukać swojego programu w wersji *.EXE.! 7. Zamknij okno programu rozkazem Close [Alt]-[W], [C] (lub [Alt]-[F3]). 8. WyjdŸ z Turbo C + + [Alt]-[X]. Jeœli TC będzie domagał się zapisu programu na dysk - odpowiedz [Y] - Tak. 9. Zmieniajšc odpowiednio katalog i ewentualnie dysk odszukaj swój program GOTOTEST.EXE. 10. Uruchom program poza œrodowiskiem IDE. 11. SprawdŸ reakcję programu na klawisze: [Esc], [Ctrl]-[C], [Ctrl]-[Break]. Uruchom powtórnie TC i załaduj program rozkazem: TC A:GOTOTEST Wykonaj od nowa kompilację programu [F9]. ... is up to date... Jeœli TC nie zechce powtórzyć kompilacji i odpowie Ci: Making A:\GOTOTEST.C is up to date (Program w takiej wersji już skompilowałem, więcej nie będę!) 55 str 56 nie przejmuj się. Dokonaj jakiejkolwiek pozornej zmiany w programie (np. dodaj spację lub pusty wiersz w dowolnym miejscu). Takich pozornych zmian wystarczy by oszukać Turbo C + + . TC nie jest na tyle inteligentny, by rozróżniać zmiany rzeczywiste i pozorne w pliku Ÿródłowym. Powtórz kompilację programu. Nie musisz uruchamiać programu. Zwróć uwagę tym razem na pojawiajšce się w okienku komunikatów ostrzeżenie: Warning: A:\GOTOTEST.C 14: Unreachable code in function main. (Uwaga: Kod programu stanowi zamkniętš strukturę. Rozbudowa kodu funkcji main niemożliwa.) O co chodzi? Przyjrzyj się tekstowi programu. Nawet jeœli po rozkazie skoku bezwarunkowego: goto etykieta; dopiszesz jakikolwiek inny rozkaz, to program nigdy tego rozkazu nie wykona. Właœnie o to chodzi. Program nie może nawet nigdy wykonać rozkazu "return 0", który dodaliœmy "z przyzwyczajenia". Pętla programowa powinna być wykonywana w nieskończonoœć. Kto wobec tego przerwał działanie Twojego programu? Nieskończonš pętlę pro- gramowš przerwał DOS. Program zwrócił się do systemu DOS, a konkretnie do którejœ z DOSowskich funkcji obsługi WEJŒCIA/WYJŒCIA i to DOS wykrył, że przycisnšłeœ klawisze [Ctrl]-[C] i przerwał obsługę Twojego programu. Następnie DOS "wyrzucił" twój program z pamięci operacyjnej komputera i zgłosił gotowoœć do wykonania dalszych Twoich poleceń - swoim znakiem zachęty C:\ >~ lub A:\ > Spróbujmy wykonać taki sam "face lifting" programom P6 i P7, dodajšc do nich najprostszš pętlę. Zamiast nielubianego rozkazu goto zastosujemy pętlę for. Ponieważ nie możemy sprecyzować żadnych warunków, każemy programowi wykonywać pętlę bezwarunkowo. 1. Pozamykaj zbędne okna [Alt]-[F3]. 2. Otwórz okienko dialogowe Open [F3]. 3. Wpisz do okienka tekstowego nazwę: A:\FACELIFT.C [Enter) 4. Wpisz tekst programu: // Przyklad FACELIFT.C // Program przykladowy lOnal6.C / I6nal0.C FACE LIFTING ~ include int liczba; int main() clrscr(); 56 str 57 for(") printf("Podaj liczbe dziesietna calkowita ? \n"); scanf("o/od", &liczba); printf("Szesnastkowo to wynosi:\n"); printf("o/oX",liczba); getch(); printf("Podaj liczbe SZESNASTKOWA-np.DF- DUZE LITERY: \n"); scanf("o/oX", &liczba); printf("o/os","Dziesietnie to wynosi: "); printf("o/od",liczba); getch(); return 0; 5. Uruchom program Run, Run. (Oznaczenie Run, Run oznacza: * Rozwiń menu Run, np. [Alt]-[R]; * Wybierz z menu rozkaz Run, [R]. 6. Dla przetestowania działania programu: * podaj kolejno liczby o różnej długoœci 1, 2, 3, 4, 5, 6 cyfrowe; * zwróć uwagę, czy program przetwarza poprawnie liczby dowolnej długoœci? 7. Przerwij program naciskajšc klawisze [Ctrl]-[C]. 8. Zapisz program na dyskietkę [F2]. 9. WyjdŸ z IDE naciskajšc klawisze [Alt]-[X]. 5.2. Opracuj program pobierajšcy znak z klawiatury i podajšcy w od- powiedzi kod ASCII pobranego znaku dziesiętnie. 5.3. Opracuj program pobierajšcy liczbę dziesiętnš i podajšcy w od- powiedzi : * kod ósemkowy, * kod szesnastkowy, * znak o zadanym ** dziesiętnie ** szesnastkowo kodzie ASCII. 57 str 58 Lekcja 6 Jakie operatory stosuje Turbo C-~ + Podczas tej lekcji: * Poznasz operatory języka C. * Przetestujesz dzialanie niektórych operatorów. * Dowiesz się więcej o deklarowaniu i inicjowaniu zmiennych. Słów kluczowych jest w języku C stosunkowo niewiele, za to operatorów wyraŸnie więcej niż np. w Basicu. Z kilku operatorów już korzystałeœ w swoich programach. pełnš listę operatorów Turbo C + + v 1.0 wraz z krótkim wyjaœnieniem. Operatory C++ sš podzielone na 16 grup i można je scharakteryzować: * priorytetem ** najwyższy priorytet ma grupa 1 a najniższy grupa 1 fi - przecinek, np. mnożenie ma wyŸszy priorytet niż dodawanie; ** wewnštrz każdej z 16 grup priorytet operatorów jest równy; * łšcznoœciš (wišzaniem). Precedence - kolejnoœć, priorytet. Associativity - asocjatywnoœć, łšcznoœć, wišzanie. Operator jest łšczny lewo/prawo-stronnie, jeœli w wyrażeniu zawierajšcym na tym samym poziomie hierarchii nawiasów min. dwa identyczne operatory najpierw jest wykonywany operator lewy/prawy. Operator jest łšczny, jeœli kolej- noœć wykonania nie wpływa na wynik. Przykład: a+b+c+d = (a+d)+(c+b) ASSIGN(ment) - Przypisanie. EQAL(ity) - Równy, odpowiadajšcy. BITWISE - bit po bicie (bitowo). Oznaczenia łšcznoœci przyjęte w Tabeli 1: {L->R} (Left to Right) z lewa na prawo. {L< <-R~ (Right to Left) z prawa na lewo. REFERENCE - odwołanie do..., powołanie się na..., wskazanie na... . DEREFERENCE - skasowanie referencji. 58 str 59 unkcje logiczne: R - LUB - suma logiczna (alternatywa). D - I - iloczyn logiczny. OR (eXclusive OR) - ALBO - alternatywa wyłšczajšca. OT - NIE - negacja logiczna. :~ abela 1. Lista operatorów języka C + + . Kategoria Operator Co robi / jak działa 1. Highest () * ogranicza wyrażenia, a(Najwyższy Parentheses * izoluje wyrażenia warunkowe, priorytet) * wskazuje na wywołanie funkcji, {L-R} grupuje argumenty funkcji. [] zawartoœć jedno - lub wielowymiarowych Brackets tablic (direct component selector) - > (indirect, or pointer, selection) Bezpoœrednie lub poœrednie wskazanie elementu unii bšdŸ struktury. Operator specyficzny dla C + + . Pozwala na dostęp do nazw GLOBALNYCH, nawet jeœli zostały przysłonięte przez LOKALN E. 2. ! Negacja logiczna (NOT) Jednoar- gumentowe ~ Zamiana na kod KOMPLEMENTARNY bit po (Unary) bicie. Dotyczy liczb typu INT. {L < < -R} + Bez zmiany znaku (Unary plus) Zmienia znak liczby / wyrażenia (Unary minus) + + PREinkrementacja/POSTinkrementacja PRE/POSTdekrementacja & Operator adresu(Referencing operator) * Operator wskazania (Dereferencing operator) sizeof Zwraca wielkoœć argumentu w bajtach 59 str 60 Dynamiczne zarzšdzanie pamięciš: new - przydziela pamięć, delete - likwiduie nrzvdział namieci 3. Multi- * Mnożenie (UWAGA: Druga rola "*") plikatywne {L- > R} / Dzielenie Keszta z dzielema lmodulo) 4. Dostępu .* Operatory specyficzne dla C + + . (Member (dereference) Skasowanie bezpoœredniego wskazania access) na członka klasy (Class Member). {L-R} - > * Skasowanie poœredniego wskazania typu objektowe "wskaŸnik do wskaŸnika" 5. Addy- + Dodawanie dwuargumentowe. tywne {L- > R} Odejmowanie dwuargumentowe. 6. Przesu- < < Binarne przesunięcie w lewo. niůYcia (Shift) > > Binarne przesunięcie w prawo. {L- > R} (bit po bicie) 7. Relacji < Mniejsze niż... {L- > R} w ięxsze mz. . . . Mnięjsze lub równe. wi‡xsze iuo rowne. 8. Równoœci - - Równe (równa się). {L- > R} ~ = Nie równe. 9. & AND binarnie (Bitwise AND) {L->R} UWAGA: Druga rola "&". 10. XOR binarnie (Alternatywa wyłšczna). {L- > R} UWAGA: To nie potęga! I 1. {L- > R} ; OR binarnie (bit po bicie) 12. {L->R} && ~ Iloczyn logiczny (Lo~ical AND). 1 '1 I I I Q"~.,~ I ~~.~"1 /1P \ str 61 Zapis a ? x : y oznacza: "if a = = TRUE then x else y" gdzie TRUE to logiczna PRAWDA Przypisz wartoœć (jak Przypisz iloczyn. Zapis X* = 7 oznacza: X=X*7 (o 1 bajt krócej!). Przypisz resztę z dzielenia Przypisz sumę X + = 2 oznacza "X: = X + 2" Przypisz iloczyn binarny ( Bitwise AND) bit po bicie. Przypisz XOR bit po bicie Przypisz sumę log. bit po bicie. Przypisz wynik przesunięcia o jeden bit w lewo. Oddziela elementy na liœcie argu- mentów funkcji, Stosowany w specjalnych wyrażeniach tzw. "Comma Expression". Operatory ~ i ~~ stosuje się tylko w PREPROCESORZE. Gdyby okazało się, że oferowane przez powyższy zestaw operatory nie wystarczajš Ci lub niezbyt odpowiadajš, Turbo C++ pozwala na tzw. OVERLOADING, czyli przypisanie operatorom innego, wybranego przez użytkownika działania. Można więc z operatorami robić takie same sztuczki jak z identyfikatorami. Na razie jednak sšdzę, że ten zestaw nam wystarczy, w każdym razie na kilka najbliższych lekcji. Podobnie, jak pienišdze na bezludnej wyspie, niewiele warta jest wiedza, której nie można zastosować praktycznie. PrzejdŸmy więc do czynu i przetestujmy działanie niektórych operatorów w praktyce. 61 str 62 TEST OPERATORÓW JEDNOARGUMENTOWYCH 1. Uruchom TC. 2. Pozamykaj zbędne okna. 3. Otwórz plik nowego programu: * Open [F3), * Wpisz: A:\UNARY.TST Zwróć uwagę na nietypowe rozszerzenie *.TST. Turbo C+ + jest tolerancyjny. Wersje Ÿródłowe programów nie muszš mieć rozszerzenia *.C. * Wybierz klawisz [Open) w okienku lub naciœnij [Enter]. 4. Wpisz tekst programu: // UNARY.TST - operatoryjednoargumentowe ~ include < stdio.h > ~ include float x; void main(void) clrscr(); for(") printf("\n Podaj liczbe...\n"); scanf("o/of", &x); printf("\no/of\to/of\to/of\n",x, +x, -x ); printf("\no/of", --x ); printf("\to/of", x ); printf("\to/of", ++x); Zwróć uwagę, że po nawiasie zamykacjšcym nieskończonš pętlę nie ma tym razem żadnego rozkazu. Nie wystšpi także ostrzeżenie przy kompilacji. To trochę niesolidnie popełniać najbardziej typowy błšd poczštkujšcych programistów - zakładać nieskoń- czone pętle i liczyć na cud, a konkretniej na DOS. W następnych programach zrezygnujemy już z tej brzydkiej praktyki. 5. Uruchom program Run, Run. Popraw ewentualne błędy 6. Podajšc różne wartoœci liczby x: - dodatnie i ujemne, - całkowite i rzeczywiste, przeanalizuj działanie operatorów. 62 str 63 7. Przerwij program [Ctrl)-[C]. 8. Zmodyfikuj w programie deklarację typu zmiennej X wpisujšc kolejno: - float x; (rzeczywista) - int x; (całkowita) - short int x; (krótka całkowita) - long int x; (długa całkowita) 9. Zwróć uwagę, że zmiana del;laracji zmiennej bez JEDNOCZESNEJ zmiany formatu w funkcjach scanf() i printf() spowoduje komunikaty o błędach. 10. Spróbuj samodzielnie dobrać odpowiednie formaty w funkcjach scanf() i printf(). Jeœli miałeœ kłopot z dobraniem stosownych formatów, nie przejmuj się. Przyjrzyj się następnym przykładowym programom. Zajmijmy się teraz dokładniej INKREMENTACJĽ, DEKREMENTACJĽ i OPE- RATORAMI PRZYPISANIA. 1. Zamknij zbędne okna na ekranie. Pamuiętaj o zapisaniu programów na dyskietkę w tej wersji, która poprawnie działa lub w ostatniej wersji roboczej. 2. Otwórz plik: A:\ASSIGN.TST 3. Wpisz tekst programu: ~ include < stdio.h > ~ include long int x; short int krok; char klawisz; int main() clrscr(); printf("Test operatora przypisania += \n"); x=0; printf("Podaj KROK ? \n"); scanf("%d",&krok); for(") printf("\n%d\n", x+ =krok); printf("[Enter] - dalej [K) - Koniec\n"); klawisz = getch(); if (klawisz=='k~ ~ klawisz=='K') goto koniec; koniec: printf("\n Nacisnij dowolny klawisz..."); getch(); return 0; 63 str 64 Zgodnie z obietnicš, w tym programie już sami sprawdzamy, czy nie pora przerwać pętlę. Zamiast użyć typowej instrukcji break (przerwij) stosujemy nielubiane goto, gdyż jest bardziej uniwersalne i w przeciwieństwie do break pozwala wyraŸnie pokazać dokšd następuje skok po przerwaniu pętli. Zwróć uwagę na nowe elementy w programie: * DEKLARACJE ZMIENNYCH: long int x; (długa, całkowita) short int krok; (krótka, całkowita) char klawisz; (zmienna znakowa) * INSTRUKCJĘ WARUNKOWĽ: if (KLAWISZ=='k'~ ~ KLAWISZ=='K') goto koniec; (JEŻELI zmienna KLAWISZ równa się "k" LUB równa się "K" idŸ do etykiety koniec:) * Warunek sprawdzany po słowie ifjest ujęty w nawiasy. * Nadanie wartoœci zmiennej znakowej char klawisz przez funkcję: klawisz = getch(); 4. Skompiluj program. Popraw ewentualne błędy. 5. Uruchom program. Podajšc różne liczby (tylko całkowite!) przeœledŸ działanie operatora. 6. Zapisz poprawnš wersję programu na dysk/dyskietkę [F2]. 7. Jeœli masz już doœć, wyjdŸ z TC - [Alt]-[X], jeœli nie, pozamykaj tylko zbędne okna i możesz przejœć do zadań do samodzielnego rozwišzania - > [Z]! 6.1. Do programu przykładowego [P - 15] wstaw kolejno różne operato- ry przypisania: * - , , / = itp. PrzeœledŸ działanie operatorów. 6.2. W programie [P-15] zmień typ zmiennych: long int x; na float x; short int KROK; float KROK; Przetestuj działanie operatorów w przypadku liczb zmiennoprzecin- kowych. 6.3. Zapisz w języku C * negację iloczynu logicznego, * sumę logicznš negacji dwu warunków W programie [P-16] zilustrujemy działanie wszystkich pięciu operatorów inkrementacji (dekrementacja to też inkrementacja tylko w przeciwnš stronę). // INDEKREM.C przyklad P-16 ~ include rstdio.h> ~ include int b,c,d,e; str 65 1nt i; int STO =100; void main(void) clrscr( ); printf("Demonstruje dzialanie \n"); printf(" PREinkrementacji POSTinkrementacji"); printf("\nNr --X ++X X-- X++ \n"); b=c=d=e=ST0; for(i=1; i<6; i++) i { printf("%d\t%d\t%d\t\to/od\to/od\t\n",i,--b,+ +c,d--,e+ +); getch ( ) ; PRE I POSTINKREMENTACJA. INKREMENTACJA oznacza zwiększenie liczby o jeden, DEKREMENTACJA oznacza zmniejszenie liczby o jeden. PRE oznacza wykonanie in/de-krementacji przed użyciem zmiennej, POST - in/de-krementację po użyciu zmiennej. Działanie możesz przeœledzić na wydruku, który powinien Ci dać program przykładowy [P-16] INDEKREM.C: Demonstruje działanie PREinkrementacji POSTinkrementacji Nr --X + + X X-- X + + 1 99 101 100 100 2 98 102 99 101 3 97 103 98 102 4 96 104 97 103 5 95 105 96 104 Uruchom program powtórnie naciskajšc klawisz [F7]. Odpowiada to poleceniu Trace into (włšcz œledzenie) z menu Run. Przeœledzimy działanie programu przy pomocy Debuggera . Po wykonaniu kompilacji (lub odstšpieniu od kompilacji, jeœli nie dokonałeœ zmian w programie) pojawił się na ekranie pasek wyróżnienia wokół funkcji main(), bo to od niej rozpoczyna się zawsze wykonanie programu. Naciœnij powtórnie [F7]. Pasek przesunšł się na funkcję clrscr();. Mignšł na chwilę ekran użytkownika, ale na razie nie ma po co tam zaglšdać, więc wykonamy kolejne kroki. Podam klejno: [Klawisz]-[wiersz]. [F7] - printf("Demonstruję..."); Zaglšdamy na ekran użytkownika [Alt]-[F5).....[Enter] - wracamy do edytora. [F7],[F7]... doszliœmy do wiersza b=c=d=e=STO; 65 str 66 Zapraszamy teraz debugger do pracy wydajšc mu polecenie "Wykonaj Inspekcję" [Alt]-[D), Inspect. Pojawia się okienko dialogowe "Inspect". * Wpisz do okienka tekstowego nazwę zmiennej b i naciœnij [Enter]. Pojawiło się okienko dialogowe "Inspecting b" zawierajšce fizyczny adres pamięci RAM, pod którym umieszczono zmiennš b i wartoœć zmiennej b (zero; instrukcja przypisania nada jej wartoœć 100). Naciœnij [Esc]. Okienko zniknęło. [F7] - for(i=1; i<6; i++); * NaprowadŸ kursor na zmiennš d w tekœcie programu i wykonaj inspekcję powtórnie [Alt)-[D], [I]. Jak widzisz w okienku zmiennej d została nadana wartoœć 100. Naciœnij [Esc]. Dokonamy teraz modyfikacji wartoœci zmiennej przy pomocy polecenia Evaluate and Modify (sprawdŸ i zmodyfikuj) z menu Debug. * Naciœnij klawisze [Alt]-[D], [E). Pojawiło się okienko dialogowe "Evaluate and Modify". W okienku tekstowym "Expression" (wyrażenie) widzisz swojš zmiennš d. * PrzejdŸ przy pomocy [Tab] do okienka tekstowego "New Value" (nowa wartoœć) i wpisz tam liczbę 1000. Naciœnij [Enter] a następnie [Esc]. Okienko zamknęło się. Zmiana wartoœci zmiennej została dokonana. [F7] - printf("...") - wnętrze pętli for. [F7] - wykonała się pętla. Obejrzyjmy wyniki [Alt]-[F5]. W czwartej kolumnie widzisz efekt wprowadzonej zmiany: Demonstruje dzialanie PREinkrementacji POSTinkrementacji Nr --X + + X X-- X + + 1 99 101 1000 100 2 98 102 999 101 3 97 103 998 102 4 96 104 997 103 5 95 105 996 104 Zwróć uwagę w programie [P-16] na * Zliczanie iloœci wykonanych przez program pętli int i; (deklaracja, że i będzie zmiennš całkowitš) i=1; (zainicjowanie zmiennej, nadanie poczštkowej wartoœci) i + + ; (powiększanie i o 1 w każdej pętli) i < 6 (warunek kontynuacji) * Możliwoœć grupowej deklaracji zmiennych tego samego typu: int b,c,d,e; str 67 6.4. Zmień w programie [P-16) wartoœć poczštkowš STO na dowolnš innš - np. zero. Przetestuj działanie programu. 6.5. SprawdŸ, czy można wszystkie zmienne używane w programie [P-16] zadeklarować wspólnie (jeden wiersz zamiast trzech). 67 str 68 Lekcja 7 Jak deklarować zmienne Co to jest wskaŸnik W trakcie tej lekcji: 1. Dowiesz się więcej o deklaracjach. 2. Poprawisz trochę system MS DOS. 3. Dowiesz się co tojest wskaŸnik i do czego sluży. l~EKLARACJE. Deklarować można w języku C + + : * zmienne; * funkcje; * typy (chodzi oczywiœcie o typy nietypowe). Zmienne w języku C + + mogš mieć charakter: * skalarów - którym przypisuje się nierozdzielne dane np. całkowite, rzeczywiste, wskazujšce (typu wskaŸnik) itp. * agregatów - którym przypisuje się dane typu strukturalnego np. obiektowe, tablicowe czy strukturowe. Powyższy podział nie jest tak całkiem precyzyjny, ponieważ pomiędzy wskaŸnikami a tablicami istnieje w języku C doœć specyficzna zależnoœć, ale więcej na ten temat dowiesz się z póŸniejszych lekcji. Zmienne mogš być: * deklarowane, * definiowane i * inicjowane. Stała to to taka zmienna, której wartoœć można przypisać tylko raz. Z punktu widzenia komputera niewiele się to różni, bo miejsce w pamięci i tak, stosownie do zade- klarowanego typu zarezerwować trzeba, umieœcić w tablicy i zapamiętać sobie iden- tyfikator i adres też. Jedyna praktyczna różnica polega na tym, że zmiennej zade- klarowanej jako stała, np.: const float PI =3.142; nie można przypisać w programie żadnej innej wartoœci, innymi słowy zapis: const float PI = 3.14; jest jednoczeœnie DEKLARACJĽ, DEFINICJĽ i ZAINICJOWANIEM stałej Pl. 68 str 69 t x,y,z; ( D E KLARACJA) nst float TEMP=36.6; (DEFINICJA) =42; (ZAINICJOWANIE zmiennej) AŁA czy ZMIENNA. nst - (CONSTant) - stała. Deklaracja stałej, słowo kluczowe języku C. ~ Var - (VARiable) - zmienna. W języku C przyjmowane domyœlnie. Słowo var (stosowane w Pascalu) NIE JEST słowem kluczowym języka C ani C+ + (!). Skutek praktyczny: * Ma sens i jest poprawna deklaracja: const float PI = 3.1416; * Niepoprawna natomiast jest deklaracja: var float x; Jeœli nie zadeklarowano stałej słowem const, to zmienna (var) przyjmowana jest domyœlnie. Definicja powoduje nie tylko okreœlenie, jakiego typu wartoœciami może operować dana zmienna bšdŸ funkcja, która zostaje od tego momentu skojarzona z podanym identyfikatorem, ale dodatkowo powoduje: * w przypadku zmiennej - przypisanie jej wartoœci, * w przypadku funkcji - przyporzšdkowanie ciała funkcji. Zdefniujmy dla przykładu kilka własnych funkcji. Przykład: void UstawDosERRORLEVEL(int n) /" nazwa funkcji`/ exit(n); /` skromne ciało funkcji "/ Przykład int DrukujAutora(void) printf("\nAdam MAJCZAK AD 1993 - C++ w 48 godzin!\n"); printf("tel. 032-625-276 nie dzwonić po 23!") return 0; 69 str 70 Przykład void DrukujPytanie(void) printf("Podaj liczbe z zakresu od 0 do 255"); printf("\nUstawie Ci ERRORLEVEL\t"); W powyższych przykładach zwróć uwagę na: * sposób deklarowania zmiennej, przekazywanej jako parametr do funkcji - n i err; * definicje funkcji i ich wywołanie w programie (podobnie jak w Pascalu). A teraz zilustrujemy zastosowanie tego mechanizmu w programie przykładowym. Funkcje powyższe sš PREDEFINIOWANE w pliku FUNKCJE1.H na dyskietce dołšczonej do ksišżki. Wpisz i uruchom program: ~ include "stdio.h" ~ include "A:\funkcjel .h" int err; void main(void) DrukujAutora(); DrukujPytanie(); scanf(""/od", &err); UstawDosERRORLEVEL(err); Jeœli tworzyłeœ już w systemie MS DOS swoje programy wsadowe (np. typu INSTALUJ.BAT), to zauważyłeœ zapewne, że brak w systemie DOS programu sys- temowego pozwalajšcego na wybór wariantu działania programu wsadowego podczas pracy, zgodnie z życzeniem operatora. Wielu programistów korzysta chętnie z programu BE.EXE (opcja BE ASK) z pakietu Norton Utilities. Ale to trochę nieelegancko korzystać z takiego nieautoryzowanego zapożyczenia (o prawach autorskich i licencji nie wspo- mnę), więc uzupełnimy ten istotny brak w systemie MS DOS. 7.1. Opracuj najprostszy program PYTAJ.EXE ustawiajšcy zmiennš systemowš ERRORLEVEL: 70 str 71 ~include < stdio.h > int n; void main (void) scanf(""/od", &n); exit(n); 7.2. Zastosuj program PYTAJ.EXE we własnych plikach wsadowych typu *.BAT wedhig wzoru: [~ echo off :LOOP cls echo 1. Wariant 1 echo 2. Wariant 2 echo 3. Wariant 3 echo Wybierz wariant działania programu...1,2,3 ? PYTAJ IF ERRORLEVEL 3 GOTO START3 IF ERRORLEVEL 2 GOTO START2 IF ERRORLEVEL 1 GOTO START1 echo Chyba zartujesz...? goto LOOP :START1 AKCJA WARIANT 1 GOTO KON I EC :START2 'AKCJA WARIANT 2 GOTO KON I EC :START3 'AKCJA WARIANT 3 :KONIEC 'AKCJA WARIANT n - oznacza dowolny cišg komend systemu DOS, np. COPY, MD, DEL, lub uruchomienie dowolnego programu. Do utworzenia pliku wsadowego możesz zastosować edytor systemowy EDIT. 7.3. Skompiluj program posługujšc się oddzielnym kompilatorem TCC.EXE. Ten wariant kompilatora jest pozbawiony zintegrowanego edytora. Musisz uruchomić go piszšc odpowiedni rozkaz po DOSowskim znaku zachęty C:\ > . Zastosowanie przy kompilacji małego modelu pamięci pozwol Ci uzyskać swój program w wersji *.COM, a nie *.EXE. Wydaj rozkaz: c:\tc\bin\tcc -mt -It c:\pytaj.c Jeœli pliki znajdujš się w różnych katalogach, podaj właœciwe œcieżki dostępu (path). 71 str 72 CO TO ZA PARAMETRY??? Przez swš "ułomnoœć" - I 6 bitowš szynę i segmentację pemięci komputery IBM PC wymusiły wprowadzenie modeli pamięci: TINY, SMALL, COMPACT, MEDIUM. LARGE, HUGE. Więcej informacji na ten temat znajdziesz w DODATKU. Parametry dotyczš sposobu kompilacji i zastosowanego modelu pamięci: -mt - kompiluj (-*.OBJ) wykorzystujšc model TINY -lt - konsoliduj (-*.COM) wykorzystujšc model TINY i zatem odpowiednie biblioteki (do każdego modelu jest odpowiednia biblioteka *.LIB). Możesz stosować także: ms, mm, ml, mh, ls, lm,11, lh. Po instalacji Turbo C++ dla TC.EXE slandardowo jest przyjmowany model SMALL. Zatem kompilacja, którš wykonujesz z IDE daje taki sam efekt, jak zastosowanie kompilatora tcc w następujšcy sposób: tcc -ms -ls program.c Nie uda się przerobienie z EXE na COM tych programów, w których występujš funkcje realizujšce arytmetykę zmiennoprzecinkowš (tloat). System DOS oferuje Ci do takich celów program EXE2BIN, ale lepiej jest panować nad tym problemem na etapie tworzenia programu. Skoro już poprawiliœmy trochę system DOS i możemy odczuwać satysfakcję, że dzięki C + + nasz DOS jest już trochę lepszy niż u nie wtajemniczonych kolegów, wracamy do opisu języka C + + . DEKLAROWANIE ZMIENNYCH CAŁKOWITYCH. Zwróć uwagę, że dla IBM PC typy int i short int sš interpretowane tak samo. Dla innych komputerów może być inaczej. Tabela 2. Deklaracje zmiennych całkowitych TYP Specyfikatory Zakres wartoœci znakowa char -128... + 127 znakowa bez znaku unsigned char 0...255 krótka short int -32768... + 32767 krótka bez znaku unsigned short int 0...65535 całkowita zwykła int -32768... + 32767 całk. bez znaku unsigned int 0...65535 całk. długa long int -2147483647...+2147483647 całk. dł. b. znaku unsigned long int 0...4294967295 Ponieważ nie ma liczb ani short float, ani unsigned short float to słowo int może zostać opuszczone w deklaracji. Poprawne sš zatem deklaracje: short a; unsigned short b; 72 str 73 DEKLAROWANIE ZMIENNYCH RZECZYWISTYCH (zmiennopozycyjnych). Tabela 3. Deklaracje zmiennych zmiennopozycyjnych TYP Specyfikatory Zakres wartoœci rzeczywista float + -3.4E-38. ..3.4E + 38 podwójnej precyzji double + -1.7E-308..1.7E + 308 poczwórnej prec. long double +-3.4E-4932...3.4E+4932 Zapis + -3.4E-38. ..3.4E + 38 oznacza: -3.4*10 +38...0...+3.4*10 38...+3.4*10 +38 Dopuszczalne sš deklaracje i definicje grupowe z zastosowaniem listy zmiennych. Zmienne na liœcie należy oddzielić przecinkami: int a=O,b=1,c,d; float PI=3.14, max=36.6; Poœwięcimy teraz chwilę drugiej funkcji, któršjuż wielokrotnie stosowaliœmy - funkcji wejœcia - scanf(). FUNKCJA scanf(). Funkcja formatowanego wejœcia ze standardowego strumienia wejœciowego (stdin). Funkcja jest predefmiowana w pliku STDIO.H i korzystajšc z funkcji systemu operacyjnego wczytuje dane w postaci tekstu z klawiatury konsoli. Interpretacja pobranych przez funkcję scanf znaków nastšpi zgodnie z życzeniem programisty okreœlonym przez zadany funkcji format ("/of, "/od, "/oc itp.). Wywołanie funkcji scanfma postać: scanf(Format, Adres zmiennej1, Adres zmiennej2...); dla przykładu scanf("/of"/of"/of, &X1, &X2, &X3); wczytuje trzy liczby zmiennoprzecinkowe X1, X2 i X3. Format decyduje, czy pobrane znaki zostanš zinterpretowane np. jako liczba całkowita, znak, łańcuch znaków (napis), czy też w inny sposób. Od sposobu interpretacji zależy i rozmieszczenie ich w pamięci i póŸniejsze sięgnięcie do nich, czyli odwołanie do danych umieszczonych w pamięci operacyjnej komputera. Zwróć uwagę, że podajšc nazwy (identyfikatory) zmiennych należy poprzedzić je w funkcji scanf() operatorem adresu [&] . Zapis: int X; scanf("/od, &X); oznacza, że zostanš wykonane następujšce działania: * Kompilator zarezerwuje 2 bajty pomięci w obszarze pamięci danych programu na zmiennš X typu int; 73 str 74 * W momencie wywołania funkcji scanf funkcji tej zostanie przekazany adres pamięci pod którym ma zostać umieszczona zmienna X, czyli tzw. WSKAZANIE DO ZMIENNEJ; * Znaki pobrane z klawiatury przez funkcję scanf majš zostać przekształcone do postaci wynikajšcej z wybranego formatu "/ad - tzn. do postaci zajmujšcej dwa bajty liczby całkowitej ze znakiem. A JEŒLI PODAM INNY FORMAT? Język C wykona Twoje rozkazy najlepiej jak umie, niestety nie sprawdzajšc po drodze fortnatów, a z zer i jedynek zapisanych w pamięci żaden format nie wynika. Otrzymasz btędne dane. Wpisz i uruchom program P-18. Jest to przykład skutków błędnego formatowania. Dołšcz pliki STDIO.H i CONIO.H. void main() float A, B; clrscr(); scanf("/of "/of, &A, &B); printf(n"/oft"/od, A,B); getch(); 6.3 Zmień w programie [P-18] w funkcji printf() wzorce formatu na o/os "/oc, itp. Porównaj wyniki. Z filozofii języka C wynika, że adres w pamięci to taka sama liczba, jak wszystkie inne i wobec tego można niš manipulować. Adresami rzšdzš jednak doœć specjalne prawa dlatego też w języku C występuje jeszcze jeden specjalny typ zmiennych - tzw. ZMIENNE WSKAZUJĽCE (ang. pointer - wskaŸnik). Twoja intuicja podpowiada Ci zapewne, że sš to zmienne całkowite (nie ma przecież komórki pamięci o adresie 0.245 ani 61/17). Pojęcia "komórka pamięci" a nie np. "bajt" używam œwiadomie, ponieważ obszar zajmowany w pamięci przez zmiennš może mieć różnš długoœć. Aby komputer wiedział ile kolejnych bajtów pamięci zajmuje wskazany obiekt (liczba długa, krótka, znak itp.), deklarujšc wskaŸnik trzeba podać na co będzie wskazywał. W sposób "nieoficjalny" już w funkcji scanf korzystaliœmy z tego mechanizmu. Jest to zjawisko specyficzne dla języka C, które zostało częœciowo przeniesione do nowszych wersji kompilatorów Pascala, więc zajmijmy się nim trochę dokładniej. 74 str 75 POJĘCIE ZMIENNEJ WSKAZUJĽCEJ I ZMIENNEJ WSKAZYWANEJ. WskaŸnik to zxnienna, która zawiera adres innej zmiennej w pamięci komputera. Istnienie wskaŸników umożliwia poœrednie odwoływanie się do wskazywanego obiektu (liczby, znaku, łańcucha znaków itp.) a także stosunkowo proste odwołanie się do obiektów sšsiadujšcych z nim w pamięci. Jest to szczególnie wygodne w przypadku tablic. Załóżmy, że: x - jest umieszczonš gdzieœ w pamięci komputera zmiennš całkowitš typu int zajmujšcš dwa kolejne bajty pamięci, a px - jest wskaŸnikiem do zmiennej x. Jednoargumentowy operator & podaje adres obiektu, a zatem instrukcja: px = &x przypisuje px adres zmiennej x. Mówimy, że: px wskazuje na zmiennš x lub px jest WSKANłKłEM (pointerem) do zmiennej x. Jednoargumentowy operator * (naz. OPERATOREM WYŁUSKANIA) powoduje, że zntienna "potraktowana" tym operatorem jest traktowana jako adres pewnego obiektu. Zatem, jeœli przyjmiemy, że y jest zmiennš typu int, to działanie: y=x oraz px = &x y = * px będzie mieć identyczny skutek. Zapis y = x oznacza: "Nadaj zmiennej y dotychczasowš wartoœć zmiennej x"; a zapis y = *px oznacza: "Nadaj zmiennej y dotychczasowš wartoœć zmiennej, której adres w pamięci wskazuje wskaŸnik px" (czyli właœnie x !). WskaŸniki także wymagajš deklaracji. Poprawna deklaracja w opisanym powyżej przypadku powinna wyglšdać tak: int x,y; int *px; main() Zapis int *px; oznacza: "px jest wskaŸnikiem i będzie wskazywać na liczby typu int". WskaŸniki do zmiennych mogš zamiast zmiennych pojawiać się w wyrażeniach po PRAWEJ STRONIE, np. zapisy: int X,Y; int *pX; pX = &X; Y = *pX + 1; /* to samo, co Y = X + 1 */ printf(""/od", *pX); /* to samo, co printf(""/od", X); */ Y = sqrt(*pX); /* pierwiastek kwadrat. z X */ 75 str 76 sš w języku C poprawne. Zwróć uwagę, że operatory & i * majš wyższy priorytet niż operatory arytmetyczne, dzięki czemu * najpierw następuje pobranie spod wskazanego przez wskaŸnik adresu zmiennej; * potem następuje wykonanie operacji arytmetyeznej; (operacja nie jest więc wykonywana na wskaŸniku, a na wskazywanej zmiennej!). W języku C możliwa jest także sytuacja odwrotna: Y = *(pX + 1 ) Ponieważ operator () ma wyższy priorytet niż * ,to w takim przypadku: najpierw wskaŸnik zostaje zwiększony o 1; potem zostaje pobrana z pamięci wartoœć znajdujšca się pod wskazanym adresem (w tym momencie nie jest to już adres zmiennej X, a obiektu "następnego" w pamięci) i przypisana zmiennej Y. Taki sposób poruszania się po pamięci jest szczególnie wygodny, jeœli pod kolejnymi adresami pamięci rozmieœcimy np. kolejne wyrazy z tablicy, czy kolejne znaki tekstu. Na raziejednak nie bardzo wiadomo co komputer ma w sšsiednich komórkach pamięci, więc przyjrzyjmy się wyrażeniom, w których wskaŸnik występiije po LEWEJ STRONIE. Zapisy: ! *pX = 0; i X = 0; *pX += 1; i X += 1; (*pX)++; ; X++;/*3*/ majš identyczne działanie. Zwróć uwagę w przykładzie /*3*/, że ze względu na priorytet operatorów () - najwyższy - najpierw pobieramy wskazanš zmiennš; + + - niższy, potem zwiększmy wskazanš zmiennš o 1; Gdyby zapis miał postać: *pX + + to najpierw nastšpiłoby - zwiększenie wskainika o 1 i wskazanie "sšsiedniej" zmiennej, potem - wyłuskanie, czyli pobranie z pamięci zmiennej wskazanej przez nowy, zwiększony wskaŸnik, zawartoœć pamięci natomiasl, lj. wszystkie zmienne rozmieszczone w pamięci pozostałyby bez zmian. .IAK TO WŁAŒCIWIE JEST Z TYM PRIORYTETEM '? Wszystkie operatory jednoargumentowe (kategoria 2, patrz Tabl. 1) majš taki sam priorytet, ale sš PRAWOSTRONNIE ŁĽCZNE {L-R}. Oznacza to, że operacje będš wykonywane Z PRAWA NA LEWO. W wyrażeniu *pX + + oznacza to: najpierw + + potem * Zwróć uwagę, że kolejnoœć {L-R} dotyczy WSZYSTKIC,H operatorów jednoargumentowych. 76 str 77 Jeœli dwa wskaŸniki wskazujš zmienne takiego samego typu, np. po zadeklarowaniu: Œnt *pX, *pY; int X, Y; i zainicjowaniu: pX = &X; pY = &Y; można zastosować operator przypisania: pY = pX Spowoduje to skopiowanie wartoœci (adresu) wskaŸnika pX do pY, dzięki czemu od tego momentu wskaŸnik pY zacznie wskazywać zmiennš X. Zwróć uwagę, że nie oznacza to bynajmniej zmiany wartoœci zmiennych - ani wielkoœc X, ani wielkoœć Y, ani ich adresy w pamięci NIE ULEGAJĽ ZMIANIE. Zatem działanie instrukcji: pY = pX i *pY = *pX jest RÓŻNE a wynika to znowu z priorytetu operatorów: najpierw * wyłuskanie zmiennych spod podanych adresów, potem = przypisanie wartoœci (ale już zmiennym a nie wskaŸnikom!) Język C chętnie korzysta ze wskazania adresu przy przekazywaniu danych - paramet- rów do/od funkcji. Asekurujšc się na całej linii i podkreœlajšc, że nie zawsze wyglšda to tak prosto i ładnie, poshzżę się do zademonstrowania działania wskaŸników przykładowym programem [P-19]. Wpisz i uruchom [Ctrl],-[F9] następujšcy program: [wersja 1 ] ~include "stdio.h" ~include "conio.h" int a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,x=9,y=1 O,i; int *ptr1; long int *ptr2; void main() clrscr(); ptr1= &a; ptr2= &a; printf("Skok o 2Bajty Skok o 4Bajty"); for(i=0; i<=9; i++) printf("\no/od", * (ptr1 + i) ) ; printf("\t\to/od", * (ptr2+ i) ) ; getc h ( ) 77 str 78 [wersja 2] int a=11,b=22,c=33,d=44,e=55,f=66,g=77,h=88,x=99,y=1 O,i; int *ptrl ; long int *ptr2; void main() clrscr(); ptrl =&a; ptr2 = &a; for (i=0; i=9; i++) printf("\"n"/od, *(ptr1 +i)); printf("t"/od", *(ptr2+i)); getch ( ) ; W programie wykonywane sš następujšce czynnoœci: 1. Deklarujemy zmienne całkowite int (każda powinna zajšć 2 bajty pamięci) i nadajemy im wartoœci w taki sposób aby łatwo można je było rozpoznać. 2. Deklarujemy dwa wskaŸnki: ptrl - poprawny - do dwubajtowych zmiennych typu int; ptr2 - niepoprawny - do czterobajtowych zmiennych typu long int. 3. Ustawiamy oba wskaŸniki tak by wskazywały adres w pamięci pierwszej liczby a =11. 4. Zwiększamy oba wskaŸniki i sprawdzamy, co wskazujš. Jeœli kompilator rozmieœci nasze zmienne w kolejnych komórkach pamięci, to powinniœmy uzyskać nastęujšcy wydruk: Skok o 2B Skok o 4B 11 11 22 3 3 33 55 77 5 5 99 66 27475 77 28448 88 8258 99 27475 10 2844 78 str 79 Zwróć uwagę, że to deklaracja wskaŸnika decyduje, co praktycznie oznacza operacja *(ptr + 1). W pierwszym przypadku wskaŸnik powiększa si‡ o 2 a w drugim o 4 bajty. Te odpowiednio 2 i 4 bajty stanowiš długoœć komórki pamięci lub precyzyjniej, pola pamięci przeznaczonego dla zmiennych okreœlonego typu. Wartoœci pojawiajšce się w drugiej kolumnie po 99 sš przypadkowe i u Ciebie mogš okazać się inne. Aby zapewnić programiœcie jeszcze szersze możliwoœci, język C pozwala wskaŸnikom nie tylko wskazywać adres zmiennej w pamięci. WskaŸnik może również wskazywać na inny wskaŸnik. Takie wskazania: int X; int pX; int ppX; pX = &X;ppX = &pX; oznaczamy: *pX- pX wskazuje BEZPOŒREDNIO zmiennš X; ""ppX- ppX skazuje POŒREDNIO zmiennš X (jest wskaŸnikiem do wskaŸnika). """pppX - pppX wskazuje poœrednio wskaŸnik do zmiennej X itd. 7.4 Wybierz dowolne dwa przykładowe programy omawiane wczeœniej i przeredaguj je posługujšc się zamiast zmiennych - wskaŸnikami do tych zmiennych. Pamiętaj, że przed użyciem wskaŸnika należy: * zadeklarować na jaki typ zmiennych wskazuje wskaŸnik; * przyporzšdkować wskaŸnik okreœlonej zmiennej. 7.5 Zastanów się, co oznacza ostrzeżenie TC wypisywane podczas uruchomienia programu [P-19]: Warning 8: Suspicious pointer conversion in function main. Do nauki jazdy, bšdŸ wtedy, gdy chcemy co niedzielę wyjeżdżać na grzyby, wystarczy nam krajowy samochód marki Syrenka a z marek zagranicznych np. "Trabant". Jeœli chcemy, jednakże zostać profesjonal- nym kierowcš rajdowym, to potrzebujemy znacznie nowoczeœniejszego samochodu. Trochę podobnie jest z programowaniem komputerów. Język C, szczególnie po stowarzyszeniu go z nowoczesnym œrodowiskiem operacyjnym Windows czy UNIX/XENIX jest czymœ, co na kształt samochodu Ferrari daje Ci do dyspozycji ogromnš pot‡gę i ogromne możliwoœci. Ale każdy kij ma dwa końce - od Ciebie wymaga to większych umiejętnoœci i ostrożnoœci w stosowaniu "narzędzi chirurgicz- nych", które dostajesz do ręki. Pierwsze takie narz‡dzie, które przy nieumiejętnym stosowaniu może narobić sporo szkodyjuż poznałeœ - to wskaŸniki i operator referencji, pozwalajšce Ci "grzebać" po pamięci operacyjnej komputera. Zanim więc zaczniesz "buszować" po pamięci swojego komputera: * Przeczytaj uważnie tę ksišżkę do końca, * Eksperymentuj ostrożnie. 79 str 80 Lekcja 8 WskaŸniki i tablice w C W czasie tej lekcji: 1. Dowiesz się więcej o zastosowaniu wskaŸników. 2. Co majš wspólnego wskaŸniki i tablice wjęzyku C. WSKANIKI I TABLICE W JĘZYKU C. W języku C pomiędzy wskaŸnikami a tablicami istnieje bardzo œcisły zwišzek. Do ponumerowania elementów w tablicy shzżš tzw. INDEKSY. W języku C * KAŻDA OPERACJA korzystajšca z indeksów może zostać wykona- na przy pomocy wskaŸników; * posługiwanie się wskaŸnikiem zamiast indeksu na ogół przyspiesza operację. Tablice, podobnie jak zmienne i funkcje wymagajš przed użyciem DEKLARACJI. Upraszczajšc problem - komputer musi wiedzieć ile miejsca zarezerwować w pamięci i w jaki sposób rozmieœcić kolejne OBIEKTY, czyli kolejne elementy tablicy. CZY TO JUŻ POCZĽTEK PROGRAMOWANIA OBIEKTOWEGO ? Jeszcze nie. OBIEKTEM w szerokim znaczeniu tego słowa jest każda liczba, znak, łańcuch znaków itp.. Takimi klasycznymi obiektami języki programowania operowałyjuż od dawien dawna. Prawdziwe programo- wanie obiektowe w dzisiejszym, węższym znaczeniu rozpoczyna się jednak tam, gdzie obiektem może stać się także coœ "nietypowego" - np. rysunek. Jest to jednak właœciwy chyba moment, by zwrócić Ci uwagę, że z punktu widzenia komputera obiekt to coœ, co zajmuje pewien obszar pamięci i z czym wiadomo jak postępować. Deklaracja: int A[12); oznacza: należy zarezerwować 12 KOLEJNYCH komórek pamięci dla 12 liczb cał- kowitych typu int (po 2 bajty każda). Jednowymiarowa tablica (wektor) będzie się nazywać "A", a jej kolejne elementy zostanš ponumerowane przy pomocy indeksu: - zwróć uwagę, że w C zaczynamy liczyć OD ZERA A NIE OD JEDYNKI; A[0), A[1), A[2], A[3], .... A[11). Jeœli chcemy zadeklarować: - indeks i; - wskaŸnik, wskazujšcy nam poczštek (pierwszy, czyli zerowy element) tablicy; - samš tablicę; 80 str 81 to takie deklaracje powinny wyglšdać następujšco: int i; int " pA; int A[12]; Aby wskaŸnik wskazywał na poczštek tablicy A[12], musimy go jeszcze zainicjować: pA = &A [0] ; Jeœli poszczególne elementy tablicy sš zawsze rozmieszczane KOLEJNO, to: 'pA[0] oznacza: "wyhzskaj zawartoœć komórki pamięci wskazanej przez wskaŸnik", czyli inaczej - po- bierz z pamięci pierwszy (zerowy!) element tablicy A. Jeœli deklaracja typów elementów tablicy i deklaracja typu wskaŸnika sš zgodne i poprawne, to n:e musimy się dalej martwić ile bajtów zajmuje dany obiekt - element tablicy. Zapisy: 'pA[0); *pA; A[0] '(pA[0]+1) *(pA+1) A[1] '(pA[0)+2) "(pA+2) A[2] itd. sš równoważne i oznaczajš kolejne wyrazy tablicy A. Jeœli tablica jest dwu- lub trójwymiarowa, to poczštek tablicy oznacza zapis: A[0] [0]; A[0) [0] [0]; itd. Zwróć uwagę, że wskaŸnik do tablicy *pA oznacza praktycznie wskaŸnik do POCZĽT- KOWEGO ELEMENTU TABLICY: "pA = *pA[0] To samo można zapisać w języku C w jeszcze inny sposób. Jeœli A jest nazwš tablicy, to zapis: *A oznacza wskazanie do poczštku tablicy A, a zapisy: '(A+1 ) *(pA+1 ) A[1 ] *(A+8) *(pA+g) A[8] itd. sš równoważne. Podobnie identyczne znaczenie majš zapisy: x=&A[i] x=A+i "pACi] *(A+i) Należy jednak podkreœlić, że pomiędzy nazwami tablic (w naszym przykładzie A) a wskaŸnikami istnieje zasadnicza różnica. WskaŸnik jest ZMIENNĽ, zatem operacje: pA=A; pA++; sš dopuszczalne i sensowne. Nazwa tablicy natomiast jest STAŁĽ, zatem operacje: A = pA; ŻLE! A++; LE! sš niedopuszczalne i próba ich wykonania spowoduje błędy! 81 str 82 DEKLAROWANIE I INICJOWANIE TABLIC. Elementom tablicy, podobnie jak zmiennym możemy nadawać watroœci. Wartoœci takie należy podawać w nawiasach klamrowych, a wielkoœć tablicy - w nawiasach kwadratowych. Przykład int WEKTO R [5] ; Tablica WEKTOR jest jednowymiarowa i składa się z 5 elementów typu int: WEKTOR[0]....WEKTOR[4]. Przykład float Array[10) [5]; Tablica Array jest dwuwymiarowa i składa się z 50 elementów typu float Array[0] [0], Array[0] [1 ] ......Array[0] [9] Array[1 ] [0], Array[1 ] [1 ] ......Array[1 ] [9] Array[4] [0], Array[4) [1 )......Array[4] [9] Przykład const int b[4] ={1,2,33,444}; Elementom jednowymiarowej tablicy (wektora) b przypisano wartoœći: b[0] =1; b[1 ] =2; b[2] =33; b[3] =444; Przykład int TAB[2][3]= {{1, 2, 3},{2, 4, 6}}; TAB[0][0] = 1 TAB[0][1) = 2 TAB[0][2) = 3 TAB[1][0] = 2 TAB[1)[1] = 4 TAB[1)[2] = 6 Przykład: Tablica znakowa. Obie formy zapisu dajš ten sam efekt char hej[4] ="Ahoj" char hej[4]={'A', 'h', 'o', 'j'}; hej [0] ='A' hej [1 ] ='h' hej [2] ='o' itp. Przykład: Tablica uzupełniona zerami przez domniemanie float T[2] [3] ={{1, 2.22}, {.5}} kompilator uzupełni zerami do postaci: T[0] [0] =1 T[0] [1 ] =2.22 T[0] [2] =0 T[1 ] [0] =0.5 T[1 ] [1 ] =0 T[1 ] [2] =0 Jeœli nawias kwadratowy zawierajšcy wymiar pozostawimy pusty, to kompilator obliczy jego domniemanš zawartoœć w oparciu o podanš zawartoœć tablicy. Nie spowoduje więc błędu zapis: char D [] ="Jakis napis" int A[] [2] = {{1,2}, {3,4}, {5,6}} Jeœli nie podamy ani wymiaru, ani zawartoœci: 82 str 83 int A[] ; kompilator "zbuntuje się" i wykaże błšd. Dla przykładu, skompiluj program [P-20]. Zwróć uwagę na sposób zainicjowania wskaŸnika. ~include "stdio.h" ~include int a[] [2] ={ {1,2},{3,4},{5,6},{7,8},{9,10},{11,12} }; char b1]={ "Poniedzialek" }; int i; int *pa; char *pb; void main() pa = &a[0][0]; pb = b; // lub pb = b[0]; clrscr(); for (i=0; i<12; i++) printf(""/od\t"/oc\n", *(pa+i), *(pb+i)); getch ( ) ; Zwróć uwagę, że w języku C każdy wymiar tablicy musi mieć swojš parę nawiasów kwadratowych. Dla przykładu, tablicę trójwymiarowš należy deklarować nie tak TAB3D[i, j, k] lecz tak: TAB3D[i] [j] [k]; Jest w dobrym stylu panować nad swoimi danymi i umieszczaćje w tzw. BUFORACH, czyli w wydzielonych obszarach pamięci o znanym adresie, wielkoœci i przeznaczeniu. W programie [P-21] utworzymy taki bufor w postaci tablicy bufor[20] i zastosujemy zamiast funkcji scanf czytajšcej bezpoœrednio z klawiatury parę funkcji: gets() - GET String - pobierz łańcuch znaków z klawiatury do bufora; sscanf(bufor) - odczytaj z bufora (z pamięci). Aby uniknšć nielubianego goto stosujemy konstrukcję for - break, która działa tak samo - też pozwala nam pętelkować. Dokładniej pętlę for omówimy w lekcji 9. Ponieważ mam nadzieję, że "szkolnš" postać pętli for pamiętasz for(i=1; i < 100; i++) pozwalam sobie trochę wyprzedzajšco zastosować jš w programie. Niepodobny do Pascala ani do Basica zapis wynika właœnie z tego, że skok następuje bezwarunkowo. For * nie zeruje licznika pętli (zbędne typowe i =1 ); * nie sprawdza żadnego warunku (zbędne i < 100), * nie liczy pęti (i = i + 1 lub i + + też zbędne !). 83 str 84 ~include "stdio.h" ~include "conio.h" int liczba, ile=0, suma=0; void main() ehar bufor[20] ; clrscr(); printf("podaj liczby - ja oblicze SREDNIA i SUMA\n"); printf("ZERO = KONIEC\n"); for(") // Wykonuj petle BEZWARUNKOWO gets(bufor); sscanf(bufor, "o/od", &liczba); suma += liczba; ile++ů if (liczba == 0) break; // JEŒLI ==0 PRZERWIJ PETLE printf("Suma wynosi: o/od\n", suma); printf("Srednia wynosi: o/od\n", suma / ile); getch(); Poniżej trochę bardziej elegancka wersja z zastosowaniem pętli typu while. Więcej o pgtlach dowiesz się z Lekcji 9. ~include < stdio.h > ~include < conio.h > int liczba, ile=1, suma=0; void main() char bufor[20]; clrscr(); printf("podaj liczby - ja oblicze SREDNIA i SUMA\n") printf("ZERO = KONIEC\n"); gets(bufor); sscanf(bufor, "o/od", &liczba); while (liczba != 0) str 85 suma += Iiczba; gets(bufor); sscanf(bufor, ""bd", &liczba); if(liczba == 0) printf("I to by bylo na tyle...\n"); else ile+ +; printf("Suma wynosi: "/od\n", suma); printf("$rednia wynosi: "bd\n", suma / ile); getch(); Program powyższy, choć operuje tablicš, robi to trochę jakby za kulisami. Utwórzmy zatem innš - bardziej dydaktycznš tablicę, której elementy byłyby łatwo rozpoznawalne. Dzięki matematyce bardziej jesteœmy przyzwyczajeni do zapisu tablic w takiej postaci: all a12 a13 a14 a15 a16 a21 a22 a23 a24 a25 a26 a31 a32 a33 a34 a35 a36 a41 a42 a43 a44 a45 a46 gdzie a;~ oznacza element tablicy zlokalizowany w: - wierszu i - kolumnie j Przypiszmy kolejnym elementom tablicy następujšce wartoœci: 11 12 13 14 15 16 21 22 23 24 25 26 31 32 33 34 35 36 41 42 43 44 45 46 Jest to tablica dwuwymiarowa o wymiarach 4WIERSZE X 6KOLUMN, czyli krócej 4X6. Liczby będšce elementami tablicy sš typu całkowitego. Jeœli zatem nazwiemy jš TABLICA, to zgodnie z zasadami przyjętymi w języku C/C+ + możemy jš zade- klarować: int TABLICA[4] [6]; Pamiętajmy, że język C liczy nie od jedynki a od zera, zatem TABLICA[0] [0] = a11 = 11, TABLIC,~[2][3] = a34 = 34 itd. Znajšc zawartoœć tablicy możemy jš zdefiniować/zainicjować: int TABLICA[4][6] = {{ 11,12,13,14,15,16},{21,22,23,24,25,26} {31,32,33,34,35,36},{41,42,43,44,45,46}}; Taki sposób inicjowania tablicy, aczkolwiek pomaga wyjaœnić metodę, z punktu 85 str 86 widzenia programistów jest trochę nieelegancki. Liczbę przypisywanš danemu elemen- towi tablicy można łatwo obliczyć. TABLICA[i)[j] = (i+1)*10 + (j+1); Przykładowo: TABLICA[2)[5] = (2+1)*10 +(5+1) = 36 Najbardziej oczywistym rozwišzaniem byłoby napisanie pętli int i, j; for (i=0; i=3; i++) { for (j=0; j<=5; j++) { TABLICA[i][j] = (i+1)*10 + (j+1);} Skorojuż mamy naszš doœwiadczalnš tablicę, to spróbujmy przeœledzić rozmieszczenie elementów tablicy w pamięci i odwołać się do tablicy na kilka sposobów. int TABLICA[4] [6] ={{11,12,13,14,15,16},{21,22,23,24,25,26}, {31,32,33,34,35,36},{41,42,43,44,45,46}}; ~include < stdio.h > ~include i nt * pT; int i, j; void main() clrscr(); printf("OTO NASZA TABLICA\n"); for (i=0; i=3; i++) for (j=0; j<=5; j++) printf("o/od\t, TABLICA[i] [j]); printf("\n"); printf("n\Inicjujemy wskaŸnik na poczatek tablicy\n"); ' printf("i INKREMENTUJEMY wskaŸnik *pT++\n"); pT= &TAB LI CA [0] [0] ; for (i=0; i<4*6; i++) printf("o/od", *(pT+i)); getch () ; 86 str 87 Zwróć uwagę, że jeœli tablica ma wymiary A * B (np. 3 * 4) i składa się z k=A*B elementów, to wjęzyku C zakres indeksów wynosi zawsze 0,1, 2, .....A*B-2, A*B-1. Tak więc tablica 10 x 10 (stuelementowa) będzie składać się z elementów o numerach 0...99, a nie 1...100. ~include ~include int TABLICA[4] [6]; int ` pT; int i, j; void main() clrscr(); printf("Inicjujemy tablice\n"); for (i=0; i<4; i++) for (j = 0; j < 6; j + + ) { TABLICACi][j] = (i+1)~10 + (j+1); } // INDEKS printf("OTO NASZA TABLICA\n"); ~ for (i=0; i<=3; i++) for (j = 0; j < = 5; j + + ) printf("%d\t", TABLICA[i] [j] ); printf("\n"); printf("\n\Inicjujemy wskaŸnik na poczatek tablicy\n"); printf("i INKREMENTUJEMY wskaŸnik `pT++\n"); pT= &TAB LI CA[0] [0] ; for (i=0; i<4~6; i++) printf(""/od", `(pT+i)); getch(); Aby nabrać wprawy, spróbujmy pomanipulować innš tablicš, znanš Ci prawie "od urodzenia" - tabliczkš mnożenia. Jest to kwadratowa tablica 10 x 10, której każdy wyraz opisuje się prostš zależnoœciš T(i,j) = i*j. Jeœli przypomnimy sobie, że indeksy w C zacznš się nie od jedynki a od zera, to zapis ten przybierze w C następujšcš formę: int T[10] [10]; T[i]Cj] = (i+1 )~(j+1 ); Do peMi szczęœcia brak jeszcze wskaŸnika do tablicy: int `pT; i jego zainicjowania pT = &T[0)[0]; str 88 I już możemy zaczynać. Moglibyœmy oczywiœcie zainicjować tablicę "na piechotę", ale to i nieeleganckie, i pracochłonne, i o pomyłkę łatwiej. Pamiętaj, że komputer myli się rzadziej niż programista, więc zawsze lepiej jemu zostawić możliwie jak najwięcej roboty. #include #include int T[10] [10]; int *pT; int i, j, k; char spacja = , void main() clrscr(); printf("t\TABLICZKA MNOZENIA (ineksy)\n"); for (i=0; i<10; i++) for (j = 0; j < 10; j + + ) { T[i][j] = (i+1 )'"(j+1 ); if (T[i] [j] < 10) printf("o/odo/oc", T[i][j], spacja); else printf("o/od", T[i] [j] ); printf("\n"); printf("n\Inicjujemy i INKREMENTUJEMY wskaŸnik *pT++\n\n"); pT= &TCO] [0] ; for (k=0; k<10*10; k++) if (*(pT+k) < 10) printf("o/odo/oc", *(pT+k) , spacja); else printf("o/od", *(pT+k)); if ((k+1)o/o10 == 0) printf("\n"); getch(); II } Po wynikachjednocyfrowych dodajemy trzy spacje a po dwucyfrowych dwie spacje. Po dziesięciu kolejnych wynikach trzeba wstawić znak nowego wiersza. Sprawdzamy te warunki : if (*(pT+k) < 10) - jeœli wynik jest mniejszy niż 10.. str 89 lub if (T[i] [j] < 10); if ( (k+ 1 ) "/o 10 = = 0) - jeœli k jest całkowita wielokrotnoœcia 10, czyli - jeœli reszta z dzielenia równa się zero... Zastosowane w powyższych programach nawiasy klamrowe {} spełniajš rolę IN- STRUKCJI GRUPUJĽCEJ i pozwalajš podobnie jak para BEGIN...END w Pascalu zamknšć w pętli więcej niż jednš instrukcję. Instrukcje ujęte w nawiasy klamrowe sš traktowane jak pojedyncza instrukcja prosta. Tablice mogš zawierać liczby, ale mogš zawierać także znaki. Przykład prostej tablicy znakowej zawiera program [P-24]. ~include ~include < conio.h > char T[7] [12) ={"Poniedziałek", "Wtorek", "$roda", "Czwartek", "Piatek", "Sobota", "Niedziela"}; char *pT; int i, j, k; char spacja = ' ; void main() clrscr(); pT = &TCO] [0] ; printf("\t TABLICA znakowa (ineksy)\n\n"); for (i=0; i<7; i++) for (j=0; j<12; j++) printf(""/oc", T[i] [j] ); printf("\n"); printf("\n\t Przy pomocy wskaŸnika \n\n"); for (k=0; k<7*12; k++) printf("%d", *(pT+k) ); //TU! - opis w tekœcie if ((k+1)"/o12 == 0) printf("\n"); getch(); Nazwy dni majš różnš długoœć, czym więc wypełniane sš puste miejsca w tablicy? Jeœli w miejscu zaznaczonym komentarzem //TU! zmienisz format z printf(""/oc", *(pT+k) ); na printf("%d", *(pT+k) ); uzyskasz zamiast znaków kody ASCII. 89 str 90 TABLICA znakowa (ineksy) Poniedziałek Wtorek Œroda Czwartek Pištek Sobota Niedziela Przy pomocy wskaŸnika: 80111 110105101 100122105 97108101 107 87116111 114101 107000000 83114111 100970000000 67122119 97114116101 107 0 0 0 0 8010597116101107000000 83111 98111 11697000000 78105101 100122105101 108 97 0 0 0 Okaże się, że puste miejsca zostały wypełnione zerami. Zern w kodzie ASCII - NUL - '\0'jest znakiem niewidocznym, nie było więc widoczne na wydruku w formie znakowej printf("%c"...). 8.1. Posługujšc się wskaŸnikiem i inkrementujšc wskaŸnik z różnym krokiem - np. pT + = 2; pT + = 3 itp., zmodyfikuj programy [P-23], [P-24] tak, by uzyskać wydruk tylko częœci tablicy. 8.2. Spróbuj zastšpić inkrementację wskaŸnika pT+ + dekrementacjš, odwracajšc tablicę "do góry nogami". Jak należałoby poprawnie zainic- jować wskaŸnik? 8.3. Napisz program drukujšcy tabliczkę mnożenia w układzie szesnastkowym - od 1 * 1 do F*F. 8.4. Wydrukuj nazwy dni tygodnia pionowo i wspak. 8.5. Zinterpretuj następujšce zapisy: int *pt int; float "pt float; int p = 7, d = 27; float x = 1.2345, Y = 32.14; void *general; ! pt~int = &p; *pt~int += d general = pt int; pt float = &x; Y += 5 * ('pt float); str 91 general = pt float; const char "name1 = "Jasio"; //wskaŸnik do STALEJ char "const name2 = "Grzesio"; //wskaŸnik do STALEGO ADRESU 91 str 92 Lekcja 9 Jak tworzyć w programie pgtle i rozgałgzienia W trakcie tej lekcji: 1. Dowiesz się znacznie więcej o pętlach. 2. Przeanalizujemy instrukcje warunkowe iformulowanie warunków. Zaczniemy tę lekcję nietypowo - od słownika, ponieważ dobrze jest rozumieć dokładnie co się pisze. Do organizacji pętli będš nam potrzebne następujšce słowa: if - jeżeli (poprzedza warunek do sprawdzenia); else - a jeœli nie, to (w przeciwnym wypadku...); for - dla; while - dopóki (dopóki nie spełnimy warunku); do - wykonaj, wykonuj; break - przerwij (wykonanie pętli); switch - przełšcz; case - przypadek, wariant ~edna z możliwoœci); goto - idŸ do... default - domyœlny, (automatyczny, pozostały); continue - kontynuuj (pętlę); UWAGA: W C/C + + nie stosuje się słowa THEN PĘTLA TYPU FOR. Ponieważ pozwalam sobie liczyć na to, że język C nie jest Twoim pierwszym językiem programowania, w kilku przy- kładowych programach zastosowałemjuż pętlę for w najbardziej typowej formie, maksymalnie zbliżonej do Pascala i Basica. Pora jednak zwrócić Ci uwagę, że w "C" pętla for może działać również trochę inaczej. Jest to jeden z najprostszych przykładów demonstrujšcy elastycznoœć języka "C". Ogólna postać pętli for jest następujšca: for (W inicjujšce; W logiczne; W kroku) Instrukcja; gdzie skrót W~ oznacza wyrażenie. Każde z tych wyrażeń może zostać pominięte (patrz przykład { * * } ). Wykonanie pętli for przebiega następujšco: 1. Wykonanie JEDEN raz WYRAŻENIA INICJUJĽCEGO 2. Obliczenie wartoœci LOGICZNEJ wyrażenia logicznego. 92 str 93 . Jeœli W logiczne ma wartoœć PRAWDA (TRUE) nastšpi wykonanie Instrukcji. 4. Obliczenie wyrażenia kroku. 5. Powtórne sprawdzenie warunku - czy wyrażenie logiczne ma wartoœć różnš od zera. Jeœli wyrażenie logiczne ma wartoœć zero, nastšpi zakończenie pętli. Warunek jest testowany PRZED wykonaniem instrukcji. Jeœli zatem nie zostanie spełniony warunek, instrukcja może nie wykonać się ANI RAZU. Instrukcja może być INSTRUKCJĽ GRUPUJĽCĽ, składajšcš się z instrukcji prostych, deklaracji i definicji zmiennych lokalnych: { cišg deklaracji lub definicji; cišg instrukcji; } Ogromnie ważny jest fakt, że język "C" ocenia wartoœć logicznš wyrażenia wedhzg zasady: 0 - FALSE, FAŁSZ, inaczej ZERO LOGICZNE jeœli WYRAŻENIE = = 0 lub jest fałszywe w znaczeniu logicznym; 1 - TRUE, PRAWDA, JEDYNKA LOGICZNA, jeœli wyrażenie ma DOWOLNĽ WARTOŒĆ NUMERYCZNĽ RÓŻNĽ OD ZERA (!) lub jest prawdziwe w sensie logicznym. Przykład "Klasycznie" zastosowana pętla for oblicza pierwiastki kwadratowe kolejnych liczb całkowitych. ~include < math.h > ~include void main() { int n; for (n=0; n<=100; n++) printf(""/of\t", sqrt(n)); getch(); Wyrażenie inicjujšce może zostać pominięte. Innymi słowy zmienna może zostać zainicjowana na zewnštrz pętli, a pętla przejmie jš takš jaka jest w danym momencie. Przykładowo: float n; n=(2*3)/(3*n*n -1.234); for (; n<=100; n++) printf(""/of4.4\t", sqrt(n)); Przykład Warunek przerwania pętli może mieć także inny charakter. W przykładzie pętla zostanie przerwana, jeœli różnica pomiędzy kolejnymi pierwiastkami przekroczy 3.0. 93 str 94 void main() float y=0, n=0; for (; (sqrt(n)-y)=3.0; n++) { y=sqrt(n); printf(%f2.3t, y); getch(); UWAGA: SprawdŸ, czy nawias (sqrt(n)-y) < = 3 można pominšć? Jaki jest priorytet operatorów w wyrażeniach: (sqrt(n)-y) < =3.0 i sqrt(n)-y< =3.0 Jaki będzic wynik? Dlaczego? Przykład Instrukcja stanowišca ciało pętli może być instrukcjš pustš a wszystkie istotne czynnoœci mogš zostać wykonane w ramach samego "szkieletu" for. Program przy- kładowy sprawdza ile kolejnych liczb całkowitych trzeba zsumować by uzyskać sumę nie mniejszš niż tysišc. void main() { float SUMA=0, n=0; for (; SUMA < 1000; SUMA+=(++n)); printf(""/of", n); getch(); 1 CZY NIE MOŻNA JAŒNIEJ ??? Można, ale po nabraniu wprawy takie skróty pozwolš Ci przyspieszyć tworzenie programów. Zmniej- szenie wielkoœci pliku tekstowego jest w dzisiejszych czasach mniej istotne. Rozszyfrujmy zapis SUMA+ =(+ +n). Preinkrementacja następuje PRZED użyciem zmiennej n, więc: 1. Najpierw + + n, czyli n = n ~ 1. 2. Potem SUMA=SUMA+ (n+ 1). Dla wyjaœnienia przedstawiam dwie wersje (obie z pętlš for): void main() { void main() float SUMA=0; { float SUMA=0, n=0; int n; for (; SUMA < 1000; SUMA+=(++n)); } clrscr(); for (n=0; SUMA=1000; n++) SUMA=SUMA+n; 94 str 95 Przykład Tojeszcze nie koniec pokażu elastycznoœci C. W pętli for wolno nam umieœcić więcej niż jedno wyrażenie inicjujšce i więcej niżjedno wyrażenie kroku oddzielajšcje przecinkami. flat a, b, c; const float d=1.2345; void main() { for (a=5,b=3.14,c=10; c; ++a,b*=d,c--) printf("n"/of\t"/of\t"/of", a,b,c); getch(); } Zwróć uwagę, że zapisy warunku: if (c)...; i if (c != 0)...; sš w języku C równoważne. Przykład Jeszcze jeden kwiatek. Program będzie pisał kropki aż do naciœnięcia dowolnego klawisza, co wykryje funkcja kbhit(), będšca odpowiednikem KeyPressed w Pascalu. Zapis !kbhit() oznacza "NIE NACIŒNIĘTO KLAWISZA", czyli w buforze klawiatury nie oczekuje znak. Zwróć uwagę, że funkcja getch() może oczekiwać na klawisz w nieskończonoœć. Aby uniknšć kłopotliwych sytuacji, czasem znacznie wygodniej jest zastosować kbhit(), szczególnie, jeœli czekamy na DOWOLNY klawisz. void main() for (; !kbhit(); printf(".")); Przykład . WskaŸnik w charakterze zmiennej roboczej w pętli typu for. Pętla powoduje wypisanie napisu. char *Ptr = "Jakis napis" void main() for (; (*Ptr) ;) printf(""/oc",* Pt+ + ) ; getch(); A teraz przykład programu do zabawy. Pętla for służy do wykrywania zgodnoœci klawisza z elementami tablicy TABL[]. W tablicy D[] umieszczone zostały częstotliwoœci 95 str 96 kolejnych dŸwięków, które program oblicza sam, wykorzystujšc przybliżony współczyn- nik. ~ include "conio.h" ~ include "dos.h" ~ include "math.h" ~ include "stdio.h" char TABL[27]={"zsxdcvgbhnjm,ZSXDCVGBHNJM<"}; char k; float D [26) ; int i; void main() { clrscr(); printf("[A]- KONIEC, dostepne klawisze: \n"); printf("ZSXDCVGBHNJM,i [Shift]"); D [0] = 200.0; for(i=1; i<26; i++) D[i]=D[i-1]*1.0577; for (") //patrz przyklad {"} k = getch(); for(i=0; i<27; i++) { if (k==TABL[i]) { sound(D[i)); delay(100); nosound(); } if (k=='a' k=='A') break; //Wyjœcie z pętli. k = '0'; Po uruchomieniu programu klawisze działajš w sposób przypominajšcy prosty klawiszowy instrument muzyczny. Jak widzisz, konstrukcje języka "C" sš na tyle elastyczne i na tyle uniwersalne, że możnaby się zastanawiać, czy oprócz tego, co już poznałeœ potrzeba jeszcze czegoœ by zrealizować dowolny algorytm. Niestety potrzeba, przechodzimy więc do następnej pętli. PĘTLA TYPU WHILE. Pętlę typu while stosuje się na ogół "do skutku", tj. do momentu spełnienia warunku, zwykle wtedy, gdy nie jesteœmy w stanie przewidzieć potrzebnej iloœci cykli. Konstrukcja pętli while wyglšda następujšco: while (Wyrażenie logiczne) Instrukcja; Jeœli Wyrażenielogiczne ma wartoœć różnš od zera, to zostanie wykonana Instrukcja. Sprawdzenie następuje PRZED wykonaniem Instrukcji, toteż Instrukcja może nie zostać wykonana ANI RAZU. Instrukcja może być INSTRUKCJĽ GRUPUJĽCĽ. 96 str 97 Przykład Stosujemy pętlę while do programu piszšcego kropki (patrz wyżej). void main() while (!kbhit()) printf("."); Przykład Stosujemy pętlę while w programie obliczajšcym sumę. void main ( ) { float SU MA=0, n =0; clrscr(); while (SUMA < 1000) SUMA+=(++n); printf("SUMA: o/o4.Of ostatnia liczba: %3.Of", SUMA, n); getch(); char "Pointer1="Koniec napisu to \0, "Pointer==0" ; char "Pointer2="Koniec napisu to \0, *Pointer==0" ; void main(){ clrscr(); while (* Pointerl ) printf("o/oc", *Pointer1 ++); printf("n\Zobacz ten NUL na koncu lancucha znakow\n"); while (" Pointer2) printf("o/oc", "Pointer2++); printf("o/od", *Pointer2); getch ( ) ; PĘTLA DO...WHILE. Konstrukcja dwuczłonowa do...while tworzy pętlę, która: * jest wykonywana zawsze CO NAJMNIEJ JEDEN RAZ, ponieważ warunek jest sprawdzany nie na wejœciu do pętli, a na wyjœciu z pętli; * przerwanie pętli powodowane jest przez NIESPEŁNIENIE WARUNKU. Schemat pętli do...while jest następujšcy: do Instrukcja while (Wyrażenie logiczne); Instrukcja może być instrukcjš grupujšcš. Przykład void main () str 98 do {printf(".");} while (!kbhit()); printf("Koniec petli...."); INSTRUKCJA WARUNKOWA if, if...else i else...if.. Instrukcja warunkowa ma postać: if (Wyrażenie) Instrukcja; if(Wyrażenie) Instrukcjal; else Instrukcja2; Jeœli Wyrażenie ma wartoœć różnš od zera (LOGICZNĽ bšdŸ NUMERYCZNĽ !) to zostanie wykonana Instrukcjal, w przeciwnym razie wykonana zostanie Instrukcja2. Instrukcje mogš być instrukcjami grupujšcymi. Słowa kluczowe if i else mogš być stosowane wielokrotnie. Pozwala to tworzyć np. tzw. drzewa binarne. Przykład void main() float a; scanf(""/of", &a); if (a < 0) printf("Ujemna!"); else if (a==0) printf("Zero!"); else printf("Dodatnia!"); Przykład if (a>0) if (a<100) printf("Dwucyfrowa"); else printf("100+"); inaczej : if(a > 0) {if(a < 100) printf("Dwucyfrowa"); else printf("100+");} Wyrażenie może zawierać operatory logiczne: if (a > 0 && a < 100) printf("Dwucyfrowa"); else printf(" 100 + "); Zapis 100+ oznacza "sto i więcej". Przykład Język C pozwala na krótszy zapis instrukcji warunkowej (a > b)? MAX = a : MAX = b; inaczej if (a > b) MAX = a; else MAX = b; 98 str 99 INSTRUKCJA BREAK. Instrukcja break powoduje natychmiastowe bezwarunkowe opuszczenie pętli dowol- nego typu i przejœcie do najbliższej instrukcji po zakończeniu pętli. Jeœli w pętli for opuœcimy wyrażenie logiczne, to zostanie automatycznie przyjęte 1. Pętla będzie zatem wykonywana bezwarunkowo w nieskończonoœć. W przykładzie poniżej nieskończonš pętlę przerywa po podaniu z kalwiatury zera instrukcja break. Przykład {*} float a, sigma=0; void main(){ for (") printf("\n Podaj liczbe do sumowania\n"); scanf(""/of", &a); if (a==0) break; sigma+=a; printf("\n SUMA: %f", sigma); printf("Nastapil BREAK"); getch ( ) ; INSTRUKCJA CONTINUE. Instrukcja continue powoduje przedwczesne, bezwarunkowe zakończenie wykonania wewnętrznej instrukcji pętli i podjęcie próby realizacji następnego cyklu pętli. Próby, ponieważ najpierw zostanie sprawdzony warunek kontynuacji pętli. Program z przykładu poprzedniego zmodyfikujemy w taki sposób, by * jeœli liczba jest dodatnia - dodawał jš do sumy sigma; * jeœli liczba jest ujemna - nie robił nic, pomijał bieżšcš pętlę przy pomocy rozkazu continue; (Ponieważ warunek wejœciowy pętli jest zawsze spełniony, to pętlę zawsze uda się kontynuować.) * jeœli liczba równa się zero - przerywał pętlę instrukcjš break Przykład float a, sigma=0; void main() for (") printf("n\Sumuje tylko liczby dodatnie\n"); scanf(""/of", &a); if (a<0) continue; if (a= =0) break; 99 str 100 sigma+ =a; printf("\n SUMA: %f", sigma); printf("Nastapil BREAK"); getch ( ) ; INSTRUKCJA SWITCH. Instrukcja switch dokonuje WYBORU w zależnoœci od stanu wyrażenia przełšczajšce- go (selector) jednego z możliwych przypadków - wariantów (case). Każdy wariant jest oznaczony przy pomocy stałej - tzw. ETYKIETY WYBORU. Wyrażenie przełšczajšce może przyjmować wartoœci typu int. Ogólna postać istrukcji jest następujšca: switch (selector) case STAŁA1: Cišg~instrukcji-wariant 1; case STAŁA2: Cišg~instrukcji-wariant 2; case STAŁAn Cišg instrukcji-wariant n; default : Ostatni cišg~instrukcji; Należy podkreœlić, że po dokonaniu wyboru i skoku do etykiety wykonane zostanš również WSZYSTKIE INSTRUKCJE PONIŻEJ DANEJ ETYKIETY. Jeœli chcemy tego uniknšć, musimy dodać rozkaz break. # define pisz printf //dla przypomnienia # include void main() int Numer~Dnia; pisz("n\ Podaj numer dnia tygodnia\n"); scanf("o/od", &Numer~Dnia); switch(Numer~Dnia) case 1: pisz("PONIEDZIALEK."); case 2: pisz("WTOREK"); case 3: pisz("SRODA."); case 4: pisz("CZWARTEK."); case 5: pisz("PIATEK."); case 6: pisz("SOBOTA."); case 7: pisz("NI EDZI ELA."); default: pisz("\n ' 100 str 101 Zwróć uwagę, że w przykładzie [P-27) wariant default zostanie wykonany ZAWSZE, nawet jeœli podasz liczbę większš niż 7. ~ define pisz printf ~ include void main() int Numer~Dnia; pisz("n\Podaj numer dnia tygodnia\n"); scanf(""/od", &Numer~Dnia); switch(Numer~Dnia) case 1: pisz("PON."); break; case 2: pisz("WTOR"); break; case 3: pisz("SRO."); break; case 4: pisz("CZW."); break; case 5: pisz("PIO."); break; case 6: pisz("SOB."); break; case 7: pisz("NIEDZ."); break; default: pisz(\n ?????); Instrukcja break przerywa wykonanie. Wariant default zostanie wykonany TYLKO w przypadku podania liczby większej niż 7. INSTRUKCJA POWROTU RETURN Służy do zakończenia wykonania zawierajšcej jš funkcji i może mieć postać: return; return stała; return Wyrażenie; Przykład DeFniujemy funkcję dodaj() zwracajšcš, poprzez instrukcję return wartoœć przekaza- nego jej w momencie wywołania argumentu powiększonš o 5. float dodaj (float x) x+=5; return x; Funkcja dodaj() zwraca wartoœć i nadaje tę wartoœć zmiennej wynik zadeklarowanej nazewnštrz funkcji i znanej w programie głównym. A oto program w całoœci. 101 str 102 float funkcja dodaj(float x) x+=5; return x; float dana = 1, wynik = 0; void main() clrscr(); wynik = funkcjadodaj(dana) printf(""/of", wynik); INSTRUKCJA SKOKU BEZWARUNKOWEGO GOTO I ETYKIETY Składnia instrukcji skoku goto jest następujšca: goto Identyfikator etykiety; UWAGA: Po każdej etykiecie musi wystšpić CO NAJMNIEJ JEDNA INSTRUKCJA. Jeœli etykieta oznacza koniec programu, to musi po niej wystšpić instrukcja pusta. Instrukcja goto nie cieszy się powodzeniem ani dobrš sławš (niesłusz- nie!). Ostrożne i umiejętne jej stosowanie jeszcze nikomu nie zaszkodziło. Należy tu zaznaczyć, że etykieta nie wymaga deklaracji. Przykład Program generuje dŸwięki i odlicza. ~ include < dos.h > ~ include int main() { int czestotliwosc=5000, n=10, milisekundy=990; printf("\n"); start: sound(częstotliwoœć); delay(milisekundy); nosound(); czestotl iwoœć/ =1. 2; printf(""/od", --n); if (n) goto start; //pętle strukturalne zrób sam(a) 102 str 103 return 0; koniec: ; } // Tu jest instrukcja pusta sound - dŸwięk; delay - opóŸnienie, zwłoka; nosound - bez dŸwięku (wyłšcz dŸwięk); 9.1. Bioršc pod uwagę, że iloraz częstotliwoœci kolejnych dŸwięków jest stały tzn. Fcis/Fc=Ffis/Ff=....=const oraz, że oktawa to podwojenie częstotliwoœci, opracuj program i oblicz częstotliwoœci poszczególnych dŸwięków. 9.2. Spróbuj zastosować w programie [P-28] kolejno pętle for, while, do...while. 9.3. Zastosuj w programie [P-28) instrukcję switch. 103 str 104 Lekcja 10 Jak tworzyć i stosować struktury W trakcic tej lekcji poznasz pojęcia: * Klasy ~rniennej. * Straktwy. * Pola bitowego. * Un. Dowiesz się taJEże więcej o operacjach logiczrrych. CO TO JEST KLASA ZMIENNEJ. W języku C i C + + programista ma większy wpływ na rozmieszczenie zmiennych w pamięci operacyjnej komputera i w rejestrach mikro- procesora. Może to mieć decydujšcy wptyw na dostępnoœć danych z różnych miejsc programu i szybkoœć działania programu. Należy podkreœlić, że TYP ZMIENNEJ (char, int, float itp.) decyduje o sposobie interpretacji przechowywanych w pamięci zer i jedynek, natomiast KLASA ZMIENNEJ decyduje o sposobie przechowywania zmiennej w pamięci. W C+ + występujš cztery klasy zmiennych. ZMIENNE STATYCZNE - static. Otrzymujš stałš lokalizację w pamięci w momencie uruchamiania programu. Za- chowujš swojš wartoœć przez cały czas realizacji programu, chyba, że œwiadomie zażšdamy zmiany tego stanu - np. instrukcjš przypisania. Przykład deklaracji: static float LICZBA; Zmienne statyczne, które nie zostały jawnie zainicjowane w programie, otrzymujš po zadeklarowaniu wartoœć ZERO. ZMIENNE AUTOMATYCZNE - auto. Otrzymujš przydział miejsca w pamięci dynamicznie - na stosie procesora, w momen- cie rozpoczęcia wykonania tego bloku programu, w którym zmienne te zostały zadeklarowane. Przydzielenie pamięci nie zwalnia nas z obowišzku zainicjowania zmiennej (wczeœniej wartoœć zmiennej jest przypadkowa). Zmienne automatyczne "znikajš" po zakończeniu wykonywania bloku. Pamięć im przydzielona zostaje zwol- niona. Przykład: auto long SUMA; 104 str 105 ZMIENNE REJESTROWE - register. Zmienne rejestrowe sš także zmiennymi lokalnymi, widocznymi tylko wewnštrz tego bloku programu, w którym zostały zadeklarowane. Turbo C+ + może wykorzystać dwa szesnastobitowe rejestry mikro- procesora - DI i SI do przechowywania zmiennych. Jeœli zadeklarujemy w programie wigcej zmiennychjako zmienne rejestrowe - zostanš one umieszczone na stosie. Znaczne przyspieszenie działania programu powoduje wykorzystanie rejestru do przechowywania np. licznika pętli. Przykład: register int i; for (i=1; i<1000; i++) {.....} ZMIENNE ZEWNĘTRZNE - extern. Jeœli zmienna została - raz i TYLKO RAZ - zadeklarowana w pojedynczym segmencie dużego programu, zostanie w tymże segmencie umieszczona w pamięci i potraktowana podobnie do zmiennych typu static. Po zastosowaniu w innych segmentach deklaracji extern zmienna ta może być dostępna w innym segmencie programu . Przykład: extern int NUMER; STRUKTURY. Poznane wczeœniej tablice mogš zawierać wiele danych, ale wszystkie te dane muszš być tego samego typu. Dla zgrupowania powišzanych ze sobš logicznie danych różnego typu Pascal stosuje rekordy, a C/C + + - STRUKTURY, deklarowane przy pomocy słowa struct. Kolejne pola struktury sš umieszczane w pamięci zgodnie z kolejnoœciš ich deklarowania. Strukturę, podobnie jak zmiennš, MUSIMY ZADEKLAROWAĆ. Struktura jest objektem bardziej złożonym niż pojedyncza zmienna, więc i deklaracja struktury jest bardziej skomplikowana. Deklaracja struktury składa się z następujšcych elementów: 1. Słowo kluczowe struct (obowišzkowe). 2. Nazwa (opcjonalna). Jeœli podamy nazwę, to nazwa ta będzie oznaczać dany typ struktury. 3. Nawias klamrowy { 4. Deklaracje kolejnych składników struktury. 5. Nawias klamrowy } 6. Lista nazw struktur okreœlonego powyżej typu (może zostać zadeklarowana oddziel- nie). Przykład. Deklaracja ogólnego typu struktury i okreœlenie wewnętrznej postaci struktury struct Ludzie { char Imiona[30]; char Nazwisko[20]; int wiek; char pokrewienstwo [10] }; 105 str 106 Jeœli okreœlimyjuż typ struktury - ezyli rodzaj, wielkoœć i przeznaczenie poszczególnych pól struktury, możemy dalej tworzyć - deklarować i inicjować konkretne struktury danego typu. Przykład. Deklaracja następnych pochodnych struktur tego samego typu. struct Ludzie Moi, Twoi, Jego, Jej, Szwagra; Deklarację struktury pierwotnej i pochodnych, lub innymi słowy pierwszej i następnych, można połšczyć . Przykład. Połšczona deklaracja struktur. struct Ludzie { char pokrewienstwo[10]; char Imiona[30]; int wiek; } Moi, Twoi, Szwagra; Struktury statyczne * majš stałe miejsce w pamięci w trakcie całego programu; * sš widoczne i dostępne w całym programie. Zadeklarujemy teraz typ struktury i zainicjujemy dwie struktury. Przykład. Zainicjowanie dwu struktur statycznych. struct Ludzie { char pokrewienstwo[10]; char Imiona[30); int wiek; } struct Ludzie Moi, Szwagra; static struct Ludzie Moi = { "Stryjek", "Walenty", 87 }; static struct Ludzie Szwagra = { "ciotka", "Ala", 21 }; Zapis static struct Ludzie Szwagra oznacza: statyczna struktura typu "Ludzie" pod nazwš "Szwagra". Do struktury w całoœci możemy odwoływać się za pomocš jej nazwy a do poszczególnych elementów struktury poprzez nazwę struktury i nazwę pola struktury - ROZDZIELO- NE KROPKĽ ".". Zademonstrujmy to na przykładzie. Zwróć uwagę na róŸne sposoby przekazywania danych pomiędzy strukturami: C4.Wiek = Czlowiek2.Wiek; - przekazanie zawartoœci pojedynczego pola numerycz- nego; C4 = Czlowiek3; - przekazanie zawartoœci całej struktury Czlowiek3 do C4. Przykład. Program manipulujšcy prostš strukturš. 106 str 107 int main() struct Ludzie char Imie[20]; int Wiek; char Status[30]; char TeI~Nr[10] ; static struct Ludzie Czlowiek1={"Ala", 7, "Ta, co ma Asa","?"}, Czlowiek2={"Patrycja",13, "Corka", "625276"}, Czlowiek3={"Jan Krzysztof", 47, "Kolega z partii", "23478"}; struct Ludzie C4, C5; C4=Czlowiek3; C4 .Wiek= Czlowiek2.Wiek; C5=Czlowiek1; clrscr(); printf("o/os o/od o/os\n", C4.Imie, C4.Wiek, C4.Status); printf("o/os o/os", C5.Imie, C5.Status); return 0; Tablice mogš być elementami struktur, ale i odwrotnie - ze struktur, jak z cegiełek można tworzyć konstrukcje o wyższym stopniu złożonoœci - struktury struktur i tablice struktur. Jeœli tablica składa się z liczb typu int, to deklarujemy jš: int TABLICA[10]; jeœli tablica składa się ze struktur, to deklarujemy jš: struct TAB LI CA [50] ; W przykładzie poniżej przedstawiono * deklarację jednowymiarowej tablicy LISTA[50), * elementami tablicy sš struktury typu SCzlowiek, * jednym z elementów każdej struktury SCzlowiek jest struktura "niższego rzędu" typu Adres; 107 str 108 int main() struct Adres char Ulica [30] ; int Nr~Domu; int Nr~Mieszk; struct SCzlowiek char Imie[20]; int Wiek; struct Adres Mieszkanie; struct S Czlowiek LISTA [50] ; LISTA[1].Wiek=34; LISTA[1 ].Mieszkanie.Nr~Domu=29; printf(""/od", LISTA[1 ].Mieszkanie.Nr~Domu); return 0; Zapis printf(""/od", LISTA[1 ) .Mieszkanie.Nr~Domu oznacza: * wybierz element nr 1 z tabliCy LISTA; (jak wynika z deklaracji tablicy, każdy jej element będzie miał wewnętrznš strukturę zorganizowanš tak, jak opisano w deklaracji struktury SCzlowiek); * wybierz ze struktury typu SCzlowiek pole Mieszkanie; (jak wynika z deklaracji, pole Mieszkanie będzie miało wewnętrznš organizację zgodnš ze strukturš Adres); * ze struktury typu Adres wybierz pole Nr Domu; * Wydrukuj zawartoœć pola pamięci interpretujšc jš jako liczbę typu int - w formacie %d. Słowo struktura tak doskonale pasuje, że chciałoby się powiedzieć: jeœli struktura struktur jest wielopoziomowa, to podobnie, jak przy wielowymiarowych tablicach, każdy poziom przy nadawaniu wartoœci musi zostać ujęty w dodatkowš parę nawiasów klamrowych. I08 str 109 A CO Z ŁAŃCUCHAMI ZNAKOWYMI ? Język C + + oferuje do kopiowania łańcuchów znakowych specjalnš funkcję strcpy(). Nazwa funkcji to skrót STRing CoPY (kopiuj łańcuch). Sposób wykorzystania tej funkcji: strcpy(Dokšd, Skšd); lub strcpy(Dokšd, "łańcuch znaków we własnej osobie"); Przykład - patrz [P-31]. STRUKTURY I WSKANIKI WskaŸniki mogš wskazywać strukturę w całoœci lub element struktury. Język C oferuje specjalny operator - który pozwala na odwoływanie się do elementów struktury. W przykładzie poniżej przedstawiono różne sposoby odwołania się do elementów trzech identycznych struktur STA, STB, STC. int main() struet char Tekst [20] ; int Liczba1; float Liczba2; } STA, STB, STC, *Pointer; STA.Liczba1 = 1; STA.Liczba2 = 2.2; strcpy(STA.Tekst, "To jest tekst"); ST B = STA; Pointer = &STC; Pointer- > Liczba1 =1; Pointer- > Liczba2 = 2.2; strcpy(Pointer- > Tekst, STA.Tekst); printf("\nLiczba1-STA Liczba2-STB Tekst-STC\n\n"); printf(""/od\t", STA.Liczbal ); printf(""/of\t", STB.Liczba2); printf(""/os", Pointer- > Tekst) ; return 0; Rozszyfrujmy zapis: strcpy(Pointer- > Tekst, STA.Tekst) Skopiuj łańcuch znaków z pola Tekst struktury STA do pola Tekst struktury wskazywa- nej przez pointer. Prawda, że to całkiem proste? 109 str 110 CZY MUSIMY TO ROZDZIELAĆ ? Jak zauważyłeœ, liczby mog- libyœmy zapisywać także jako łańcuchy znaków, ale wtedy nie moglibyœ- my wykonywać na tych liczbach działań. Konwersję liczba - łańcuch znaków lub odwrotnie łańcuch znaków - liczba wykonujš w C specjalne funkcje np.: atoi() - Ascii TO Int.; itoa() - Int TO Ascii itp. Więcej informacji na ten temat i przykłady znajdziesz w dalszej częœci ksišżki. PODSUMUJMY... Struktura jest zbiorem logicznie powišzanych zmiennych różnych typów. Deklaracja struktury (oznacznik - cytuję za Leksykonem języka C) jest zbliżona do definiowania nowego typu. Strukturę można okreœlić na jeden z trzech sposobów: 1. struct oznacznik {lista deklaracji} 2. struct {lista deklaracji} nazwa zmiennej 3. struct oznacznik {lista deklaracji} nazwa zmiennej Elementami struktury mogš być zmienne dowolnego typu, łšdznie z innymi struk- turami . Ciekawostka: WskaŸnik do deklarowanej struktury może być wjęzyku C zastosowany jako jeden zjej WŁASNYCH elementów. Jeœli wskaŸnik wchodzšcy w skład struktury wskazuje na WŁASNĽ strukturę, to nazywa się to AUTOREFERENCJĽ STRUKTURY. POLA BITOWE. Często zdarza się, że jakaœ zmienna ma zawężony zakres wartoœci. Dla przykładu zmienne logiczne (tzw. flagi) to zawsze tylko 0 lub 1. Wiek rzadko przekracza 255 lat a liczba dzieci zwykle niejest większa niż 15. Nawet najbardziej niestali panowie nie zdšżš ożenić się i rozwieœć więcej niż 7 razy. Gdybyœmy zatem chcieli zapisać informacje * płeć 0 - mężczyzna, 1 - kobieta ( 1 bit ); * wiek 0 - 255 lat (8 bitów); * iloœć dzieci 0 - 15 (4 bity); * kolejny numer małżeństwa 0 - 7 (3 bity); to przecież wszystkie te informacje mogš nam się zmieœcić w jednym szesnastobitowym rejestrze lub w dwu bajtach pamięci. Takie kilka bitów wydzielone i majšce okreœlone znaczenie to właœnie pole bitowe. Turbo C + + pozwala także na uwzględnianie znaku w polach bitowych. Pola bitowe mogš być typu int i unsigned int (czyli takie jak w przykładzie poniżej). Jeœli jakieœ dane chcemy przechowywać w postaci pola bitowego, to w deklaracji struktury sygnalizujemy to dwukropkiem. Stwarza to dwie istotne możliwoœci: * bardziej ekonomicznego wykorzystania pamięci; * łatwego dodatkowego zaszyfrowania danych. 110 str 111 //Pamietaj o dolaczeniu plikow naglowkowych ! int main() struct USC { int Sex :1; unsigned Wiek : 8; unsigned Dzieci : 4; unsigned Ktora : 3; } Facet; int bufor; clrscr(); Facet.Sex = 0; printf("\n Ile ma lat ? : "); scanf("%d, &bufor); Facet.Wiek = bufor; printf("\n Ktore malzenstwo ? : "); scanf("%d", &bufor); Facet.Ktora = bufor; printf("\n Ile dzieci ? : "); scanf("%d, &bufor); Facet.Dzieci = bufor; printf("\n\n"); if (Facet.Ktora) printf("Facet ma "/od zone", Facet.Ktora); printf("\nPlec: Dzieci: Wiek (lat): \n\n"); printf(""/od\t%d\t"/od", Facet.Sex, Facet.Dzieci, Facet.Wiek); getch(); return 0; Uruchom program i sprawdŸ co się stanie, jeœli Facet będzie miał np. 257 lat lub 123 żonę. Przekroczenie zadeklarowanego zakresu powoduje obcięcie częœci bitów. Aby uzyskać "wyrównanie" pola bitowego do poczštku słowa należy przed in- teresujšcym nas polem bitowym zdefiniować tzw. pole puste: * pole bitowe bez nazwy; * długoœć pola pustego powinna wynosić 0. Poniżej przedstawiam przykład pola bitowego zajmujšcego trzy kolejne słowa 16 bitowe. Dodanie pola pustego wymusza rozpoczęcie pola pole IV od poczštku trzeciego słowa maszynowego (zakładamy, że pracujemy z komputerem 16 bitowym). struct unsigned pole~l :4; unsigned pole~ll:10; unsig,ed pole~lll:4; unsigned :0; /* to jest pole puste */ unsigned pole~lV:5; } pole~przykladowe; Zwróć uwagę, że częœć bitów w drugim i trzecim słowie maszynowym nie zostanie wykorzystana. 111 str 112 UNIE czyli ZMIENNE WARIANTOWE. Unie to specyficzne struktury, w których pola pamięci przeznaczone na objekty różnego typu nakładajš się. Jeœli jakaœ zmienna może być reprezentowana na kilka sposobów (wariantów) to sensowne jest przydzielenie jej nie struktury a un. W danej chwili pole pamięci należšce do unii może zawierać TYLKO JEDEN WARIANT. W przykładzie - albo cyfrę (która znakowojest widzianajako znak ASCII o kodzie 2,3,4 itd.) albo napis. Do zadeklarowania unii służy słowo kluczowe union. ~ include "string.h" ~ include "stdio.h" int BUFOR, i; int main() union{ int Cyfra; char Napis[20]; } Unia; for (i=1; i<11; i++) printf("\n Podaj liczbe jednocyfrowa: "); scanf("o/od", &BUFOR); if (BUFOR9) strcpy(Unia.Napis, "TO NIE CYFRA !"); else Unia.Cyfra = BUFOR; printf("\n Pole jako Cyfra Pole jako Napis \n"); /* Tu wyswietlimy warianty: Pole jako cyfra i jako napis*/ /* Petla pozwoli Ci przeanalizowac wszystkie cyfry 0...9 */ printf("o/od\t\t\to/os", Unia.Cyfra, Unia.Napis); return 0; Pętla w przykładzie nie ma znaczenia. Służy tylko dla Twojej wygody - dzięki niej nie musisz uruchamiać programu przykładowego wielokrotnie. Podobnie zmienne BUFOR oraz i majš znaczenie pomocnicze. Zwróć uwagę, że nieprawidłowa interpretacja zawartoœci pola unii może spowodować wadliwe działanie programu. 112 str 113 10.1. W programie P-33 zamień unię na strukturę. Porównaj działanie. 10.2 W programie P-32 przydziel na Wiek w strukturze Facet o jeden bit mniej. Ile lat może teraz mieć Facet ? 10.3. Zmodyfikuj program P-32 tak, by napis o liczbie mężów/żon zależał od płci - pola Sex. 10.4. Zamieniwszy unię na strukturę w programie P-33, sprawdŸ, czy wpływa to na wielkoœć pliku *.EXE. OPERACJE LOGICZNE. Zaczniemy od operacji logicznych na pojedynczych bitach liczb całkowitych. W Turbo C+ + mamy do dyspozycji następujšce operatory: " Zaprzeczenie (NOT) "0 =1; " 1= 0; : Suma (OR) 00 = 0; 01=1; 10 =1;11=1; & Iloczyn (AND) 0&0 = 0; 0& I = 0; 1 &0 = 0; 1 & 1=1; Alternatywa wyłšczna ALBO...ALBO (XOR) 0 0=0; 0 I =1; 1 0=1; 1 I =0; << Przesunięcie bitów w lewo (Shift Left) << 00001000 = 00010000 dzieœ. 8<< I =16 >> Przesunięcie bitów w prawo (Shift Right) >> 00001000 = 00000100 dzieœ. 82=2 Miło byłoby pooglšdać to trochę dokładniej w przykładowych programach, ale potrzebne nam do tego będš funkcje. Zajmijmy się więc uważniej funkcjami w języku C. 113 str 114 Lekcja 11 Jak posługiwać sig funkcjami W trakcie tej lekcji dowiesz się więcej o: * Junkcjach i prototypach funkcji; * przekazywaniu argumentów funkcji; * wspólpracyfunkcji ze wskaŸnikami. Aby przedstawić działanie operatorów logicznych opracujemy własnš funkcję Demo() i zastosujemy jš w programie przykładowym [P-34. najważniejszy fragment] int Demo(int Liczba) int MaxNr=15; for (; MaxNr>=0; MaxNr--) if ((Liczba~iMaxNr)&1 ) printf("1 "); else printf("0"); return 0; //Funkcja nie musi nic zwracac Funkcja przesuwa liczbę o kolejno 15,14,13 itd. bitów w prawo i sprawdza, czy 16,15,14 bit jest jedynkš, czy zerem. Iloczyn logiczny z jedynkš ( 0000000000000001 ) gwarantuje nam, że wpływ na wynik operacji będzie miał tylko ten jeden bit (patrz wyżej - jak działajš operatory logiczne). ~ include int Demo(in~ Liczba) int MaxNr=15; for (; MaxNr>=0; MaxNr--) if ( ( Liczba~i Max Nr) &1 ) printf("1 "); else printf("0"); return 0; 114 str 115 char odp; int main() int X, Y; clrscr(); printf("\nPodaj dwie liczby calkowite od -32768 do +32767\n"); printf("n\Liczby X i Y rozdziel spacja"); printf("\nPo podaniu drugiej liczby nacisnij [Enter]"); printf("n\Liczby ujemne sa w kodzie dopelniajacym"); printf("\nskrajny lewy bit oznacza znak 0-Plus,1-Minus"); for(") printf("\n"); scanf("%d "/od", &X, &Y); printf("\nX:\t"); Demo(X); printf("\nY:\t"); Demo( ~X); printf("\n"Y:\t); Demo("Y); printf("\nX&Y:\t"); Demo(X&Y); printf("\nX:Y:\t"); Demo(X i Y); printf("\nX Y:\t"); Demo(X Y); printf("\nY:\t"); Demo(Y); printf("\nY>>1:\t"); Demo(Y> > 1 ); printf("\nY<<2:\t"); Demo(Yr <2); printf("\n\n Jeszcze raz? T/N"); odp=getch(); if (odp!='T'&& odp!='t') break; return 0; Jeœli operacje majš być wykonywane nie na bitach a na logicznej wartoœci wyrażeń, to i i oznacza sumę (LUB); && oznacza iloczyn (I); ! oznacza negację (NIE). Przykłady: (x= =0 x > 5) - x równa się 0 LU B x większy niż 5; (a > 5 && a! =11 ) - a większe niż 5 I a nie równe 11; (num>=5 && num!=6 i i a>0) num nie mniejsze niż 5 I num nie równe 6 LUB a dodatnie; Wyrażenia logiczne sprawdzane instrukcjš if MUSZĽ być ujęte w nawiasy okršgłe. Do wytworzenia wartoœci logicznej wyrażenia może zostać użyty operator relacji: ~ > = > !- . Jeœli tak się nie stanie, za wartoœć logicznš wyrażenia przyjmowane ~est: 115 str 116 1, PRAWDA, TRUE, jeœli wartoœć numeryczna wyrażenia jest różna od zera. 0, FAŁSZ, FALSE, jeœli wartoœć numeryczna wyrażenia jest równa zero. Porównaj: if (a < =0) ... if ( a ) ... if (a+b) ... Konwersja - przykłady. Po obejrzeniu dwójkowej interpretacji pojawiła Ci się zapewne wštpliwoœć, czy koniecznie musieliœmy tworzyć jakšœ specjalnš funkcję. Oczywiœcie, że nie. Turbo C + + dysponuje wieloma funkcjami wykonujšcymi takie działania, np: itoa() - Integer TO Ascii - zamiana liczby typu int na łańcuch znaków ASCII; Itoa() - Long int TO Ascii - zamiana long int - > ASCII; atoi() - zamiana Ascii - >int; atol() - zamiana Asdii - >long int. Wszystkie wymienione funkcje przekształcajšc liczby na łańcuchy znaków potrzebujš trzech parametrów: * pl - liczby do przekształcenia; * p2 - bufora, w którym będš przechowywać wynik - łańcuch ASCII; * p3 - podstawy (dwójkowa, dziesiętna itp.). Jeœli chcemy korzystać z tych funkcji, powinniœmy dołšczyć plik nagłówkowy z ich prototypami - stdlib.h (STandarD LIBrary - standardowa biblioteka). A oto przykład. // Uwaga: mozna "stdio.h" lub ~ include "stdio.h" ~ include "stdlib.h" main() int i; char B10[10], B2[20], B16[10]; //BUFORY for (i=1; i<17; i++) printf(""/os "/os "/os\n", itoa(i, B10[i],10), itoa(i, B2[i], 2), itoa(i, B16[i],16)); return 0; 116 str 117 Opracuj program testujšcy działanie funkcji atoi(). KILKA SŁÓW O 'lf~YPACH DANYCH W C/C+ + . Przed przystšpieniem do obszernego zagadnienia "funkcje w C" krótko zasyg- nalizujemy jeszcze jedno zjawisko. Wiesz z pewnoœciš, że wykonywane na liczbach dwójkowych mnożenie może dać wynik o dhigoœci znacznie większej niż mnożna i mnożnik. W programach może się poza tym pojawić koniecznoœć np. mnożenia liczb zmiennoprzecinkowych przez całkowite. Jak w takich przypadkach postępuje C ? Po pierwsze: C może sam dokonywać konwersji, czyli zmiany typów danych naogół zgodnie z zasadš nadawania zmiennej "mniej pojemnego" rodzaju typu zmiennej "bardziej pojemnego" rodzaju przed wykonaniem operacji; Po drugie: my sami możemy zmusić C do zmiany typu FORSUJĽC typ œwiadomie w programie. W przykładzie poniżej podajšc w nawiasach żšdany typ zmiennej forsujemy zmianę typu int na typ float. ~ include "stdio.h" void main() int a=7; printf(""/of", (float) a); Konwersja typów nazywana bywa także "rzutowaniem" typów (ang. type casting). A oto szerszy przykład "forsowania typów": int a = 2; float x =17.1, y = 8.95, z; char c; c = (char)a + (char)x; c = (char)(a + (int)x); c = (char)(a + x); c=a+x; z = (float)((int)x * (int)y); z = (float)((int)x * (int)y); z = (float)((int)(x * y)); z = x * y; c = char(a) + char(x); c = char(a + int(x)); 117 str 118 c = char(a + x); e=a +x; z = float(int(x) " int(y)); z = float(int(x) " int(y)); z = float(int(x " y)); z=x"y; TYP WYLICZENIOWY enum. Zamiast stosować wiele dyrektyw define można posłużyć się w języku C specjalnym typem - WYLICZENIOWYM. Działanie dyrektyw: ~ define Poniedziałek 1 ~ define Wtorek 2 ~ define Œroda 3 #f define Niedziela 7 oraz definicji typu wyliczeniowego typedefenum { . . . . } w poniższym przykładzie będzie identyczne, z drobnym zastrzeżeniem: C, jak zwykle, liczy od zera. DEFINICJA TYPU typedef. Definicja zmiennej (podaję za "Leksykon języka C" - A. Ragen) typu wyliczeniowego winna mieć następujšcš postać: enum oznacznik //niezbyt szczęœliwa nazwa lista wartoœci zmiennej } nazwa zmiennej; Wartoœci zmiennej na liœcie należy rozdzielić przecinkami int main () typedef enum { Poniedziałek, Wtorek, Œroda, Czwartek, Piatek, Sobota, Niedziela} Dzien Tygodnia; Dzien Tygodnia Dzien; 118 str 119 r(Dzien = Poniedzialek; Dzien<=Niedziela; Dzien++) printf("%d\n", Dzien); :urn 0; Raz zdefiniowanym typem (podobnie jak w strukturach) możemy się dalej spokojnie posługiwać. Linia: Dzien Tygodnia Dzien; oznacza - zmienna Dzien będzie typu (wyliczeniowego, wczeœniej zdefiniowanego) takiego jak Dzien Tygodnia. FUNKCJE W JĘZYKU C/C+ + . Pojęcie funkcji obejmuje w C/C+ + zarówno pascalowe procedury, jak i basicowe podprogramy. Funkcji zdefiniowanych w Turbo C + + przez prducentajest bardzo dużo. Dla przykładu, funkcje arytmetyczne, które możesz wykorzystać do obliczeń numerycz- nych to np.: abs - wartoœć bezwzględna, cos - cosinus, sin - sinus, tan - tangens, asin, atan, acos, - funkcje odwrotne ARCUS SINUS... funkcje hiperboliczne: sinh, cosh, tanh, wykładnicze i logarytmiczne: exp - e~x log - logarytm naturalny, Iog10 - logarytm dziesiętny. Jeœli skorzystasz z help i zajrzysz do pliku math.h (Help ; Index ; math.h), znajdziesz tam jeszcze wiele przydatnych funkcji. Funkcja może, a1e nie musi zwracać wartoœć do programu - dokładniej do funkcji wyższego poziomu, z której została wywołana. Shxży do tego instrukcja return. Użytkownik może w C definiować własne funkcje. Funkcja może być bezparametrowa. Oto przykład bezparametrowej funkcji, zwracajšcej zawsze liczbę całkowitš trzynaœcie: int F Trzynascie() return 13; Poprawne wywołanie naszej funkcji w programie głównym miałoby postać: int main() { int X; ........ // Funkcja typu int nie musi byc deklarowana. X = F Trzynascie(); 119 str 120 Ajeœli funkcja musi pobraćjakieœ parametry od programu (funkcji wyższego poziomu, wywołujšcej)? Zwróć uwagę, że program główny w C to też funkcja - main(). Przykład następny pokazuje definicję funkcji obliczajšcej pištš potęgę pobranego argumentu i wywołanie tej funkcji w programie głównym. int F XdoPiatej(int ARGUMENT) int ROBOCZA; //automatyczna wewnetrzna zmienna funkcji ROBOCZA= ARGUMENT'ARGUMENT; ROBOCZA=ROBOCZA"ROBOCZA"ARGUMENT; return ROBOCZA; int main() int Podstawa, Wynik, a, b; //... Funkcja nie jest deklarowana przed uzyciem Wynik = F XdoPiatej(Podstawa); //...... a = F XdoPiatej(b); //..... return 0; Zwróć uwagę, że definiujšc funkcję podajemy nazwę i typ ARGUMENTU FORMAL- NEGO funkcji - ARGUMENT. W momencie wywołania na jego miejsce podstawiany jest rzeczywisty bieżšcy argument funkcji. Przyszła pora na to, na co być może czekałeœ od poczštku. Jeœli bowiem korzystałeœ wczeœniej z Turbo C v.1.5 lub 2.0 i masz swoje stare programy, dziwiłeœ się, być może , że próba ich skompilowania i uruchomienia sprawia kłopoty. Wszystkie one sš napisane wedhzg szablonu: main() Czego zatem im brak? Brak im DEKLARACJI FUNKCJI main(). W Turbo C++ wszystkie fumkcje (podobniejak zmienne), nie wyłšczajšc funkcji main(), powinny zostać ZADEKLARO- WANE. Jeœli funkcja nie zostanie zadeklarowana przed użyciem, to domyœlnie przy- jmowany jest typ funkcji int. Możnaby zatem przyjšć domyœlnie typ int i już. Tak, ale wtedy funkcja main() ma obowišzek ZWRÓCIĆ WARTOŒĆ TYPU int. Możesz zatem 120 str 121 uruchomić swoje stare programy, dopisawszy do nich tuż przed końcowym nawiasem klamrowym : '~ return 0; Aby zapewnić wysokš dokładnoœć obliczeń wymienione wyżej funkcje biblioteczne sqrt(), sin() itp. "uprawiajš" arytmetykę na długich liczbach typu double. Funkcję takš przed użyciem w swoim programie MUSISZ ZADEKLAROWAĆ. Przykład: main() double a, b; double sqrt(); // tu skasuj deklaracje funkcji sqrt() // a otrzymasz bledny wynik ! clrscr(); printf("Podaj liczbe\n"); scanf("o/olf", &a); b = sqrt(a); printf("\n o/oLf", (long double) b); getch(); return 0; PROTOTYPY FUNKCJI, czyli o jeszcze deklaracjach funkcji. Prototyp funkcji to taka deklaracja, która: * została umieszczona na poczštku programu poza funkcjš main(), * zawiera deklarację zarówno typu funkcji, jak i typów argumentów. Przykład prototypu (funkcja2.c): double FUNKCJA( double X, double Y); main() { double A=0, B=3.14; printf("Wynik działania funkcji: \n"); printf("o/olf", FUNKCJA(A,B)); return 0; } double FUNKCJA(double X, double Y) return ((1 +X)*Y); Prototyp mógłby równie dobrze wyglšdać tak: double FUNKCJA(double, double); nazwy parametrów formalnych nie sš istotne i można je pominšć. W Turbo C/C + + wolno nie zwracać wartoœci funkcjom typu void. To dlatego właœnie często rozpoczynaliœmy programy od void main() 121 str 122 Skutek praktyczny: Jeœli w ciele funkcji typu void występuje instrukcja return (nie musi wystšpić) to instrukcja ta nie może mieć argumentów. Oto przykład prototypu, deftnicji i wywołania funkcji typu void: void RYSUJPROSTOKAT( int Wys, int Szer, char Wzorek); // Prototyp void main() { clrscr(); RYSUJPROSTOKAT(5, 20, ' '); // klocek ASCII 176 - [Alt]-[176] getch ( ) ; RYSUJPROSTOKAT(15,15, ' '); //[Alt] - [177] getch(); void RYSUJPROSTOKAT( int Wys, int Szer, char Wzorek) int i, j; // automatyczne zmienne wewnętrzne funkcji for(i=1; i<=Wys; i++) for(j=1; j=Szer; j++) printf(""/oc",Wzorek); printf("\n"); Prototypy wszystkich funkcji standardowych znajdujš się w plikach nagłówkowych *.H (ang. Header file). Skutek praktyczny: JEŒLI DOŁĽCZYSZ DO PROGRAMU STOSOWNE PLIKI NAGŁÓWKOWE *.h,możesz ZREZYGNOWAĆ Z DEKLARACJI FUNKCJI. Dodajšc do programu wiersz: ~ include < math.h > dołšczajšcy plik zawierajšcy prototyp funkcji sqrt(), możesz napisać program tak: ~ include < stdio.h > ~ include < math.h > main() double a, b; // tu zniknela deklaracja funkcji sqrt() 122 str 123 clrscr(); // mimo to wynik jest poprawny ! printf("Podaj liczbę\n"); scanf(""/olf", &a); b = sqrt(a); printf(n "/oLf, (long double) b); getc h ( ) ; return 0; PRZEKAZYWANIE PARAMETRÓW DO FUNKCJI. W Turbo C/ Turbo C+ + często przekazuje się parametry do funkcji przy pomocy wskaŸników. Aby przeœledzić co dzieje się wewnštrz funkcji wpisz i uruchom program [P-40). Najpierw skonstruujemy sam program a następnie zmodyfikujemy go w taki sposób, abyœ mógł sobie popodglšdać cały proces. Przy pomocy funkcji printf() każemy wydrukować kolejne stany zmiennych, stan programu i funkcji a funkcja getch() pozwoli Ci obejrzeć to "krok po kroku". Mogłoby się wydawać, że program poniżej skon- struowany jest poprawnie. . . void FUNKCJA( int ); //Prototyp, deklaracja funkcji void main() int Zmienna; //Zmienna funkcji main, rzeczywisty argument clrscr(); Zmienna = 7; FUNKCJA( Zmienna); //Wywolanie funkcji printf(""/od", Zmienna); //Wydruk wyniku void FUNKCJA( int Argument) //Definicja funkcji Argument = 10 * Argument + Argument; FUNKCJA jest jak widać trywialna. będzie zamieniać np. 2 na 22, 3 na 33 itp. tylko w tym celu, by łatwo było stwierdzić, czy funkcja zadziałała czy nie. Rozbudujmy program tak by przeœledzić kolejne stadia. void FUNKCJA( int ); //Prototyp int Zmienna; void main() clrscr(); printf("Stadium: \tZmienna Argument"); printf("\nStadium 1 \t%d\tnie istnieje\n", Zmienna); Zmienna = 7; 123 str 124 printf("Stadium 2\t%d\tnie istnieje\n", Zmienna ); FU N KCJA( Zmienna); printf("Stadium 3\tobd", Zmienna); // printf("%d", Argument); // taka proba sie N I E U DA ! getch(); void FUNKCJA( int Argument) //Definicja funkcji printf("jestesmy wewnatrz funkcji\n"); printf("Nastapiło kopiowanie Zmienna - >Argument\n"); printf("\t\t"bd\t"/od\n", Zmienna, Argument); getch(); Argument = 10"Argument + Argument; printf("t\t"~d\t"/od\n", Zmienna, Argument); getch ( ) ; Próba wydrukowania zmiennej Argument gdziekolwiek poza wnętrzem FUNKCJI() nie uda się i spowoduje komunikat o błędzie. Oznacza to, że POZA FUNKCJĽ zmienna Argument NIE ISTNIEJE. Jest tworzona na stosie jako zmienna automatyczna na wyłšczny użytek funkcji, w której została zadeklarowana i znika po wyjœciu z funkcji. Przy takiej organizacji funkcji i programu funkcja otrzymuje kopię zmiennej, na niej wykonuje swoje działania, natomiast żmienna (zmienne) wewnętrzna funkcji znika po wyjœciu z funkcji. Problem przekazania parametrów pomiędzy funkcjami wywohijšcymi ("wyższego rzędu" - tu: main) i wywoływanymi (tu: FUNKCJA) można rozwišzać przy pomocy * instrukcji return (zwrot do programu jednej wartoœci) lub * wskaŸników. Możemy przecież funkcji przekazać nie samš zmiennš, a wskaŸnik do zmiennej (robiliœmy to już w przypadku funkcji scanf - dlatego, że samej zmiennej jeszcze nie było - miała zostać dopiero pobrana, ale istniało już przeznaczone na tš nowš zmiennš - zarezer- wowane dla niej miejsce. Mogł zatem istnieć wskaŸnik wskazujšcy to miejsce). wskaŸnik należy oczywiœcie zadeklarować. Nasz program przybrałby zatem nowš postać - [P-41.1). WskaŸnik do zmiennej nazwiemy *Argument. //Pamietaj o plikach naglowkowych ! void FUNKCJA( int 'Argument); //Prototyp int Zmienna; void main() clrscr(); printf("Stadium: \tZmienna Argument"); printf("\nStadium 1 \t"/od\tnie istnieje\n", Zmienna); 124 str 125 Zmienna = 7; printf("Stadium 2\t%d\tnie istnieje\n", Zmienna ); FUNKCJA( &2mienna); //Pobierz do funkcji ADRES Zmiennej printf("Stadium 3\t%d", Zmienna); // printf("o/od", Argument); // taka proba sie N I E U DA ! getch(); void FUNKCJA( int *Argument) // Definicja funkcji printf("jestesmy wewnatrz funkcji\n"); printf("Nastapilo kopiowanie ADRESOW a nie zmiennej\n" ); printf("ADRESY:\t\t o/oX\to/oX\n", &Zmienna, Argument); getci~(); *Argument = 10* *Argument + *Argument; /* DZIALANIE */ printf("t\to/od\to/od\n", Zmienna, *Argument); getch(); W lin /* DZIALANIE */ mnożymy i dodajemy to, co wskazuje wskaŸnik, czyli Zmiennš. Funkcja działa zatem nie na własnej kopii zmiennej a bezpoœrednio na zmiennej zewnętrznej. Zwróć uwagę na analogię w sposobie wywołania funkcji: FUNKCJA( &Zmienna ); scanf("%d", &Zmienna ); A jeœli argumentem funkcji ma być tablica? Rozważ przykładowy program [P-40] . Program zawiera pewnš nadmiarowoœć (ku większej jasnoœci mechanizmów). ~ include ~ include < stdio.h > SUMA( int k, int Tablica[] ) int i, SumTab=0; for (i=0; i10) { printf(TO ZA DUZO ! - max.10); continue; suma = SUMA( N,TAB ); printf("n\TO JEST suma z progr. glownego o/od", suma); printf("\n Jeszcze raz ? T/N"); Odp = getch(); while (Odp!='N' && Odp!='n'); return 0; Kompilacja w Turbo C+ + jest wieloprzebiegowa (PASS 1, PASS 2), więc defmicja funkcji może być zarówno na poczštku jak i na końcu. A oto następny przykład. Operujšc adresem - wskaŸnikiem do obiektu (tu wskaŸ- nikami do dwu tablic) funkcja Wypelniacz() zapisuje pod wskazany adres cišg identycz- nych znaków. Na końcu każdego łańcucha znaków zostaje dodany NUL - (0)jako znak końca. Taki format zapisu łańcuchów znakowych nazywa się ASCIIZ. void Wypelniacz(char ~BUFOR, char Znak, int Dlugosc); char TAB2D[5] [10]; // Tablica 5 X 10 = 50 elementow char TAB1 D[50]; // Tablica 1 X 50 = 50 elementow int k; main() clrscr(); Wypelniacz( TAB~1 D, 'X', 41 ); //Wypelnia X-ami printf("o/os\n\n", TAB 1 D); for (k=0; k<5; k++) Wypelniacz( TAB2D[k], 65+k, 9); //ASCII 65 to A; 66 to B itd. for (k=0; k<5; k++) printf("o/os\n", TAB2D[k]); getch(); return 0; 126 str 127 void Wypelniacz( char 'BUFOR, char Znak, int Dlugosc ) int i; for ( i=0; i<=(Dlugosc-1); i++) "(BUFOR+i) = Znak; "(BUFOR+Dlugosc) = '\0'; Zwróć uwagę, że: * NAZWA TABLICY (tu: TABID i TAB2D) funkcjonuje jako wskaŸnik PIERW- SZEGO ELEMENTU TABLICY. FUNKCJE TYPU WSKANIKOWEGO Funkcje mogš zwracać do programu zarówno wartoœci typu int, czy float, jak i wartoœci typu ADRESU. Podobnie jak wskaŸnik wymaga deklaracji i podania w deklaracji na jakiego typu obiekty będzie wskazywał, podobnie funkcja takiego typu wymaga w deklaracji okreœlenia typu wskazywanych obiektów. Wiesz już, że zależy od tego tzw. krok wskaŸnika. W przykładzie [P-43] funkcja Minimum() poszukuje najmniejszego elementu tablicy i zwraca wskaŸnik do tegoż elementu. Znajšc lokalizację najmniejszego elementu możemy utworzyć nowš tablicę, ale już uporzšdkowanš według wielkoœci. int BALAGAN [10); int PORZADEK[10); // Tablica koncowa - uporzadkowana int k, "pointer , MAX=10000 ; int 'Minimum(int Ilosc, int 'TABL); main() clrscr(); printf("Podaj 10 liczb calkowitych od -10000 do 10000\n"); for (k=0; k<=9; k++) scanf(""/od", &BALAGAN[k)); printf("Po kolei: \n\n"); for ( k=0; k<=9; k++ ) pointer= Minimum(10, BALAGAN); PORZADEK[k] ='pointer; 'pointer= MAX; for(k=0; k<=9; k++) printf("%d", PORZADEK[k]); getch(); return 0; int 'Minimum( int Ilosc, int "TABL ) int *pMin; int i; 127 str 128 pMin=TABL; for (i=1; i ~ include < math.h > double NASZA( double ); //Deklaracja zwyklej funkcji double ("Funkcja)(double ARG); //pointer do funkcji double Liczba, Wynik; //Deklaracje zmiennych int WYBOR; ' main() elrscr(); printf("Podaj Liczbe \n"); I scanf(""/olf", &Liczba); printf("CO MAM ZROBIC ?\n"); printf("1 - Sinus \n"); 128 str 129 printf("2 - Pierwiastek\n"); printf("3 - Odwrotnosc 1/x\n"); scanf("%d", &WYBOR); switch(WYBOR) case 1: Funkcja=sin; break; case 2: Funkcja=sqrt; break; case 3: Funkcja=NASZA; break; Wynik=Funkcja(Liczba); // Wywolanie wybranej funkcji printf("n\nWYNIK = %lf", Wynik); getch(); return 0; double NASZA(double a) printf("\n A TO NASZA PRYWATNA FUNKCJA\n"); if (a!=0) a=1/a; else printf("???\n"); return a; main() - FUNKCJA SPECJALNA ůTa ksišżka siłš rzeczy, ze względu na swojš skromnš objętoœć i skalę zagadnienia o którym traktuje (autor jest zdania, że język C to cała filozofia nowoczesnej informatyki "w pigułce") pełna jest skrótów i przemilczeń. Nie możemy jednak pozostawić bez, krótkiego choćby, opisu pomijanego dyskretnie do tej pory problemu PRZEKAZANIA PARAMETRÓW DO PROGRAMU. Konwencja funkcji w języku C/C+ + wyraŸnie rozgranicza dwa różne punkty widzenia. Funkcja pozwala na swego rodzaju separację œwiata wewnętrznego (lokalnego, własnego) funkcji od œwiata zewnętrznego. Nie zdziwi Cię więc zapewne, że i sposób widzenia parametrów przekazywanych programowi przez DOS i sposób widzenia "od wewnštrz" argumentów pobierabych przez funkcję main() jest diametralnie różny. To, co DOS widzi tak: PROGRAM PAR1 PAR2 PAR3 PAR4 PARS [...][Enter] funkcja main() widzi tak: main(int argc, char **argv, char **env) lub tak: main(int argc, char *argv[], char *env[]) CO TO JEST ??? Zapisane zgodnie z obyczajami stosowanymi w prototypach funkcji: int argc - liczba całkowita ( > =1, bo parametr Nr 1 to nazwa samego programu, za poœrednictwem której DOS wywołuje funkcję main). Liczba argumentów - parametrów może być zmienna. UWAGA: Język programowania wsadowego BPL przyjmuje nazwę programu za 129 str 130 parametr %0 a C+ + uznajejš za parametr o numerze argv[0], tym niemniej, nawetjeœli nie ma żadnych parametrów argc = I. argv - to tablica zawierajšca wskaŸniki do łańcuchów tekstowych reprezentowanych w kodzie ASCIIZ - nazw kolejnych paramentrów, z którymi został wywołany program. Pierszy element tej tablicy to nazwa programu. Ostatni element tej tablicy, o numerze argv - 1 to ostatni niezerowy parametr wywołania programu. env - to także tablica zawierajšca wskaŸniki do łańcuchów znakowych w kodzie ASCIIZ reprezentujšcych parametry œrodowiska (environment variables). WskaŸnik o wartoœci NUL sygnalizuje koniec tablicy. W Turbo C+ + istnieje także predefiniowana zmienna globalna, przy pomocy której można uzyskać dostęp do œrodowiska operacyjnego (::)environ . Przykłady poniżej przedstawiajš sposób wykorzystania parametrów wejœciowych programu. # include "stdio.h" ~ include "stdlib.h" main(int argc, char "argv[], char "env[]) printf("Parametry srodowiska DOS: \n"); int i = 0; do printf("o/os \n", env[i]); i++; while (env[i] != NULL); printf("Lista parametrow programu: \n"); for(i=1; i<= argc - 1; i++) printf("o/os \n", argv[i]); printf("Nazwa programu: \n"); printf("o/os", argv[0] ); return 0; Ponieważ C+ + traktuje nazwę tablicy i wskaŸnik do tablicy w specjalny sposób, następujšce zapisy sš równoważne: *argv~ oraz **argv *env0 oraz **env Nazwy argumentów argc, argv i env sš zastrzeżone i muszš występować zawsze w tej samej kolejnoœci. Argumenty nie muszš występować zawsze w komplecie. Dopuszczalne sš zapisy: 130 str 131 main(int argc, char **argv, char **env) main(int argc, char *argv[]) main(int argc) main() ale niedopuszczalny jest zapis: main(char *env~) Nawetjeœli nie zamierzamy wykorzystać "wczeœniejszych" parametrów - MUSIMY JE PODAĆ. PODSUMUJMY.. Uogólniona składnia definicji funkcji jest następujšca: klasa~pamięci typ wyniku nazwa funkcji(lista argumentów) deklaracje argumentów deklarowanych definicje i deklaracje zmiennych instrukcje Przykład: static int funkcjaprzykl(a,b,c) //argumenty formalne float a; //argumenty deklarowane char b; int c; int wspolczynnik = 100; //cialo funkcji return (c * wspolczynnik); UWAGA ! W nazwach własnych funkcji wolno nam stosować duże litery. Często poprawia to czytelnoœć tekstu programu. Należy jednak pamiętać, że: NAZWY WSZYSTKICH FUNKCJI STANDARDOWYCH MUSZĽ BYĆ PISANE MAŁYMI LITERAMI. 11.1. Spróbuj tak zmodyfikować funkcję Demo(), by liczba w formie dwójkowej była pisana "od tyłu". Do cofania kursora w funkcji printf użyj sekwencji \b\b. 11.2. Zinterpretuj zapis: if IMIANOWNIK) printf("/of,1 /MIANOWNIK); else exit(1 ); 11.3 Spróbuj przeprowadzić rzutowanie typu we własnym programie. 11.4 Przekaż wartoœć w programie przykładowym posługujšc się instruk- cjš: return (10"Argument + Argument); 11.5 Rozszerz zestaw funkcji do wyboru w programie przykładowym. 131 str 132 str 133 Czgœć II. OOP - programowanie obiektowe w Turbo C ~- -~ str 134 OOP - Object Oriented Programming oznacza dosłownie "programowanie obiek- towo zorientowane", które często przeciwstawia się "klasycznemu" sposobowi pro- gramowania, nazywanemu KR style programming, czyli programowaniu w stylu Kerninghan & Ritchie. Jak przekonasz się za chwilę, różnica nie jest aż tak ostra. Umowna granica przebiega gdzieœ pomiędzy przykładami [P-47) a [P-48]. Jak wskazujš badania marketingowe, popularnoœć Windows i UNIXa stale roœnie, i najprawdopodobniej pod znakiem rosnšcej dominacji tego tandemu upłynie ostatnia dekada naszego XX wieku. Nie sposób wyobrazić sobie tego bez C + + i programowania obiektowego. 134 str 135 Lekcja 12 Operacje plikowe i wstgp do strumieni danych W systemia DOS dane i programy sš zgrupowane w pliki. Pliki (ang. file) mogš być TEKSTOWE i BINARNE. Najczęstszymi operacjami na plikach sš: * Utworzenie nowego pliku (ang. CREATE); * Odczyt z pliku (ang. READ); * Zapis do pliku (WRITE); * Otwarcie pliku (OPEN); * Zamknięcie pliku (CLOSE); r~ * Wyszukanie danej w pliku (SEEK); W kontaktach z urzšdzeniami - np. z dyskiem poœredniczš DOS i BIOS. To system DOS wie, gdzie na dysku szukać pliku (katalogu) o podanej nazwie i w których sektorach dysku znajdujš się fizycznie dane należšce do danego pliku. Operacje z plikami opierajš się o odwoływanie do systemu operacyjnego za poœrednictwem tzw. Deskryptora pliku (File Descriptor - numer identyfikacyjny pliku). Zestaw narzędzi potrzebnych nam do pracy to: IO.H - prototypy funkcji obsługi WEjœcia/WYjœcia (ang. lnput/Output =10); FCNTL.H - plik zawierajšcy definicje wymienionych poniżej stałych: O BINARY - otwarcie pliku w trybie binarnym; O TEXT - otwarcie pliku w trybie tekstowym; O RDONLY (Open for Read Only) - otwórz tylko do odczytu; O WRONLY (...Write Only) - tylko dla zapisu; O~RDWR (Reading and Writing) dozwolony zapis i odczyt; STAT.H - zawiera definicje stałych S IREAD - plik tylko do odczytu (przydatne dla funkcji creat); S IWRITE - tylko zapis (przydatne dla funkcji creat); FUNKCJE: int oper(pl, p2, p3) - trójparametrowa funkcja otwierajšca plik; (parametry patrz przykład) zwraca do programu Wynik = -1 (operacja zakończona niepowodzeniem - np. nie ma pliku) lub Wynik = File Descriptor - numer pliku przekazany przez DOS. int creat(pl, p2) - funkcja tworzšca nowy plik; int read(...) - funkcja czytajšca z pliku; int write(...) - funkcja zapisu do pliku; imt close(...) - zamknięcie pliku. 135 str 136 Po uruchomieniu program otwiera automatycznie trzy standardowe pliki, zwišzanl z urzšdzeniami: 0 - stdin - standardowy plik wejœciowy (norm. klawiatura konsoli); 1 - stdout - standardowy plik wyjœciowy (norm. monitor); 2 - stderr - standardowy plik wyjœciowy - diagnostyczny (komunikaty o błędach). STD... STandarD INput - standardowe wejœcie. STD OUTput - standardowe wyjœcie. STD ERRors - plik diagnostyczny. ~ include < stdio.h > ~ include < conio.h > ~ include //Duze litery tylko dla podkreœlenia ~ include < FCNTL.H > ~ include < IO.H > char 'POINTER; int IL znakow, DLUG-pliku, TRYB dostepu, Wynik, i; int Plik-1, Plik 2; char BUFOR[15] = {"TEKST DO PLIKU"}; char STOS[3], ZNAK='X'; main() POINTER = &BUFOR[0]; printf("Wloz dyskietke do A: i nacisnij cos...\n"); Plik 1 = creat("a:\plik1.dat", S~IWRITE); if ( Plik-1 = -1 ) printf("\n Nie udalo sie zalozyc plik1.dat..."); Plik 2 = creat( "a:\plik 2.dat", S-IWRITE); if (Plik 2 =- -1) printf("\n Klops przy PIik2.dat"); fmode = O-BINARY; //Będziemy otwierac w trybie binarnym Wynik = open( "a:\plikl .dat", O WRONLY ); if (Wynik =- -1) 136 str 137 printf("\n Nie udalo sie otworzyc pliku..."); IL znakow = 15; //Ilosc znakow do zapisu Wynik =write( Plik 1, POINTER, IL znakow ); printf("Zapisalem "/od znakow do pliku.", Wynik); close( Plik~1 ); Plik 1 = open("a:\PIik1.dat", O~RDONLY ); Plik 2 = open("a:\PIik2.dat", O WRONLY ); POINTER = &STOS[0]; for (i=1; ZNAK; i++) //Kopiuje plik + spacje STOS[1 ] = ZNAK; write( Plik 2, POINTER, 2); read( Plik~1, &ZNAK,1); close(Plik~1); close(Plik 2); getch ( ) ; return 0; ~! 7! Przykładowy program [P-45] wykonuje następujšce czynnoœci: I. Tworzy plik a:\plikl.dat(potrzebny dostęp do dyskietki a:). 2. Tworzy plik a:\plik 2.dat. 3. Otwiera plik a:\plikl.datw trybie binarnym tylko do zapisu. ! (ZWRÓĆ UWAGĘ, że tryb binarny nie przeszkadza zapisać tekstu.) 4. Dokonuje zapisu do pliku. 5. Zamyka plik a:\plikl.dat. 6. Otwiera plikl.dat w trybie binarnym tylko do odczytu. 7. Otwiera plik 2.dat tylko do zapisu. 8. Kopiuje plikl.dat do plik 2.dat dodajšc spacje. Zwróć uwagę na konstrukcję: for(i =1: ZNAK; i + + ) Wyjaœnienie. Póki jest znak wykonuj kopiowanie. Przypominam, że koniec to NUL '\0'. Jeœli czytamy i piszemy po kolei - wszystko jest proste. Jeżeli natomiast ehcemy wyszukać w pliku okreœlone miejsce, to będzie nam jeszcze dodatkowo potrzebny mechanizm do okreœlenia pozycji w pliku - tzw. WSKANIK PLIKOWY. Pozycję można okreœlać względem poczštku pliku: 137 str 138 SEEK SET - stała okreœlajšca pozycjonowanie względem poczštku pliku; SEEK CUR - względem położenia bieżšcego (ang. Current - bieżšcy); SEEK END - okreœlenie pozycji wzgłędem końca pliku; EOF - End Of File - znak końca pliku. Funkcja lseek(): WSK PLK = long int lseek( plik, o ile, kierunek); służy do pozycjonowania w pliku. Liczba typu long int okreœlajšca pozycję w pliku nazywana jest WSKANIKIEM PLIKOWYM ( w programie przykładowym została oznaczona long int WSK PLK). W programie przykładowym wykonywane jest kolejno: * utworzenie na dysku pliku PROBA.DAT; * zapis do pliku wprowadzonych z klawiatury liczb całkowitych typu int; * zamknięcie pliku; * otwarcie pliku do odczytu; * ustawienie wskaŸnika na końcu pliku; * odczyt z pliku od końca; * wyprowadzenie odczytanych z pliku danych na ekran. ~ include "sys\stat.h" ~ include "conio.h" ~ include "stdio.h" ~ include "io.h" ~ include "fcntl.h" ~ define Cofnij o Zero 0 ~ define dwa bajty 2 int Numer = 0; int Plik, L, M, i; long int Dlug~Pliku; main() clrscr(); creat("A:\PROBA.DAT", S~IWRITE); printf("\nPodaj liczbe rozna od zera, zero - KONIEC"); fmode=O~BINARY; Plik=open("A:\PROBA.DAT", O WRONLY); do printf("\n Nr liczby \t"bd\t\t", Numer++); scanf(""/od", &L); if (L) write(Plik, &L, 2); while (L != 0); 138 str 139 close(Plik); getch(); printf("\n Teraz odczytam te liczby z pliku \n"); ! Plik=open("A:\PROBA.DAT", O~RDONLY); Dlug~Pliku=Iseek(Plik, 0, SEEK~END); for (i=Dlug~Pliku-dwa bajty; i>=-0; i-=2) Iseek(Plik, i, SEEK SET); read(Plik, &M, dwa bajty); printf(""/od, ", M); close(Plik); getch(); return 0; WEJŒCIE I WYJŒCIE POPRZEZ STRUMIENIE. Funkcje Turbo C + + mogš traktować wejœcie i wyjœcie (na monitor bšdŸ do pliku) jak strumienie danych. Funkcje, których prototypy zawiera plik STDIO.H, zastosujemy w przykładzie poniżej. Nazwy funkcji to, jak zwykle, skróty: fopen - Open File - otwórz plik; fclose - Close File - zamknij plik; getc - Get Character - pobierz znak ze strumienia; putc - Put Character - wyœlij znak do strumienia; Przykładowy program [P-47] kopiuje plik ze strumienia wejœciowego do strumienia wyjœciowego, wstawiajšc plusy zamiast spacji. ~ include "conio.h" ~ include "stdio.h" #/ define Fiasko NULL //NULL - stala predefiniowana //NULL oznacza PUSTY WSKAZNIK //Zmienne typu PLI K; FILE `Plik STRWE, `Plik~STRWY; //Wskazanie pliku char Pierwszy[40], Drugi[40), Znak; //Lancuchy - nazwy main() clrscr(); printf("\n Ktory plik skopiowac ? \t"); scanf(""/os", Pierwszy); printf("\n Nowa nazwa ? \t"); scanf(""/os", Drugi); Plik STRWE = fopen(Pierwszy, "rt"); //tryb Read Text="rt" 139 str 140 if(Plik STRWE == Fiasko) printf("\n Nie ma pliku o/os", Pierwszy); else Plik STRWY = fopen(Drugi, "wt"); } //Write Text="wt" if( Plik STRWY= = Fiasko) printf("\n Klops, nie moge zalozyc"); printf("pliku o/os", Drugi); else for(; Znak! = EOF; ) { Znak = getc(Plik STRWE); if(Znak =- ' ') Znak = '+'; putc(Znak, Plik STRWY); fclose(Plik STRWE); fclose(Plik STRWY); return 0; Do zarzšdzania strumieniami mamy w Turbo C + + także specjalne funkcje, których prototypy znajdujš się w pliku IOSTREAM.H (Input/Output Stream - strumień wejœcia-wyjœcia). Mamy także specjalne operatory < < i > > służšce do sterowania strumieniem danych. W trakcie Lekcji 16 zajrzymy do pliku IOSTREAM.H i zobaczymy jak to się stało, że operatorom < < oraz > > przypisano nowe działanie (ang. operator overloading). Oto banalnie prosty przykład wykorzystania tego mechanizmu. Nasza prywatna funkcja wydrukuj() poshiguje się operatorem < < i działa podobnie do printf(). Uruchom program i przetestuj. # include < iostream.h > void wydrukuj (int); main() int i; for (i=1 ; i<=10 ; i++) wydrukuj(i); return 0; void wydrukuj(int k) 140 str 141 cout < < "\n" "Wartosc liczby = " < < k; Pora wspomnieć o kilku mechanizmach zapewniajšcych językowi C+ + ogromnš elastycznoœć. Przy pomocy preprocesora (define), przesłaniania zmiennych (wiele zmien- nych o tej samej nazwie), przypisania nowego działania operatorom (operator over- loading) oraz możliwoœci przypisania funkcji o tej samej nazwie wielorakiego działania (function overloading) uzyskujemy stopień elastycznoœci wyższy, niż w "konkurencyj- nych" językach. W następnym przykładzie zilustrowano działanie operatora :: i problem "prze- słaniania" zmiennych. // Ilustracja "Przeslaniania" zmiennych ~ include "conio.h" ~ include "iostream.h" int i=13; //Zmienna globalna main() clrscr(); float i=3.1415; //Uwaga ZNOWU "i" !!! zmienna lokalna cout < < "Lokalne i wynosi " < < i < < "\n"; cout < < "Globalne i natomiast = " < < :: i < < "\n"; ::i = i + 10; cout < < "Sprawdzamy: lokalna i: " < < i < < "\n"; co u t < < "A glo baln a i: ":: i < < "\ n"; getch(); return 0; Jak widać z przykładu, zmiennš globalnš oznaczamy tak: ::zmienna a zmiennš lokalnš tak: zmienna Zanim ruszymy dalej w podróż po krainie C + + , zwróć uwagę, że nie musimy już thzmacz~ć naszemu komputerowi w jakim formacie ma nastšpić wyprowadzenie zmiennej. Niemniej jednak, to nadal my, a nie on decydujemy. Jeœli zrobiło Ci się trochę żal, że straciłeœ częœć swojej władzy, nie przejmuj się. Posługujšc się strumieniami cin i cout też możesz sobie wybrać format. Rozważ działanie programu [P-49]. I41 str 142 ~ include "conio.h" ~ include "iostream.h' ~ include "string.h" main() int i; float j; char Litera; char Tekst[50] ; i = 144; j = 18.395; Litera = 'L'; clrscr( ) ; strcpy(Tekst,"A TO JEST PANI KOWALSKA"); cout < < "ZACZYNAMY OPERACJE"; cout < < "\n"; cout < < "i = ' < < i < < "\n"; cout < < "j = ' < < j < < "\n"; cout < < "Litera to " < < Litera < < "\n"; cout < < "Tekst: " < < Tekst < < "\n"; i = 126; cout < < "DZIESIETNIE: " < < dec < < i < < "\n"; cout < < "SZESNASTKOWO (HEX): " < < hex < < i < < "\n"; cout < < "Litera znakowo: " < < (char)Litera < < "\n"; cout < < "Tu wpisz liczbe calkowita --->" ; cin > > i; cout < < "To bedzie HEX: " < < i < < "\n"; getch(); return 0; Ponieważ znasz z pewnoœciš z DOSa przełšczanie standardowych strumieni: program > plik.dat lub program > > plik.dat program < plik.dat dir ; more itp. więc nie zdziwi Cię chyba, że i Turbo C+ + pozwala na przełšczanie standardowych strumieni cout (stdout) i cin (stdin). Nie może zostać jednak przełšczony dowolnie trzeci ze standardowych strumieni cerr (stderr). W przykładowym programie poniżej wykonamy: 142 str 143 * czytanie z pliku (zamiast z klawiatury); * zapis do pliku (zamiast na monitor); * zapis na drukarkę, widzianš jako plik - urzšdzenie systemowe PRN (PRiNter). To wszystko spróbujemy wykonać przełšczajšc kierunek przepływu standardowych strumieni danych, przy pomocy cin i cout. Zwróć uwagę, bo bywa to często przyczynš pomyłek, że strzałkowanie operatorów w DOSie i w Turbo C+ + jest ODWROTNE. Działanie SYSTEM DOS Turbo C+ + Wyjœcie program > plik cout < < Wejœcie program < plik cin > > ~ include < stdio.h > ~ include ~ include "iostream.h" ~ include "fstream.h" ~ inelude "process.h" ifstream O RYG I NAL; ofstream KOPIA~PLIKU; ofstrea m D R U KA R KA; char NAZWA~PLI KU [15] ; char Flaga, Znak; void main() clrscr(); printf("Nazwa pliku do kopiowania: \n"); cout < <"Nazwa pliku do kopiowania: \n"; cin > > NAZWA~PLIKU; // Jesli pliku nie ma , to nie wolno go utworzyc - non create ORYGINAL.open(NAZWA~PLIKU, ios::nocreate); if (!ORYGINAL) { cout < < "KLOPS! Nie ma takiego...?\n"; exit(1 ); } KOPIAPLIKU.open(tokopia.kop); if (!KOPIAPLIKU) { cout < < "Nie moge utworzyc kopii...\n~'; exit(1 ); } cout < < "OTWARLEM OBA PLIKI \n"; while (ORYGINAL.get(Znak)) KOPIA~PLIKU.put(Znak); KOPIA PLI KU.close(); cout < < "Czy chcesz plik na drukarke ? T/N \n"; cin > > Flaga; if (Flaga!='T' && Flaga!='t') { ORYGINAL.close(); exit(0); } 143 str 144 DRUKARKA,open("PRN"); if (!DRUKARKA) { cout < < "Twoja drukarka jest do niczego...\n"; exit(1 ); } DRUKARKA < < "To jest kopia Twojego pliku: \n\n"; while (ORYGINAL.get(Znak)) DRUKARKA.put(Znak); DRUKARKA.close(); ORYGINAL.close(); I tak oto niepostrzeżenie znależliœmy się w œwiecie PROGRAMOWANIA OBIEK- TOWEGO w C++. Działania na obiektach i "klasyczny C" mogš współistnieć w jednym programie. Przykładem takiego współistnienia jest zastosowanie funkcji printf(), która jest nam przecież absolutnie zbędna w tym programie. Dla ułatwienia zrozumienia rozszyfrujmy kilka terminów. get - pobierz; put - wyœlij; open - otwórz; close - zamknij; ifstream - łnput File STREAM - strumień danych z pliku wejœciowego. ofstream - Output File STREAM - strumień danych do pliku wyjœciowego. ORYGINAL.open(NAZWA PLIKU, ios::nocreate); Obiektowi pod nazwš "ORYGINAL", który został zadeklarowany wczeœniej jako obiekt typu ifstream: * nadaj nazwę "NAZWA PLłKU; * otwórz ten plik w trybie "noncreate"; ::noncreate - zmienna globalna; ::create - utwórz plik. ::noncreate - wolno ci tylko otworzyć plik, jeœli istnieje, nie wolno ci tworzyć nowego pliku. Wyjaœnijmy jeszcze dla pełnej jasnoœci konstrukcję pętli: while (O RYG I NAL.get(Znak) ) KO PIA~PLI KU .put(Znak) ; ORYGłNAL.get(Znak) - pobierz znak z obiektu ORYGINAL; KOPIA PLIKU.put(Znak) - wyœlij znak do obiektu KOPIA PLIKU; while (ORYGINAL.get(Znak)) - kontynuuj pętlę dopóki znak pobrany z obiektu ORYGłNAL nie jest zerem, inaczej ! = '\0'. Jak widzisz, obiekty nie sš aż takie straszne. Porównaj "gramatykę"języka klasycznego i obiektowego: KOPIA~PLIKU.open("to kopia.kop"); Plik STRWE=fopen(Pierwszy, "rt"); 144 str 145 ("A:\PROBA.DAT", O~RDON LY); się do obiektu przy pomocy funkcji przypomina odwołanie się do pola ~~PLIKU.open("to kopia.kop"); i ( Pointer- > Tekst, STA.Tekst); Porównaj sposób zapisu z prawdziwš strukturš pod nazwš "ZWIERZAKI". // Po defnicji struktury słowo struct w deklaracjach // nastepnych struktur takiego typu jest OPCJONALNE. ~ include "iostream.h" struct ZWIERZAK { // tu slowo musi byc ! int waga; int dlugosc cm; int main() ZWIERZAK Ciapek, Azor, Kaczka; ZWIERZAK Kocisko; // zapis struct ZWIERZAK Kocisko lub struct ZWIERZAK Kotek; // ZWIERZAK Kotek dziala identycznie ! Ciapek.waga = 15; Azor.waga = 37; Kaczka.waga = 5; Kocisko.waga = 3; Kotek.waga = 14; Ciapek.dlugosc cm = 96; Azor.dlugosc cm = 78; Kaczka.dlugosc cm = 106; cout < < "Ciapek wazy: "< > w dówolnym programie wczeœniejszym zamiast funkcji printf() i scanf(). Pami‡ta~ o dołšczeniu pliku IOSTREAM.H. 146 str 147 Lekcja 13 Poczštek programowania obiektowego - klasy i obiekty Jednym z najbardziej podstawowych pojęć jest pojęcie KLASY OBIEK- TU. Zwišzek pojęć klasy i obiektu z logicznego punktu widzenia przypomina trochę relacje pomiędzy typem zmiennej a zmiennš lub typem struktury a danš strukturš. W językowym sensie można, dla przykładu powiedzieć, że: * zmienna X jest obiektem klasy LICZBY CAŁKOWITE, * struktura Ciapek (patrz przykład) jest obiektem klasy "STRUKTURY typu ZWIE- RZAKI", itp. Intuicyjnie wyczuwasz zapewne, że aby móc powiedzieć, że proste prostopadłe to szczególny przypadek obiektu klasy PROSTE, należałoby najpierw zdefiniować co to takiego ta klasa obiektów "PROSTE". Inaczej mówišc, aby mógł zaistnieć obiekt jakiejœ klasy, powinniœmy najpierw okreœlić co to takiego ta klasa. Podobnie, jak inne mechanizmy języka Turbo C + + , spróbujemy przedstawić ten problem posługujšc się przykładowym programem. // Ciapek jest tym razem obiektem klasy "ZWIERZAK" ~ include "iostream.h" class ZWIERZAK { //class - klasa - slowo kluczowe public: //public - nowe słowo kluczowe int waga; int dlugosc; main() ZWIERZAK Ciapek, Azor, Kaczka; ZWI ERZAK Kocisko; class ZWIERZAK Kotek; //class jest opcjonalne Ciapek.waga = 15; //dostep jak do pola struktury Azor.waga = 37; Kaczka.waga = 3; Ciapek.dlugosc = 104; Azor.dlugosc = 74; Kaezka.dlugosc = 62; 147 str 148 cout < < "Ciapek wazy: " < < Ciapek.waga < < "\n"; cout < r "Azor ma: " < < Azor.dlugosc < < " centymetry\n"; cout < < "Kaczka ma: " r r Kaczka.waga < < "kilo \n"; return 0; Definicja klasy "ZWIERZAK" bardzo przypomina definicję struktury "ZWIERZA- KI". Kwestiš, co jest publiczne (dostępne i widoczne z zewnštrz - ang. public), a co prywatne (ang. private) zajmiemy się w dalszej częœci podręcznika. A na razie następny przykład. Funkcje w œwiecie obiektów też zyskujš kilka nowych cech, nadajšcych im pewne szczególne uprawnienia. Mogš np. mieć zmiennš liczbę argumentów. W przy- kładzie [P-49] do pokazania tego mechanizmu wykorzystujemy makro. Jeœli jesteœ dociekliwy - plik nagłówkowy STDARG.H zawiera konstrukcję tego makra. Możesz zatem przeglšdajšc plik STDARG.H przekonać się w jaki sposób w języku C+ + definiowane sš makra. Zwróć uwagę na sposób deklaracji funkcji o zmiennej iloœci parametrów w Turbo C + + . ~ include "iostream.h" ~ include "stdarg.h" // TO TU! Jeden parametr obowiazkowy, reszta opcjonalna void pokaz arg(int n, ...); main() cout < < "\n\n Funkcja ma 1, 3 i 6 argumentow: \n"; int i = 9, jeden = 1, trzy = 3, szesc = 6; pokazarg (jeden, i ) ; pokaz arg (trzy, i, i + 5, i + 10) ; pokaz arg(szesc,1, 22, 33, 44, 55, 66); return 0; void pokaz arg(int n, ...) va I ist para m~pt; va start(param~pt, n); // Predefiniowane makro cout < < "Biezace parametry: "; int i; for (i=0; irn; i++) cout < < va arg(param~pt,int) < < " ; c o u t r < "\ n"; 148 str 149 va end(param~pt); A oto inna, ciekawa i przydatna cecha funkcji - nadawanie funkcjom nowych cech ; (ang. function overloading). WyobraŸmy sobie, że mamy funkcję wydrukuj(), która potrafi wysłać na ekran otrzymany znak: void wydrukuj(char znak) i cout < < znak; Tak zdefiniowanš funkcję możemy wywołać w programie w następujšcy sposób: wydrukuj('Z'); Czasem jednak wygodniej byłoby, gdyby nasza funkcja była bardziej elastyczna i pozwalała na wykonanie szerszego zakresu operacji, np.: wydrukuj('Z'); wydrukuj(75); // 75 to kod ASCII znaku, zamiast znaku bezpoœr. wydrukuj("Wiecej niz jeden znak"); W klasycznym języku C wymaga to zdefiniowania nowej funkcji, natomiast w C + + to, że funkcja wydrukuj() została już zdefiniowana w niczym nie przeszkadza. Poniżej deklarujemy i definjujemy takš dziwnš funkcję. class KLASA { public: void wydrukuj(char znak); void wydrukuj(int kodASCll); void wydrukuj(char "string); //wskaŸnik do lancucha Zwróć uwagę, że łańcuch znakówjest widzianyjakojednowymiarowa tablica zawierajšca dane typu znakowego, czyli w taki sposób: char TABLICA[9] ={ "123456789" }; // Tego nie musisz pisać, // To dzieje się automatycznie. Definice powinny mieć następujšcš postać: void KLASA::wydrukuj(char znak) {cout < < znak;}; void KLASA::wydrukuj(int kodASCll) {cout < < (char) kodASCll;}; void KLASA::wydrukuj(char ~string) {cout < < string;}; 149 str 150 Zapis: cout < < (char) kodASCll; oznacza forsowanie typu - zamień typ int na typ char - czyli przyporzšdkowanie kodowi znaku. Wywołanie tej funkcji w programie może spowodować różne działanie, w zależnoœci od parametru, z którym funkcja zostaje wywołana. Wywołania funkcji mogš wyglšdać np. tak: KLASA Obiekt1, Obiekt2; main() { Obiekt1.wydrukuj('A'); //Wydrukuje się litera A Obiektl .wydrukuj(99); //Wydrukuje się litera c Obiekt2.wydrukuj("napis"); //Wydrukuje się napis. Taki sposób postępowania umożliwia funkcjom większš elastycznoœć i pozwala operować bez konfliktów na różnych rodzajach danych. C+ + zawiera jednak także sposoby na ukrócenie swobody funkcji. Zwróć uwagę na zapis: void KLASA::wydrukuj(char znak) {cout < < znak;}; W taki sposób w œwieeie obiektów definiuje się funkcje. Z zapisu widać, że funkcja nie istnieje samodzielnie (jak to było poprzednio) a jest zwišzana z obiektami okreœlonej klasy. Zaraz wyjaœni się na czym polegajš wzajemne "stosunki" pomiędzy klasami, obiektami i funkcjami. HERMETYZACJA DANYCH. Jednš z najbardziej fundamentalnych potrzeb przy tworzeniu obiektówjest okreœlenie zasad przekazywania danych do obiektu, dostępu do tych danych i okreœlenie zarówno grupy funkcji (działań), które wolno, lub nie wolno przeprowadzać na danym obiekcie, a także uprawnień konkretnej funkcji w stosunku do obiektu. W przykładzie poniżej: * definiujemy klasę; * definiujemy funkcje; * przekazujemy i pobieramy dane do/od obiektu klasy Zwierzak. Zmienna int schowek powinna sugerować ukrytš przez obiekt i niedostępnš dla nieuprawnionych funkcji częœć obiektu a nie cechy anatomiczne zwierzaka. ~ include "iostream.h" class Zwierzak 150 str 151 int Schowek; //Schowek ma status private, jest niedostepny public: void SCHOWAJ(int Xwe); //Funkcje dostepne zzewnatrz int ODDAJ(void); void Zwierzak::SCHOWAJ(int Xwe) //definicja funkcji Schowek = Xwe; int Zwierzak::ODDAJ(void) return (Schowek); main() Zwierzak Ciapek, Azor, Kotek; // Obiekty klasy "Zwierzak" int Piggy; // zwykla zmienna Ciapek.SCHOWAJ (1 ); Azor.SCHOWAJ (22); Kotek.SCHOWAJ(-333); Piggy = -4444; cout < < "Ciapek ma: " < < Ciapek.ODDAJ() < < "\n"; cout < < "Azor ma: " < < Azor.ODDAJ() < < "\n"; cout < < "Kotek ma: " < r Kotek.ODDAJ() < r "\n"; cout < < "Panna Piggy ma: " < < Piggy < < "\n"; return 0; // Proba nieautoryzowanego dostepu do danych prywatnych obiektu: // cout < < Ciapek.Schowek; // printf("o/od", Ciapek.Schowek); // nie powiedzie sie Powiedzie sie natomiast próba dostępu do "zwykłej" zmiennej - dowolnš metodš - np.: printf("o/od", Piggy); Zauważalna teraz różnica pomiędzy obiektem a strukturš polega na tym, że: STRUKTURY OBIEKTY Status danych przyjmowany automatycznie: public private Na żšdanie programisty: private public I5I str 152 W praktyce oznacza to, że funkcja, która nie została przez programistę œwiadotni uprawniona do dostępu do danych obiektu nie może dokonać ani zapisu, ani odczytu tych lanych. Nazywa się to HERMETYZACJĽ (ang. encapsulation) danych. Przed nieautoryzowanym dostępem broni już kompilator. Próba skompilowania programu, w którym próbujš się dobrać do obiektu nieautoryzowane funkcje zostanie przerwana i spowoduje komunikat o błędzie. Przeanalizuj i uruchom program przy- kładowy [P-54], po skasowaniu znaku komentarza // w dowolnym miejscu. Próby nieautoryzowanego dostępu do danych obiektu PROSTOKĽT zostały poprzedzone // znakiem komentarza. W przeciwnym wypadku nie udałoby Ci się ani skompilować, ani uruchomić programu. ~ include "stdio.h" ~ include "iostream.h" class PROSTOKAT { //Prosta klasa obiektow int Dlug; int Szer; public: int LiczPole(void); // Dwie dopuszczalne METODY void ZainicjujP(int, int); // postepowania z obiektami }; // klasy PROSTOKAT int PROSTOKAT::LiczPole(void) //Pole PROSTOKATA return (Dlug * Szer); void PROSTOKAT::ZainicjujP(int x, int y) Dlug=x; Szer=y; // Dla porównania z Klasa umieszczamy Strukture struct SLUPEK { int Dlug; int Grubosc; main() PROSTOKAT Plac, Stolik; SLUPEK Maszt; // Stolik.Dlug = 12; te proby nie powioda sie! 152 str 153 // Plac.Szer = 10; // Plac.Grubosc = 8; Stolik.Zainicjuj P(12,10); Plac.Zainicjuj P(8, 8); Maszt.Dlug = 500; Maszt.Grubosc = 2; cout< <"Pole placu: "< < Plac.LiczPole() < <"\n"; cout< <"Ppwierzchnia stolika" < < Stolik.LiczPole() < <"\n"; // To sie nie uda: // cout < < LiczPoleP(Plac.Dlug, Stolik.Szer) < < "\n"; // cout < < "To tez sie nie uda" < < "\n"; // cout < < LiczPoleP(Stolik.Szer, Slupek.Grubosc) < < "\n"; // //printf(""/od", Stolik.Szer); return 0; Takie funkcje, które majš dostęp do obiektu i mogš wykonywać operacje na danych obiektu nazywajš się METODAMI. Próba nieautoryzowanego dostępu powoduje najczęœciej jeden z trzech typowych komunikatów w okienku "Message": ERROR: 'PROSTOKAT::Szer' is not accesible in function main() (BŁĽD: pole 'Szer' obiektu 'PROSTÓKAT' nie jest dostępne w (dla) funkcji main(). ) lub po zablokowaniu znakiem komentarza //: WARNING: 'Maszt' is assigned a value that is never used in function main(). (OSTRZEŻENIE: do obiektu 'Maszt' została przypisana wartoœć, która nie została ani razu użyta w funkcji main(). ). ERROR: 'Zainicjuj' is not a member of 'PROSTOKAT' in function main(). ('Zainicjuj' nie jest funkcjš autoryzowanš do dostępu do obiektu PROSTOKAT; dosłownie: member - członek). PROGRAM SIĘ "ZAWIESIŁ"! Kompilator nie wykrywa niestety wszystkich błędów programu. Może się zdarzyć, że podczas próbnego rozruchu programu system zawiesi się i trzeba będzie wykonać restart systemu [Ctrl]-[Alt]-[Del]. Zawsze lepiej mieć wtedy zapasowš kopię programu na dysku. Kolejnoœć czynnoœci powinna więc być taka: * Zapis na dysk bieżšcej kopii - [F2]; 153 str 154 * Kompilacja - [Alt]-[C], [M); * Uruchomienie - [Alt]-[R], [Enter]; * Przejrzenie wyników [Alt)-[FS]; * Ewentualne zgaszenie okna - [F3). SKĽD BIORĽ SIĘ OBIEKTY. Nic nie dzieje się samo. Jeœli więc w pamięci komputera ma zaistnieć obiekt, to jakaœ funkcja musi rozmieœcić dane należšce do obiektu w pamięci komputera. W œwiecie obiektów istniejš dwa rodzaje funkcji o takim właœnie specjalnym przeznaczeniu. Sš to tzw. KONSTRUKTORY i DESTRUKTORY. Konstruktor tworzy obiekt a destruktor (jak sama nazwa wskazuje) niszczy. Następny przykład zawiera KONSTRUKTOR i DESTRUKTOR obiektu PROSTO- KĽT. Zwróć uwagę, że program zawiera: * deklarację konstruktora i destruktora; * definicję konstruktora i destruktora; * konstruktor i destruktor pojawiajš się w definicji klasy obiektów. Nazwy klasy, konstruktora i destruktora sš identyczne, inny jest. natomiast sposób deklaracji obiektów danej klasy i konstruktora. Deklaracje i definicje obiektów sš podobne do deklaracji i definicji struktur, natomiast konstruktor i destruktor deklaruje się i definiuje podobnie do zwykłej funkcji. Po zadeklarowaniu i zdefiniowaniu konstruktora, kiedy w programie pojawia się obiekt danej klasy (np. Stolik), system Turbo C+ + wywołuje AUTOMATYCZNIE konstruktor, który nadaje parametrom stolika wartoœci. W danym przykładzie (tak być nie musi!) po nadaniu obiektom wartoœci przez automatycznie wywoływany konstruktor obiekty sš póŸniej powtórnie inicjowane przez funkcję ZainicjujP uprawnionš do nadawania wartoœci obiektom klasy PROSTO- KAT. A JEŒLI NIE MA KONSTRUKTURA? Obecnoœć konstruktora i destruktorajest OPCJONALNA (nie niezbędna). Jeœli nie ma konstruk- tora, obiekt zostaje utworzony w pamięci, ale zawartoœć jego pól jest przypadkowa aż do momentu nadania im przez jednš z metod (w przykładzie ZainicjujP()) okreœlonych wartoœci. ~ include "iostream.h" class PROSTOKAT { //Definicja klasy int Wysok; //Domyœlnie - default - private int Szerok; public: PROSTOKAT(void); //Zawiera deklaracje KONSTRUKTORA, Pole(void); //dwie METODY, void ZainicjujP(int, int); "PROSTOKAT(void); //i DESTRUKTOR 154 str 155 PROSTOKAT::PROSTOKAT(void) // definicja konstruktora Wysok = 6; Szerok = 6; int PROSTOKAT::Pole(void) //definicja metody Pole(void) return (Wysok * Szerok); void PROSTOKAT::ZainicjujP(int x, int y) //def. metody Wysok = x; Szerok = y; PROSTOKAT:: ~ PROSTOKAT(void) //definicja destruktora Wysok = 0; Szerok = 0; struct SLUPEK { int Wysok; int Grubosc; }; main() PROSTOKAT Plac, Stolik; //To powoduje wywolanie konstruktora S LU PEK Maszt; cout < < "Powierzchnia placu: " < < Plac.Pole() < < "\n"; cout < <"A pow. stolika: "< eklaracja tablicy z obiektow klasy box Malutki.set(3, 4); Duzy.set(15, 20); for (int i=0; i<=3; i++) TAB[i].set(i+ 1,10); cout < <"Malutki: "< < Malutki.Pole() < <"\t"; eout < <"Maly: "< < Maly.Pole() < <"\t"; cout < <"Duzy: "< < Duzy.Pole() < <"\n"; //Przegladamy kolejne obiekty z tablicy: cout < <"Z tablicy TAB: \n"; for (i=0; i<=3; i++) cout < < TAB[i].Pole() < <"\t"; cout < <"\n Dodatkowe Dane: \n"; cout < < Malutki.Jeszcze cosik() < <"\n"; cout < < Maly.Jeszcze cosik() < < "\n"; cout < <"Z tablicy: "< Pole(); c o u t < < "\ n"; pointer do OBJ- > Ustaw(30. 20); cout < < "NOWY po zmianie: " < < pointer do OBJ- > Pole(); co u t < < "\ n"; delete pointer do OBJ; return 0; Jest w dobrym tonie (w przypadku większych programów może mieć bardzo istotne znaczenie) skasowanie wskaŸnika do obiektu, jeœli obiekt nie jest nam już potrzebny. SłuŸy do tego operator delete (ang. usuń): delete pointer do OBJ;. Pozwala to na zwolnienie pamięci zajmowanej na stercie przez obiekt (obiekt znika) i wykorzystanie jej przez Twój program w innych celach (np. dla następnego obiektu, jeœli zażšdasz jego utworzenia). A oto jeszcze jeden przykład. Tym razem obiekty ustawiamy jak w wierszyku o rzepce. Dane i funkcje z jednago obiektu wykorzystujemy do wskazania następnego obiektu. Zapis BOX *NastepnyBox; należy rozumieć "obiekt zawiera wskaŸnik do następnego obiektu". Natomiast zapis BOX *Daj Pointer(void); oznacza: "funkcja bezparametrowa (void) zwraca wskaŸnik do obiektu klasy BOX" Uruchom i przeanalizuj program. ~ define DUZY large ~ include "iostream.h" class BOX { int DI; int Sz; BOX *NastepnyBox; //wskaŸnik do nast. obiektu public: BOX(void); //Konstruktor void Ustaw(int. int); int Pole(void); void Pokazuj(BOX *gdzie); BOX *DajPointer(void); 163 str 164 BOX::BOX(void) //Konstruktor DI = 10; Sz = 10; NastepnyBox = NULL; //Wskaznik na razie pusty-"donikad" void BOX::Ustaw(int a, int b) {DI = a; Sz = b;} int BOX::Pole(void) {return (DI * Sz);} //Ta metoda ustawia wskaŸnik na swoj argument wejsciowy void BOX::Pokazuj(BOX *gdzie) { NastepnyBox = gdzie; } //Ta metoda zwraca wskaŸnik do obiektu BOX *BOX::DajPointer(void) return NastepnyBox; main() BOX small, medium, large; //Trzy obiekty BOX *BOX~pointer; //wskaŸnik do obiektu klasy BOX small.Ustaw(2,3); Iarge.Ustaw(20, 30); cout < < "small - Malutki" < < small.Pole() < <"\n"; cout < < "medium - Maly" < < medium.Pole() < <"\n"; cout < < "large - DUZY" < < Iarge.Pole() < <"\n"; small.Pokazuj(&medium); medium.Pokazuj(&DUZY); // adres DUZEGO BOXu BOX~pointer = &small; //inicjujemy wskaznik do obiektu //Sprawdzamy na co wskazuje wskaznik: BOX~pointer = BOX~pointer- > Daj Pointer(); cout < < "Wskazany- > " < < BOX~pointer- > Pole(); cout < < "\n"; 6 BOX~pointer = &medium; BOX~pointer = BOX~pointer- > Daj Pointer(); cout < < "Nastepny- > " < < BOX~pointer- > Pole(); return 0; 164 str 165 NA CZYM POLEGA DZIEDZICZENIE. Aby przedstawić Ci mechanizm dziedziczenia wyobraŸmy sobie klasę obiektów POJAZDY. Obiektem klasy POJAZD może być np. SAMOCHÓD, ale nie tylko. Może nim być ROWER, POCIĽG itp. Jeœli rozważymy klasę, jako zbiór pewnych obiektów, to zbiór POJAZDÓW może zawierać podzbiory, np.: * rowerów * pocišgów a te można podzielić na podzbiory (podklasy) ** pocišgów towarowych ** pocišgów osobowych * samochodów a w tym (znowu podklasy) ** ciężarówek ** samochodów osobowych itd. itd. Dla takiej hierarchii zbiorów można również ustalić cechy wspólne -DZIEDZICZO- NE - po zbiorze wyższego rzędu i odrębne dla każdej podklasy. Mam nadzieję, że sens takiego rozumowania jest na tyle oczywisty, że możemy spróbować przetłumaczyć to na C++. Utworzymy klasę POJAZDY, której podklasš będš AUTKA. ~ include "conio.h" ~ include "iostream.h" elass POJAZD // klasa bazowa POJAZD public: int kola; int masa; POJAZD(void); //Konstruktor void Ustaw(int, int); void Dane(void); POJAZD::POJAZD(void) {kola=8; masa=1000;}; void POJAZD::Ustaw(int x, int y) { kola=x; masa=y;}; void POJAZD:: Dane(void) { cout < < "\n"; cout < < kola < < "\t"; cout < < masa; }; // Klasa pochodna AUTKO class AUTKO: public POJAZD 165 str 166 public: int numer; AUTKO(void); void Set(int); AUTKO::AUTKO(void) { kola=4; masa=1111; numer=0; }; void AUTKO::Set(int x) { numer=x; }; int i; main() //clrscr(); POJAZD Wagon; AUTKO Fiat; cout < < "\n"; Wagon.Dane(); Fiat.Dane(); for (i=1; i4; i++) Wagon.Ustaw(2*i, 2*i*1000); Fiat.Ustaw(i,100*i); Fiat.Set(i); Wagon.Dane(); Fiat.Dane(); cout < < " " < < Fiat.numer; cout < < "\n"; return 0; Aby okreœlić kto po kim dziedziczy stosuje się w C++ następujšcy zapis: class AUTKG: public POJAZD Oznacza to: "klasa AUTKO jest klasš pochodnš wobec klasy bazowej POJAZD". Po przeanalizowaniu i uruchomieniu programu bez trudu zidentyfikujesz, które cechy zostały odziedziczone po obiekcie klasy macieżystej. Poczšwszy od wersji 2.0 C++ zezwala także na dziedziczenie "wielobazowe". Wyjaœnimy sens takiego dziedziczenia na prostym przykładzie. Klasa obiektów KWAD- RATY może dziedziczyć cechy (inaczej być podzbiorem zbioru) zarówno od bardziej ogólnej klasy PROSTOKĽTY, jak i od klasy ROMBY, ponieważ kwadrat jest jednoczeœnie i szczególnym przypadkiem prostokšta i szczególnym przypadkiem rombu. Zwróć uwagę, że odziedziczonych po przodkach cech obiektu (ani pól - zmiennych: I66 str 167 kola, masa, ani metod: Ustaw(), Dane() ) nie musieliœmy już deklarować, ani definiować przy tworzeniu klasy pochodnej i obiektu Fiat. 14.1. Utwórz klasy i obiekty pochodne w dowolnie wybranym innym programie przykładowym. 14.2. W przykładzie [P-57) dokonaj hermetyzacji obiektów POJAZD i AUTKO. Jaka najmniejsza częœć obiektów musi pozostać w pub- licznej częœci i utworzyć interfejs? 167 str 168 Lekcja 1~ Funkcje wirtualne i polimorfzm Jeœli nie praktykowałeœ dotšd programowania obiektowego, to dwa pojęcia wymienione w tytule sš zapewne dla Ciebie zupełnym novum. Zaczniemy zatem ostrożnie. Najpierw rozbudujemy trochę naszš rodzin- kę obiektów z poprzedniej lekcji. Nazwy obiektów dobrane sš w taki sposób, że zrozumienie stopnia pokrewieństwa kolejnych obiektów powinno okazać się łatwe. Uruchom podany poniżej program przykładowy i zwróć uwagę na przesłanianie funkcji. Funkcja o takiej samej nazwie (podobnie jak zmienna globalna i lokalna) może w kolejnych pokoleniach obiektów uzyskiwać dodatkowe cechy. W C+ + przyporzšd- kowanie różnych działań funkcji o tej samej nazwie nie stanowi błędu. Jeœli spróbujesz poeksperymentować z programem i wydrukować zawartoœci innych pól obiektów, przekonasz się, że w przypadku braku konstruktora obiekt wprawdzie jest zakładany w pamięci (inaczej nie dałoby się wywołać funkcji - metody, gdyby obiekt i metoda nie istniały), ale zawartoœć pól przed ich zainicjowaniem jest przypadkowa. ~ include "conio.h" ~ include "iostream.h" class POJAZD { int kola; float masa; public: POJAZD (void); void Zglaszaj (void) { cout < < "Pojazd ! - Funkcja i masa do DZIEDZICZENIA \t"; cout < < masa < < "\n"; } POJAZD::POJAZD(void) { masa=100; }; // konstruktor class AUTKO : public POJAZD { int ilpasazerow; public: void Zglaszaj(void) // Metody sa, konstruktora brak { cout < < Autko\t"; cout < < To moja wlasna funkcja Zglaszaj() \n";~ 168 str 169 elass CIEZAROWKA : public POJAZD { int il~pasazerow; float ladunek; public: int Podaj~pasazerow(void) {return il~pasazerow;} elass ZAPRZEG : public POJAZD { int il~pasazerow; public: int Podaj~pasazerow(void) {return il~pasazerow;} main() POJAZD ROWER; AUTKO Fiat; CI EZAROWKA STAR; ZAPRZEG Fura; clrscr(); ' ROWER.Zglaszaj (); Fiat.Zglaszaj(); //Wywolanie metody w stos. do obiektu STAR.Zglaszaj(); Fura.Zglaszaj (); return 0; Aby Twój pierwszy kontakt z funkcjami i metodami wirtualnymi był w miarę możliwoœci łagodny, uproœcimy powyższy przykład i wprowadzimy słowo virtual do deklaracji (i jednoczeœnie definicji) funkcji: virtual void Zglaszaj(void) { eout < < "Pojazd \n";} Uruchom program. Jak widzisz nic strasznego się nie stało. Dziedziczenie następuje nadal według prostych i zrozumiałych zasad. Brak konstruktora nie powoduje zaniecha- nia utworzenia obiektu. ~ include "conio.h" ~ include "iostream.h" class POJAZD int kola; 169 str 170 float masa; public: virtual void Zglaszaj(void) { cout < < "Pojazd \n";} class AUTKO: public POJAZD int passenger; public: void Zglaszaj (void) cout < < "Autko cout < < "ja nie dziedzicze, ja przeslaniam... \n"; class CIEZAROWKA: public POJAZD { int passenger; float ladunek; public: int Pasazerow(void) {return passenger;} class ZAPRZEG: public POJAZD { int passenger; public: int Pasazerow(void) {return passenger;} void message(void) { cout < < "Zaprzeg \n";} main() clrscr(); POJAZD ROWER; AUTKO Fiat; CIEZAROWKA STAR; ZAPRZEG Fura; ROWER.Zglaszaj(); Fiat.Zglaszaj(); STAR .Zglaszaj ( ); Fura.Zglaszaj(); // A to sie nie uda: // Fiat=STAR; return 0; 170 str 171 Rezultat wykonania programu [P-63) Pojazd Autko: ja nie dziedzicze, ja przeslaniam... Pojazd Pojazd Metoda Zglaszaj(), która może zostać zadeklarowana jako wirtualna lub nie (patrz poniżej): * została zadeklarowana w częœci publicznej obiektu; * obsługuje interfejs obiektu; * może podlegać dziedziczeniu; * może podlegać przesłanianiu. Jeœli w klasie pochodnej nie zostanie w częœci publicznej zadeklarowana (lub od razu zdefiniowana) funkcja o takiej samej nazwie, to następuje dziedziczenie, w przeciwnym wypadku - następuje przysłanianie funkcji (na zasadzie "bliższa koszula ciału", czyli bliższa obiektowi jest jego własna funkcja. W programie poniżej sięgamy do funkcji Zgłaszaj posługujšc się nie nazwš obiektu, a wskaŸnikem do obiektu. Zapis pointer - > Zglaszaj (); należy rozumieć: Wywołaj funkcję Zglaszaj() odnoszšcš się do obiektu wskazanego przez wskaŸnik pointer. ~ include "conio.h" ~ include "iostream.h" class POJAZD int kola; float masa; public: /" virtual '/ void Zglaszaj(void) // Sprawdz ! { cout < < "Pojazd\n";} class AUTKO : public POJAZD int passenger; public: void Zglaszaj(void) {cout< <"To moje - Autko !\n";} class CIEZAROWKA: public POJAZD int passenger; float ladunek; 171 str 172 public: int Pasazerow(void) {return passenger;} class ZAPRZEG : public POJAZD int passenger; public: int Pasazerow(void) {return passenger;} void Zglaszaj(void) {cout < < "To moje - Zaprzeg";} main() POJAZD ~ROWER; AUTKO ~ Fiat; CIEZAROWKA "STAR; ZAPRZEG 'Fura; clrscr(); pointer = new POJAZD; ROWER- > Zglaszaj(); Fiat = new AUTKO; Fiat- > Zglaszaj ( ) ; STAR = new CIEZAROWKA; STAR - > Zglaszaj ( ) ; Fura = new ZAPRZEG; Fura- > Zglaszaj (); return 0; Zwróć uwagę na sposób deklaracji wskaŸnika. Podobnie jak w przypadku "zwykłych" zmiennych musimy wiedzieć jakiego typu (jakiej klasy) obiekt wskazuje wskaŸnik. Tu też wišże się to z problemem rozmieszczenia danych w pamięci. Słowo virtual zostało objęte znakami komentarza. Zlikwiduj te znaki i przekonaj się, że nie wpłynie to na sposób działania programu. Wiemy już zatem CZEGO WIRTUALNA METODA NIE ROBI. W następnym przykładzie postaramy się pokazać CO ROBI wirtualna metoda. Najprostszym sposo- bem, by przekonać się jak wpływa słowo virtual na działanie programu jest umieœcić go w programie raz tak /'virtual"/ void Zglaszaj(void) { cout ~ < "Pojazd !!!";} a raz tak virtual void Zglaszaj(void) { cout < < "Pojazd !!!";} 172 str 173 Zwróć uwagę, że operujemy w programie nie bezpoœrednio polami obiektu a wskaŸnikiem do obiektu. ~ include "conio.h" ~ include "iostream.h" class POJAZD { protected: //slowo kluczowe - dostep ograniczony int kola; float masa; public: POJAZD(void); virtual void Zglaszaj(void) { cout < < "Pojazd !!!";} POJAZD::POJAZD(void) { masa=9999; }; class AUTKO: public POJAZD { public: void Zglaszaj(void) {cout < < "Autko: m=" < < masa;} class CIEZAROWKA: public POJAZD { int passenger; float ladunek; public: int Pasazerow(void) {return passenger;} class ZAPRZEG: public POJAZD { public: ZAPRZEG (void); void Zglaszaj(void) { cout < < "ZAPRZEG: m=" < < masa;} ZAPRZEG::ZAPRZEG (void) {masa=444;}; main() clrscr(); POJAZD 'pointer; //Deklarujemy JEDEN wskaznik do obiektu pointer = new POJAZD; //Zainicjowanie pointer- > Zglaszaj (); //Wydruk delete pointer; //Skasowanie pointer = new AUTKO; //Zainicjuj nowy obiekt klasy AUTKO pointer- > Zglaszaj ( ) ; 173 str 174 delete pointer; pointer = new CIEZAROWKA; pointer- > Zglaszaj ( ) ; delete pointer; pointer = new ZAPRZEG; pointer- > Zglaszaj(); delete pointer; //kasujemy wskaxnik do obiektu return 0; W wyniku działania programu powinieneœ otrzymać następujšce wydruki * bez słowa virtual: Pojazd !!!Pojazd !!!Pojazd !!!Pojazd !!! * ze słowem virtual: Pojazd !!! Autko: Nie dziedzicze ! Pojazd !!! ZAPRZEG: Nie dziedzicze! Jak widać z przykładu obecnoœć słowa virtual wpływa na dziedziczenie bšdŸ przesłanianie funkcji Zgłaszaj(); . Pokusimy się teraz o bardziej precyzyjnš definicję. "Jeœli w klasie bazowej lub klasie pochodnej rzędu n występuje funkcja (metoda) wirtualna, to: * w następnych klasach pochodnych rzędu n funkcja o tej samej nazwie NIE POWINNA zostać poprzedzona słowem kluczowym virtual; * funkcję można wywołać TYLKO okreœliwszy jednoznacznie, do którego obiektu (jakiego typu obiektu) w danym momencie się odnosi; * jeœli wywołujemy funkcję wirtualnš, to możemy korzystać w programie przy wywoła- niu funkcji z takich aspektów tej funkcji, które zostały zdefiniowane póżniej, w następnych podklasach pochodnych. Funkcję, która posiada takie własnoœci nazywamy FUNKCJĽ POLIMORFICZNĽ (czyli funkcjš o zmiennej konstrukcji i zmiennym działaniu)". Należy podkreœlić, że funkcje polimorficzne działajš efektywnie przy jednoczesnym zastosowaniu (jak widać z powyższych przykładów): * F UNKCJI POLIMORFICZNEJ, * WSKANIKÓW DO OBIEKTÓW, * DYNAMICZNEGO ZARZĽDZANIA PAMIĘCIĽ (new, delete). PUBLIC, PRIVATE, PROTECTED. Pola, dane i funkcje mogš mieć jeden z trzech statusów: private - prywatne - dostępne dla funkcji (metod) wchodzšcych w skład (member) zarówno częœci prywatnej jak i interfejsu danej klasy. Dla reszty NIEDOSTĘPNE. public - publiczne - dostępne bez ograniczeń dla funkcji i operatorów (pamiętaj, że łańcuchy znaków wymagajš zastosowania funkcji strcpy() STRingCoPY zamiast opera- tora przyswojenia). 174 str 175 protected - chronione - dostępne dla funkcji własnych i funkcji interfejsowych (public) obiektów (klas) pochodnych. Dla reszty œwiata zewnętrznego NIEDOSTĘPNE. ~ include "conio.h" ~ include "iostream.h" class POJAZD { protected: int kola; float masa; public: POJAZD(void); virtual void Zglaszaj(void) { cout < < "Pojazd !!!";} POJAZD::POJAZD(void) { masa=9999; }; class AUTKO: public POJAZD { public: void Zglaszaj(void) {cout < < "Autko: m=" < < masa;} class CIEZAROWKA: public POJAZD { int passenger; float ladunek; public: int Pasazerow(void) {return passenger;} class ZAPRZEG: public POJAZD { public: ZAPRZEG (void); void Zglaszaj (void) { cout ZAPRZEG: m= masa;} ZAPRZEG::ZAPRZEG (void) {masa=444;}; main() clrscr(); POJAZD "pointer; //Deklarujemy JEDEN wskaŸnik do obiektu pointer = new POJAZD; //Zainicjowanie pointer- > Zglaszaj ( ); //Wydruk delete pointer; //Skasowanie pointer = new AUTKO; pointer- > Zglaszaj ( ) ; 175 str 176 delete pointer; pointer = new CIEZAROWKA; poi nter- > Zglaszaj ( ) ; delete pointer; pointer = new ZAPRZEG; pointer- > Zglaszaj () ; delete pointer; return 0; W wyniku działania programu powinieneœ uzyskać wydruk: Pojazd !!! Autko: m=9999 Pojazd !!! ZAPRZEG: m=444 Polimorfizm funkcji umożliwia ROZBL'DOWYWALNOŒĆ. Po zastosowaniu słowa kluczowego virtual nawet wtedy, gdy nasz projekt będzie się składał z wielu (nawet kompilowanych oddzielnie modułów) w kolejnych "pokoleniach" wolno nam roz- budowywać zarówno własnoœci zewnętrzne, jak i wewnętrzne obiektów - także funkcje. Dzięki istnieniu preprocesora definicje poszczególnych klas mogš się znajdować w dołšczanych plikach nagłówkowych *.H lub *.HPP. Spróbujmy wykonać to na przykładzie. ~ include "iostream.h" To jest plik POJAZD.H class POJAZD { protected: int kola; float masa; public: POJAZD(void); virtual void Zglaszaj(void) { cout < < "Pojazd !!!";} POJAZD::POJAZD(void) { masa=9999; }; AUTKO.HPP class AUTKO: public POJAZD { public: void Zglaszaj (void) {cout < < "Autko: m=" < < masa;} 176 str 177 CIEZAR.HPP class CIEZAROWKA: public POJAZD { int passenger; float ladunek; public: int Pasazerow(void) {return passenger;} ZAPRZEG.HPP class ZAPRZEG: public POJAZD { public: ZAPRZEG(void); void Zglaszaj(void) { cout < < "ZAPRZEG: m=" < < masa;} ZAPRZEG::ZAPRZEG(void) {masa=444;}; A nasz główny program wyglšdałby zapewne tak jak poniżej. W Turbo C+ + jest powszechnš praktykš, że jedne pliki korzystajš z funkcji, struktur, obiektów itp. zdefiniowanych w innych plikach. Często zatem KOLEJNOŒĆ DOŁĽCZANIA PLI- KÓW dyrektywš include może nie być obojętna. Jeœli tworzysz własne pliki, z których korzystasz w następnych programach, to powinieneœ dołšczać je w logicznej kolejnoœci. #/ include "iostream.h" ~ include "POJAZD.H" ~ include "autko.hpp" ~ include "ciezar.hpp" #/ include "zaprzeg.hpp" main() POJAZD *ptr; //wskaŸnik do obiektu klasy POJAZD ptr = new POJAZD; //Zainicjowanie wskaŸnika ptr- > Zglaszaj (); //Wywołanie funkcji delete ptr; //Skasowanie wskaŸnika ptr = new AUTKO; ptr->Zglaszaj(); delete ptr; ptr = new CIEZAROWKA; ptr- >Zglaszaj(); delete ptr; ptr = new ZAPRZEG; ptr->Zglaszaj(); delete ptr; return 0; Jeœli natomiast definicje wszystkich klas: POJAZD, AUTKO, CIEZARÓWKA, ZA- 177 str 178 PRZĘG umieœcimy w jednym własnym pliku POJAZDY.HPP, to nasz program~ przybierze jeszcze bardziej "eleganckš" formę: ~ include "iostream.h" ~ include "pojazdy.hpp" main() POJAZD 'ptr; ptr = new POJAZD; ptr->Zglaszaj(); delete ptr; ptr = new AUTKO; ptr->Zglaszaj(); delete ptr; ptr = new CIEZAROWKA; ptr->Zglaszaj(); delete ptr; ptr = new ZAPRZEG; ptr->Zglaszaj(); delete ptr; return 0; Aby nie powtarzać czterech identycznych niemal linii można zastosować zmiennš wyliczeniowš enum, tablicę zawierajšcš nazwy klas itp. Z racji skšpej objętoœci niniejszej ksišżki pozostawiam to już Twojej dociekliwoœci. W skrajnym przypadku polimorficzne funkcje wirtualne mogš w "pierwszym pokole- niu" nie zostać zdefiniowane WOGÓLE (!). Pozostawiamy sobie wtedy pełnš swobodę manewru. Funkcje takiego typu sš nazywane metodami w pełni wirtualnymi (ang. pure virtual). Przykładem takiej funkcji jest funkcja Mów() z przedstawionego poniżej przykładu. Zostawiamy jš w pełni wirtualnš, ponieważ różne obiekty klasy CZLOWIEK i klas pochodnych mogš mówić na różne sposoby... Obiekt Niemowle, dla przykładu, nie chce mówić wcale, ale z innymi obiektami może być inaczej. WyobraŸ sobie np. obiekt klasy Żona (żona to przecież też człowiek!). class Zona : public CZLOWIEK { public: void Mow(void); W tym pokoleniu definicja wirtualnej metody Mow() mogłaby wyglšdać np. tak: void Zona::Mow { COUt C < "JA NIE MAM CO NA SIEBIE WLOZYC !II"; cout C < "DLACZEGO KOWALSKI ZARABIA ZAWSZE WIECEJ NIZ TY ?!II"; //... itd., itd., itd... ~ include "conio.h" ~ include "iostream.h" 178 str 179 class CZLOWIEK { public: void Jedz(void); virtual void Mow(void) = 0; //Uwaga! funkcja WIRTUALNA void CZLOWIEK::Jedz(void) { cout < < "MNIAM, MNIAM..."; }; //To definicja metody //Funkcji Mow() nie definiujemy WCALE ! class NIEMOWLE : public CZLOWIEK { public: void Mow(void); // Tym razem BEZ slowa virtual void NIEMOWLE::Mow(void) { cout < < "Nie Umiem Mowic! \n"; }; NIEMOWLE Dziecko; main() clrscr(); Dziecko.Jedz(); Dziecko.Mow(); getch ( ) ; return 0; Taka klasa (w powyższym przykładzie klasa CZŁOWIEK), która zawiera funkcje w pełni wirtualne, nazywa się klasš ABSTRAKCYJNĽ. Jeœli spróbujesz dodać do powyższego pcogramu np.: CZLOWI EK Facet; Facet.Jedz(); uzyskasz komunikat o błędzie: Cannot create a variable for abstract class "CZLOWIEK" (Nie mogę utworzyć zmiennych dla klasy abstcakcyjnej "CZLOWIEK") KLASY ABSTRAKCYJNE. PAMIĘTAJ: * Po klasach abstrakcyjnych MOŻNA dziedziczyć! * Obiektów klas abstrakcyjnych NIE MOŻNA stosować bezpoœrednio! 179 str 180 15.1. Wyodrębnij definicje klas do plików *.HPP i sprowadŸ do postaci takiej, jak [P-66.1) poprzednie programy przykładowe. 180 str 181 Lekcja 16 Jak zobaczyć obiekt W trakcie tej lekcji dowie sz się co zrobić, by Twoje obiekty staly się bardziej "namacalae". JEŒLI CHCESZ ZOBACZYĆ SWOJE OBIEKTY... Podobnie jak poczštkujšcy automobilista trochę obawia się rozkręcić na œrubki swój samochód, tak i poczštkujšcy programiœci niechętnie korzys- tajš z DEBUGGERA. A przecież daje on Ci dodatkowe możliwoœci przeœledzenia zarówno działania Twoich programów, jak i dokładniej- szego poznania filozofii i sposobu działania C + + . Ponieważ "primo non nocere", co autor na swój użytek zawsze tłumaczy "przede wszystkim nie przestarszyć i nie zniechęcić", zostawiliœmy sobie ten kšsek na sam koniec, kiedy to mamy do czynienia z doœwiadczonym programistš, który przebrnšł już przecież przez obiekty i metody wirtualne. Nie wypada więc już ani mnie ani Tobie, Czytelniku udawać, że problem nie istnieje. Turbo C + + jest wyposażony w DEBUGGER zintegrowany, czyli wchodzšcy wraz z kompilatorem i edytorem w skład IDE. Możesz także korzystać z zewnętrznego debuggera instalujšc go przy pomocy programu konfigurujšcego TCINST, lub wykonu- jšc następujšce czynnoœci: * Rozwiń menu Options (opcje) [Alt]-[O). * Przełšcz opcję Full menu w stan On (jeœli trzeba). Menu zniknie a będzie nam jeszcze potrzebne. * Rozwiń menu Options powtórnie. * Wybierz opcję Debuggec... Pojawi się okienko dialogowe. Masz do dyspozycji: On - włšczony wła~y~‚ebugger IDE; Standalone - dołšczenie żewnętrznego debuggera; None - debugger wyłšczony. Turbo C+ + najchętniej współpracuje z Turbo Debuggerem Borlanda, wchodzšcym w skład niektórych pakietów Turbo C+ + oraz Turbo Pascal (np. Turbo Pascal 6.0 Prof.). Jeœli znasz już korzyœci wynikajšce z możliwoœci œledzenia programu w bliŸniaczym niemal IDE Turbo Pascala lub skutki zastosowania rozkazu TRON (TRace ON - włšcz œledzenie) w Basicu, to powitasz ten rozdział z ulgš (nareszcie!), jeœli nie - przeczytaj go uważnie, bo będzie Ci coraz częœciej brakować pomocy przy uruchamianiu programów. Zacznijmy od menu Run. Masz tam do dyspozycji następu~šce opc~e: 181 str 182 Go to cursor - wykonaj program do miejsca wskazanego kursorem [F4] Trace into - włšcz œledzenie [F7) Step over - włšcz œledzenie z możliwoœciš pominięcia funkcji [F8) Natomiast w menu Debug masz do dyspozycji między innymi: Inspect - dokonaj inspekcji stanu (np. wybranej zmiennej); Evaluate/modify - sprawdŸ stan/zmodyfikuj (np. wyrażenie). Wszyscy wiedzš, że przy pomocy debuggera można obejrzeć zmiennš bšdŸ funkcję. Nie wszyscyjednak wiedzš, że można zobaczyć przy pomocy debuggera także obiekty a nawet dziedziczenie cech przez obiekty. Przy pomocy programu poniżej, posługujšc się znakami semigraficznymi spróbujemy zobaczyć proste obiekty * na ekranie edytora w momencie tworzenia programu; * w okienku User screen w wyniku działania programu; * rozmieszczone w pamięci komputera, posługujšc się debuggerem. Program przykładowy zawiera pewnš nadmiarowoœć. Dziedziczenie nie jest wykorzys- tane praktycznie, by nie komplikować obrazu. Zostało jednak wprowadzone po to, byœ mógł zobaczyć przy pomocy debuggera skutki dziedziczenia. Jeœli masz wštpliwoœci, to sprawdŸ san, że znaki semigraficzne możesz w edytorze IDE wpisywać dokładnie tak samo, jak na "gołym" komputerze - przy pomocy * naciœnięcia i przytrzymania klawisza [Alt]; * wpisania numeru znaku w kodzie ASCII z małej klawiatury numerycznej; * puszczenia klawisza [Alt]. Potrzebne Ci "klocki" majš kody ASCII 176 - 224 (to też zaraz sprawdzisz przy pomocy debuggera. Wpisz i uruchom program. ~ include "dos.h" //funkca delay() - opoznienie # include "conio.h" ~ include "string.h" # include "iostream.h" ~ include "stdlib.h" //funkcja rand() - liczba losowa class AUTKO { int x,y; char string1 [12]; //Domyœlnie - default - private char string2[12]; public: AUTKO(void); //Zawiera deklaracje KONSTRUKTORA, void Rysuj(int, int); //oraz dwie METODY, void Kasuj(void); AUTKO::AUTKO(void) // definicja konstruktora 182 str 183 char *strl = ' char *str2 = " ~ O~ "; strcpy(string1, str1 ); //kopiujemy lancuchy znakow strcpy(string2, str2); // do obiektu - to rysunek obiektu void AUTKO::Rysuj(int a, int b) //def. metody Rysuj(...) // rysujšeej na ekranie x=a; y=b; gotoxy(x, y); cout < < string1; gotoxy(x, (y+ 1 ) ); cout < < string2; void AUTKO::Kasuj(void) //def. metody kasujšcej rysunek gotoxy(x, y); cout < < " ; gotoxy(x, (y+ 1 ) ); cout < < " ; class TIR: public AUTKO char stringl [12), string2[12]; int x,y; public: TIR(void); //Zawiera deklaracje KONSTRUKTORA, void Rysuj(int, int); void Kasuj ( ) ; TIR::TIR(void) // definicja konstruktora char *str1 = char *str2 = "I;™Ó~ÓI. strcpy(string1, str1 ); strcpy(string2, str2) ; void TIR::Rysuj(int dx, int dy) x=dx; y=dy; gotoxy(x, y); cout < < string1; gotoxy(x, (y+ 1 ) ) ; 183 str 184 cout < < string2; void TIR::Kasuj(void) //To nie jest DESTRUKTOR! //To jest M ETO DA! gotoxy(x,y); cout < <" ; gotoxy(x, (y+1 )); cout < <" ; AUTKO a1; TI R t1; main () int i, j; clrscr(); getch(); for(i=1; i<70; i++) a1.Rysuj(i,1 ); t1.Rysuj(i,10); delay (100); //poczekaj 100 milisekund a1.Kasuj(); tl .Kasuj(); for(i=1; i<70; i++) j=rand() o/o 2; //Generacja liczb losowych - "wyboje" t1.Rysuj(i,(15+j)); t1.Kasuj(); return 0; Program będzie najpierw oczekiwał naciœnięcia dowolnego klawisza (getch()) a następnie przez ekran przejadš Twoje obiekty. Skompiluj program: Compile Compile to OBJ [Alt]-[C], [Enter]. Jeœli do programu wkradły się jakiekolwiek błędy, skoryguj je. Uruchom program " normalnie" rozkazem Run. Drugi obiekt tl klasy TIR dzięki generowaniu liczb przypadkowych przez funkcję rand() (ang. random - przypadkowy) będzie jechał po "wyboistej" drodze. Jeœli wszystko się udało, przystępujemy do drugiej - najważniejszej częœci eksperymentu. l. Uruchom program powtórnie, tym razem przy pomocy rozkazu Trace into (włšcz œledzenie) z menu Run. Jeœli nie dokonałeœ w programie żadnych zmian, Turbo C+ + odstšpił od powtórnej kompilacji. Mignšł pusty ekran użytkownika User screen (pamiętasz, że Twoje obiekty czekajš na naciœnięcie klawisza). Na ekranie edytora pojawił się jasny pasek podœwiet- lenia, zaznaczajšc funkcję main(). Widzisz teraz na własne oczy, że właœnie od funkcji main rozpoczęło się wykonanie programu. 2. Naciœnij klawisz [F7]. Działa to tak samo,jak rozwinięcie menu Run i wydanie rozkazu Trace into, tylko szybciej. 184 str 185 Znów mignšł ekran użytkownika (nadal pusty), a pasek podœwietlenia przesunšł się na wywołanie funkcji clrscr(). 3. Naciskajšc nadal [F7) wykonujesz swój program krok po kroku. Zajrzeć co dzieje się na ekranie wyjœciowym możesz w każdej chwili przy pomocy kombinacji klawiszy [Alt]-[FS]. Opiszę NIE WSZYSTKIE, a tylko NAJWAŻNIEJSZE kroki. 4. Wskazanie przez pasek podœwietlenia wiersza al .Rysuj~; oznacza wywołanie funkcji Rysuj() w stosunku do obiektu al klasy AUTKO. Wywołanie funkcji w programie oznacza praktycznie odwołanie się do defmicji tej funkcji umieszczonej przed programem. Pasek podœwietlenia skacze do wiersza: void AUTKO::Rysuj(...) W œwietle tego, co widzisz staje się zrozumiałe, dlaczego C+ + wymaga, by definicje funkcji nie były zagnieżdżane. Pomyœl, gdyby funkcja Rysuj() została zdefiniowana wewnštrz innej funkcji, która, jak na złoœć nie została wywołana, DOKĽD POSZEDŁBY W TYM MOMENCIE pasek podœwietlenia ? Wykonujš się kolejne instrukcje umieszczone w definicji funkcji (czyli te, których życzy sobie funkcja Rysuj()). Wszystko przebiega w sposób prosty i zrozumiały aż do napotkania cout < < Przypomnijmy sobie, że jeszcze niedawno operator < < oznaczał przesunięcie bitów w lewo, dlaczego więc teraz nie przeœuwa? Zaraz się to wyjaœni. Po kolejnym przyciœnięciu [F7) następuje skok do pliku IOSTREAM.H do nowej definicji (operator overloading - nadpisanie, przedefiniowanie operatora). Widzimy tam między innymi: ostream::operator < < (operator oznacza przesłanie do strumienia wyjœciowego Output STREAM). Przy okazji każdego zapisu cout < < będziemy skakać do pliku IOSTREAM.H (zgodnie z logikš metody, którš obserwowałeœ przy wywołaniu funkcji Rysuj()). 5. Po dwu skokach do cout < < ma sens przejrzenie zawartoœci ekranu użytkownika przez [Alt]-[F5]. Zostały wyprowadzone dwa łańcuchy znaków, zobaczysz tam więć AUTKO w pozycji wyjœciowej do przejazdu przez ekran. Wróć naciskajšc [Enter] do edytora. 6. Pora zaprosić debugger we własnej osobie. * Rozwiń menu Debug [Alt]-[D). * Wybierz swojsko brzmišcy rozkaz Inspect. Przeprowadzimy inspekcję pierwszego obiektu: class AUTKO a1 185 str 186 7. Do okienka dialogowego Inspect wpisz nazwę obiektu al i naciœnij [Enter]. Pojawiło się okienko dialogowe "Inspecting al". x=1; y= I; to współrzędne AUTKA al na ekranie monitora w trybie tekstowym. Przesuwajšc pasek podœwietlenia wybierz w okienku łańcuch znaków semigraficznych string2 i naciœnij [Enter). Pojawiło się następne okienko dialogowe "Inspecting string2". Ważniejsze informacje o obiekcie zamieszczone w okienku to: string2 to tablica char [12) 8. Wybierz element tablicy string2[1) i naciœnij [Enter]. Pojawi się kolejne okienko "Inspecting string2[ 1 ]" . 8F80:OOB9 - to fizyczny adres pamięci RAM (segment : offset) pod którym umieszczony został obiekt. char 'O' 79 to znak - litera "O" (kółko autka), kod ASCII 79. 9. Wyłšcz okienka [Esc]-[Esc]-[Esc). Przejdziemy do inspekcji drugiego obiektu tl klasy TIR. 10. Wydaj rozkaz Debug Inspect. 11. Do okienka wpisz tl i naciœnij [Enter]. W okienku Inspecting tl widać rzecz zaskakujšcš. Okazuje się, że obiekt tl składa się w rzeczywistoœci z DWU OBIEKTÓW (!): * obiektu al klasy AUTKO; * właœciwego obiektu tl klasy TIR. Dzięki debuggerowi widzisz wyraŸnie CO ODZIEDZICZYŁ PO "PRZODKU" OBIEKT tl. Okazuje się, że odziedziczył praktycznie CAŁEGO "PRZODKA" 12. Wskaż w okienku AUTKO::string2 i naciœnij [Enter). Pojwi się okienko "Inspecting string2", wyglšdajšce bardzo podobnie do tego z punktu 7. Ale ZWRÓĆ UWAGĘ, ŻE FIZYCZNY ADRES PAMIĘCI JEST TYM RAZEM INNY!!! Widać więc, że pola obiektu klasy macieżystaj zostały faktycznie skopiowane (TO JEST WŁĽŒNIE DZIEDZICZENIE !) do obiektu klasy pochodnej. 13. Wycofaj się gaszšc kolejne okienka - dwa razy [Esc]. 14. Obejrzyj swoje obiekty w okienku dialogowym: Debug Evaluate / modify spróbuj zmodyfikować x, y, stringl, string2. 186 str 187 INSPEKCJA SZYBCIEJ. Zwróć uwagę, że możesz uzyskać takie same efeky szybciej, jeœli najpierw wskażesz kursorem w okienku edytora właœciwy obiekt (zmienne i funkcje to też przecież obiekty, tylko bardziej elementarne) i dopiero wtedy wydasz jeden z rozkazów: Ispect lub Evaluate / modify z menu Debug. Zastanawiasz się, zapewne, czy obiekty graficzne zastosowane zamiast semigraficznych nie okazałyby się ciekawsze. Być może, lecz nie wyglšdałyby tak prosto i przekonywajšco w okienku debuggera. 16.1. PrzeœledŸ przy pomocy debuggera inne programy obiektowe i nieobiektowe. Autor ma nadzieję, że niniejsza ksišżka pomogła Ci "przetrawić" Turbo C + + . Przyszła więc pora na ZADANIE OSTATNIE: 16.2. Przetłumacz na C+ + dowolny własny program napisany pierwo- tnie w Basicu lub Pascalu. Porównaj szybkoœć działania programu. PORA NA MORAŁY. KILKA UWAG O STYLU PROGRAMOWANIA. Ponieważ zbliżamy się nieuchronnie do końca, pora na wygłoszenie standardowego zestawu morałów, przestróg typu "uważaj, nie nadużywaj instrukcji goto", "pisz bez błędów" i kilku dobrych rad. Stałe i zmienne. Poza bardzo sympatycznym obyczajem nadawania stałym i zmiennym nazw ułat- wiajšcych zrozumienie ich roli i przeznaczenia w programie, pożytecznš manierš bywa rozróżnienie stałych i zmiennych przy pomocy wielkoœci liter. Maniera stosowana przez wielu programistów to STAŁE i Zmienne lub zmienne, ale także stałe (szczególnie te globalne). Przejęta po Pascalu maniera NazwaZmiennej , NazwaFunkcji lub nawyk starych "C-ologów" unikania dużych liter: nazwa zmiennej, nazwafunkcji() może okazać się równie komunikatywna. Jedyne uniwersalne kryterium to twardy pragmatyzm. We wszelkich sztukach inżynier- skich najlepsze jest takie rozwišzanie, które daje najlepsze efekty. Wiersze i wcięcia. Możesz oczywiœcie pisać swoje programy tak: void main(){clrscr();int x,y,z,t=O;char T[12] [12];for(") //itd. 187 str 188 wielu programistów stosuje jednak zasadę umieszczania jednej instrukcji w je~ wierszu i wcięć wyodrębniajšcych pewne czšœci programu. Program zapisany np. int TAB~LICZB[100) [10] main() int i, j; for(i = 1; i 100; i++) { for(j = 1; j < 10; j+ +) printf("o/od", i ); } getch ( ) ; jest z pewnoœciš znacznie łatwiejszy do rozszyfrowania, także dla samego autora po pewnym czasie. Komentarze. Pamiętaj, że komentarze nic nie kosztujš, natomiast ich brak (do takiego wniosku doszedłeœ zapewne sam w trakcie analizowania przykładowych programów z niniejszej ksišżki) powoduję często zbędnš stratę czasu. Instrukcja skoku bezwarunkowego goto. Istnieje powszechna niechęć do tej instrukcji, uzasadniona przez nadużywanie i nieu- miejętne stosowanie skoku goto. Wbrewjednak tej niechęci zło nie leży w samej instrukcji, którš przecież zawierajš wszystkie uniwersalne języki programowania, a w braku umiejętnoœci programistów. Każdš instrukcję można przecież przy odrobine dobrych chęci zastosować niewłaœciwie. Skutki dogmatyzmu opisuje firma Borland Int. w podręczniku "Turbo Debugger 2.0 Users Guide", rozdział "C - specific bugs" (typowe błędy specyficzne dla C) - zainteresowanych odsyłam. Na uwagę zasługuje fakt, że jest to dokładnie ten sam przykład, na bazie którego niekwestionowane autorytety - B. Kerninghan i D. Ritchie dowodzili 12 lat wczeœniej, że goto jest zbędne a conajmniej w złym stylu. Czy koniecznie muszę odkrywać Amerykę? Zaczęliœmy od grzechów C + + . Na zakończeniejeszcze o grzechach. C -ł- + jest bardzo popularnym narzędziem i w zwišzku z tym wielu producentów (nie tylko Borland i Microsoft) oferuje gotowe zestawy oprogramowania dla użytkowników C + + . Jedne nazywajš się LIBRARY i sš jak wynika z nazwy bibliotekami, inne nazywajš się DEVELOPMENT KIT i sš "zestawami narzędzi" do tworzenia aplikacji np. dla Windows, zastosowań w przemysłowych systemach sterowania - tzw. ROM - able programs, programów rezydentnych - TSR itp.. Zanim zabierzesz się za trudny problem rozejrzyj się po rynku. Wszelkie nauki i sztuki inżynierskie majš bardzo surowego egzaminatora - rzeczywis- toœć. I to ten egzamin muszš zdać przede wszystkim. Programowanie jest bez wštpienia praktycznš sztukš inżynierskš. 188 str 189 Jakikolwiek styl programowania wybierzesz dla siebie, bšdŸ konsekwentny. Jeœli bez problemów i szybko zorientujesz się jak działa Twój program obchodzšcy właœnie swoje pierwsze urodziny i równoczeœnie œwiętujšcy uruchomienie swojej 74 wersji, patrzšc na jego wersję sprzed roku, to będzie to oznaczać, że wybrałeœ dobry styl programowania. I to już niestety KONIEC. Parafrazujšc znane hasło reklamowe (nie jest to kryptoreklama, bo autor nie pamięta czyje to hasło...) ..DALEJ POBIEGNIECIE SAMI... 189 str 190 Dodatek A Dodatek zawiera odpowiedzi na najbardziej typowe pytania. Dowiesz się także jak samodzielnie uczyć się dalej. Autor ma nadzieję, że jeœli jeszcze nie podjšłeœ decyzji i nie dołšczyłeœ do tych milionów programistów na całym œwiecie, którzy dawno wybrali Turbo C + + jako swoje ulubione narzędzie pracy, to do takiej decyzjijest już coraz bliżej. Staniesz zatem przed typowym problemem: JAK UCZYĆ SIĘ DALEJ ? Niniejszy rozdział zawiera ujęte w formie pytań i odpowiedzi kilka praktycznych porad na przyszłoœć. Na końcu ksišżki znajdziesz spis literatury a na dyskietce kilka programów STUDIUM?.CPP, które pomogš Ci w dalszej samodzielnej nauce. PYTANIE 1: JAK ZAINSTALOWAĆ Turbo C + + ? OdpowiedŸ: 1. Zanim rozpoczniesz instalację: * Przekonaj się, czy masz tœ-10 MB wolnego miejsca na dysku. * Zrób zapasowš kopię dyskietek instalacyjnych. 2. Pakiet instalacyjny zawiera na dyskietce nr 1 program INSTALL.EXE . * Włóż dyskietkę do napędu. * Wydaj rozkazy: A: [Enter] lub B: [Enter] I NSTALL [Enter] Pojawi się winietka programu instalacyjnego. W wierszu statusowym program pod- powiada Ci, że masz do wyboru: ENTER - kontynuuj ESC - przerwij. wybierz [Enter]. 3. Pojawi się okienko tekstowe Enter the SOURCE drive to use A (podaj nazwę napędu Ÿródłowego, z którego instalujemy; domyœlnie przyjmowanejest A:) * teraz musisz wpisać poprawnie skšd instalujesz Turbo C + + , a więc B, C, D, itp. Program czeka na [Enter]. * jeœli zgrałeœ najpierw dyskietki instalacyjne na dysk stały i dokonujesz instalacji z dysku (tak jest szybciej), pojawi się dodatkowo pytanie o katalog, z którego instalujemy: 190 str 191 Enter the SOURCE Path (wpisz œcieżkę dostępu do katalogu Ÿródłowego; domyœlnie przyjmowanyjest ten katalog, z którego wystartował program INSTALL.EXE). 4. Pojawi się okienko z proponowanš konfiguracjš: Turbo C+ + Directory C:TC Binary Files Directory C:TCBIN Jeœli naciœniesz [Enter], pojawi się okienko tekstowe, do którego możesz wpisać nowš nazwę katalogu np.: D:TURBOC . Jeœli dokonasz zmiany, to inne katalogi dostosujš się do niej automatycznie. 5. Z czego można zrezygnować? Install Tour - instalacja programu - samouczka - TCTOUR.EXE. Klawisz [Enter] przełšcza Yes/No instaluj/nie instaluj. Unpack Examples - rozpakowanie (unzip) programów przykładowych. Jeœli wybie- rzesz No, zostanš skopiowane w wersji *.ZIP. Memory Models - modele pamięci. Jeœli naciœniesz [Enter), pojawi się okienko, w którym możesz wybrać te modele pamięci, które będš zain- stalowane. W trakcie tego kursu nie będš Ci przydatne największe modele pamięci LARGE i HUGE. Możesz z nich zrezygnować. 6. Wybierz opcję Start Installation [Enter]. Typowe kłopoty: Invalid directory name or format - nieprawidłowa nazwa katalogu lub format. Warning: There is not enough available disk space to install... UWAGA: Na dysku jest zbyt mało miejsca.... naciœnij [Esc] . PYTANIE 2: Do czego shiży program TCINST.EXE ? Odp.: Program TCINST.EXE służy do okreœlenia konfiguracji systemu Turbo C + + już po zainstalowaniu. Poniżej znajdziesz krótki opis najważniejszych opcji. W głównym menu masz do wyboru: SEARCH Direction (PRZESZUKIWANIE Kierunek) Scope (Przesłanianie) Orygin (od/do kursora) Case sensitive (kontekstowe) RUN (Uruchomienie programów z argumentami) OPTIONS Full menu (Pełne menu On/Off) Compiler (Parametry pracy kompilatora) Environment (Otoczenie) Swap To Memory (Swapowanie do pamięci a nie na dysk) 191 str 192 w tym do wyboru: Use EMS memory On/Off Use Extended Memory On/Off OPTIONS: Compiler : Code Generation : (sposób tworzenia kodu programu przez kompilator) -- Overlay support On/Off -- Model Chodzi o modele pamięci, domyœlnie przyjmowany jest Small. -- More! (a tam między innymi sposób emulacji arytmetyki zmiennoprzecinkowej) - - Floating point: Emulation None 8087 80287 (zestaw stosowanych rozkazów - dopasowanie do mikroprocesora) --- Instruction set: 8088/8086 80186 80286 OPTIONS : Compiler : C+ + Options (sposób postępownia kompilatora w stosunku do rozszerzeń wprowadzonych przy przejœciu z C do C + + ) - Use C+ + compiler: - - C + + always / CPP extention only (kompilator C+ + zawsze / tylko jeœli plik ma rozszerzenie *.CPP) - SOURCE (ródłowy tekst programu) - - Keywords (zestaw słów kluczowych) - Turbo C - ANSI (zgodny ze standardem ANSI C) - UNIX V - Kerninghan and Ritchie (zgodny z "klasycznym" C) Identifier length - długoœć identyfikatorów. Możesz wybrać liczbę 1...32 . PYTANIE 3: Jak korzystać z systemu suflera Turbo C+ + ? Odp.: System HELP Turbo C + + jest wielopoziomowy. Dostęp uzyskujemy przez [Alt]-[H] - rozwinięcie menu Help. Sposób poruszania się w systemie suflera pokażemy na przykładzie. 1. Naciœnij [Alt]-[H]. Rozwinie się główne menu systemu Help: Contens - zawartoœć Index - spis treœci Topic search - poszukiwanie zagadnienia Previous topic - poprzedni temat Help on help - jak korzystać z suflera. 192 str 193 2. Wybierz z menu opcję Index (I]. Pojawi się okienko ze spisem haseł. W celu wybrania hasła możesz: * naprowadzić pasek wyróżnienia na hasło przy pomocy klawiszy kursora i nacisnšć [Enter) lub * wpisać poszukiwane hasło z klawiatury. Przeszukiwanie nastšpi litera po literze. 3. Napisz GRAPHICS i naciœnij [Enter]. Pojawi się okienko z opisem pliku graphics.h i spis wszystkich funkcji dostępnych w trybie graficznym. 4. Napisz LINE . Pasek podœwietlenia przeskoczy do funkcji line. 5. Naciœnij [Enter]. Pojawi się okienko z opisem funkcji line (rysuj linię), w którym zobaczysz najpierw prototyp funkcji: void far line(int x1, int y1, int x2, int y2) Po okienku z tekstem opisu możesz także poruszać się przy pomocy klawiszy kursora. W okienku znajdziesz poniżej: See also - patrz także * listę funkcji podobnych (wystarczy wyróżnić i nacisnšć [Enter]); * przykładowy prosty program poshzgujšcy się danš funkcjš (line). 6. Przykładowy program możesz przenieœć do okienka edycyjnego (nie musisz przepisy- wać!). Jeœli chcesz to zrobić: * naciœnij [Alt]-[E]; Rozwinie się menu Edit. * wybierz z menu rozkaz Copy example - [C), (kopiuj przykład); Przykładowy program został skopiowany do przechowalni Clipboard. Jeœli chcesz go przejrzeć, masz do dyspozycji rozkaz Show clipboard (pokaż przechowalnię) z menu Edit. Jeœli chcesz przenieœć program do okienka edycyjnego masz do dyspozycji rozkaz Paste (wstaw) z menu Edit. 7. Teraz możesz skompilować i uruchomić przykładowy program i przekonać się jak działa wybrana funkcja. UWAGA: Wszystkie hasła napisane jasnym kolorem umożliwiajš przejœcie do następ- nego okienka w systemie HELP. Jeœli po wskazaniu hasła kursorem, wokół hasła pojawia się pasek wyróżnienia, to znaczy, że możesz nacisnšwszy [Enter), dowiedzieć się więcej. PYTANIE 4: Gdzie znaleœć dostępne kombinacje klawiszy ? Odp.: Stosowanie różnych kombinacji klawiszy może znacznie przyspieszyć pracę. Spis wszystkich kombinacji klawiszy znajduje się oczywiœcie w HELP. Aby się do niego dostać należy : 193 str 194 1. Rozwinšć menu Help [Alt]-[H]. 2. Wybrać opcję Contens (zawartoœć). 3. Najechać kursorem i wybrać [Enter] opcję Keyboard hot keys Wycofać się z systemu HELP możesz przez: * [Alt]-[F1] - poprzedni temat (poprzednie okienko) lub * [Esc] . Do uaktywnienia systemu HELP możesz użyć także klawisza [F1]. Bardzo przydatna w praktyce okazuje się także kombinacja [Ctrl]-[F1]. PYTANIE 5: Czym różniš się TC.EXE i TCC.EXE ? Odp.: TCC.EXE jest kompilatorem wywoływanym z linii rozkazu systemu DOS - bez edytora, okienek, menu itd. Rozkaz: tc program.c spowoduje start Turbo C++ i załadowanie do okienka edycyjnego wskazanego programu. Aby rozpoczęła się kompilacja musisz wydać dodatkowo rozkaz Make, Run itp. Natomiast rozkaz: tcc program.c spowoduje od razu podjęcie próby kompilacji programu do wersji EXE lub COM, zależnie od wybranych przez Ciebie parametrów komplikacji. PYTANIE 5: Co oznacza komunikat: Unable to open include file 'stdio.h' Odp.: Różne dziwne komunikaty takiego typu mogš oznaczać, że do Twojego pliku CON- FIG.~YS nie została dodana linia: FILES=20 która umożliwia systemowi Turbo C + + otwarcie do 20 plików jednoczeœnie i w efekcie poprawnš pracę TC.EVGE. Innym, często występujšcym powodem może być niewłaœ- ciwe ustawienie katalogów: Options: Directories... Include Directory - katalog zawierajšcy pliki *.H i *.HPP. PYTANIE 6: Jak wydrukować program? Odp.: Jeœli znajdujesz się w oknie edycyjnym systemu Turbo C + + , naciœnij kombinację klawiszy [Ctrl]-[K]-[P]. Możesz także wybrać polecenie Print z menu Edit. PYTANIE 7: Czy Turbo C + + może korzystać z rozszerzonej pamięci EMS i/lub XMS? Odp.: Tak. Należy uruchomić Turbo C+ + z odpowiednim parametrem: TC /E lub TC /X 194 str 195 PYTANIE 8: Jak pozbyć się zbędnych okienek otwieranych przy uruchomieniu Turbo C? Odp. : Do poruszania się pomiędzy okienkami służš kombinacje klawiszy [Alt]-[F3) - Zamknięcie okna; [Alt)-[1], [Alt)-[3] itp - Przejœcie do okna o podanym numerze. (F6] lub menu Window (okna) [Alt]-[W] i rozkazy: Close - zamknij Zoom - powiększ okno na czły ekran List - lista okien itp. Posługujšc się tymi poleceniami możesz pozamykać zbędne okna. PYTANIE 9: Jak zainicjować tryb graficzny w programie ? Odp. : Żadna funkcja graficzna nie zadziała jeœli nie zainicjujesz graficznego trybu pracy. Shzży do tego funkcja initgraph(). Przykład: ~ include "graphics.h" void main() int KARTA GRAF, TRYB GRAF; KARTA = DETECT; //Sprawdz sam jaka jest karta initgraph( &KARTA GRAF, &TRYBGRAF, "sciezka" ); closegraph(); Sciezka - oznacza œcieżkę dostępu do plików typu *.BGI. Standardowo jest to: "C:\TC\BGI" Jeœli potrzebny plik *.BGI jest w bieżšcym katalogu, to piszemy wtym miejscu pusty cudzysłów pod hasłem Opisy funkcji garficznych znajdziesz w suflerze Turbo C+ + Help: Contens: Graphics oraz Help: Index: GRAPHICS.H. PYTANIE 10: Jeœli wiemy, z jakš k~tš graficznš mamy do czynienia, to jak dołšczyć właœciwy plik *.BGI do swojego programu ? Odp. : Turbo C + + jest wyposażony w program narzędziowy BGIOBJ.EXE, który przetwarza drivery kart graficznych w pliki *.OBJ. Możesz zatem wykonać konwersję: BGIOBJ EGAVGA.BGI - > EGAVGA.OBJ a następnie dołšczyć plik EGAVGA.OBJ przy pomocy konsolidatora TLINK.EXE. 195 str 196 PYTANIE 11: Czy można zmienić wielkoœć stosu ? Odp. : Tak. Standardowo jest tworzony stos o wielkoœci 4K (4096 bajtów). Jeœli chcesz zmienić wielkoœć stosu, to masz do dyspozycji zmiennš globalnš stklen. Poza defmicjami funkcji możesz umieœcić jš w dowolnym miejscu programu: stklen = 6000; PYTANIE 12: Jak rozmieœcić w pamięci dane o wielkoœci przekraczajšcej 64 K ? Odp. : Masz do dyspozycji słowa kluczowe far i huge a do długich liczb całkowitych typ long int. Przykład ich wykorzystania: char far TABL1 [64000L]; char huge TAB2[120000L]; Modele pamięci stosowane przez Turbo C + + . * Tiny Wszystkie rejestry segmentowe majš tę samš wartoœć CS = DS = ES = SS. Stosowane sš wskaŸniki bliskie typu near. Dane + kod + stos < = 64 KB. Programy można przekształcić z postaci EXE na postać COM. * Small (domyœlny). Segment kodu i segment danych to dwa różne, nienakładajšce się segmenty. Kod < = 64 KB. Dane + stos < = 64 KB * Medium Kod < = 1MB (wskaŸniki typu far). Dane + stos < = 64 KB (wskaŸniki typu near) * Compact Kod < = 64 KB. Dane < = 1 MB. * Large I dane i kod ograniczone do 1MB. Stosowane wskaŸniki typu far, funkcje typu far * Huge Pozwala na utworzenie kodu programu do 1MB oraz wielu segmentów danych (po 64 KB każdy) plus 64 KB przeznaczone na stos. Przełšczanie modelu pamięci odbywa się przy pomocy menu Options: Options: Compiler: Code generation. W okienku dialogowym "Code generation" po prawej stronie znajduje się sekcja "Model" dotyczšca stosowanego przy kompilacji i konsolidacji modelu pamięci. Na dyskietce znajdziesz programy STUDIUM?.CPP, które pozwolš Ci przeœledzić kilka mechanizmow, na które, ze względu na skromnš objętoœć ksišżki zabrakło już miejsca. 196 str 197 Dodatek B Dla zaawansowanych i majšcych ambitne plany ZAMIAST WSTĘPU Ci, którzy chcieli jeszcze niedawno tworzyć programy dla œrodowiska Windows skazani byli na zakup Software Development Kit (SDK) firmy Microsoft i Microsoft C. Od momentu wypuszczenia na rynek Borland C++ 2.0 monopol został przełamany. Obecnie (marzec 93) rynek tworzenia aplikacji dla Windows został podzielony na dwie grupy - użytkowników Microsoft C/C + + 7 oraz Borland C + + 3.0. Borland C+ + 3.0 W poczštku ubiegłego (1992) roku pojawił się na polskim rynku Borland C + + v. 3.0. Ten kolos wymagajšcy dla pełnej instalacji ok. 50 MB (!) oferuje użytkownikowi kompletne œrodowisko (bez potrzeby dodatkowych zakupów) umożliwiajšce tworzenie programów dla Windows. Pakiet po instalacji na dysku zajmuje wprawdzie "tylko" niewiele ponad 40 MB, ale do poprawnego przebiegu procesu instalacji program instalujšcy potrzebuje ponad siedem MB. Pakiet Borland C++ & Application Frameworks zawiera "ObjectWindows" służšce do tworzenia aplikacji przeznaczonych do pracy w œrodowisku Windows a także bibliotekę EasyWin ułatiwjšcš opracowanie takich aplikacji. Dla zaawansowanych programistów pakiet ten oferuje kilka dodat- kowych programów narzędziowych. BASM - assembler, który można stosować "wewnštrz" programów tworzonych wC++. TA 3.0 - Turbo Assembler. TD 3.0 - Turbo Debugger. TP 2.0 - Turbo Profiler. BCC - oddzielny kompilator (odpowiednik TCC v. 1.0). Specjalnie dla Windows: Turbo C+ + for Windows. ObjectBrowser - przeglšdarka, pozwalajšca na przejrzenie m. in. struktury klas (bazowych - pochodnych); Resource Workshop - zestaw umożliwiajšcy łatwe generowanie menu, ikon itp.; Turbo Vision for C + + ; WinSight - program ułatwiajšcy usuwanie błędów z programów przeznaczonych dla œrodowiska Windows; a także RC (Resource Compiler) - kompilator pozwalajšcy dodawać zasoby (ikony, okienka itp.) bezpoœrednio do plików EXE. HC (Help Compiler) - kompilator kontekstowej pomocy dla œrodowiska Windows. 197 str 198 FASCYNUJĽCY, CHOĆ SKOMPLIKOWANY ŒWIAT Windows. Programowanie dla Windows ma kilka aspektów wyraŸnie odróżniajšcych programy aplikacyjne od "zwyczajnych" aplikacji przeznaczonych do pracy w œrodowisku DOS. W œrodowisku Windows program oferuje użytkownikowi do wyboru w formie obiektów graficznych na ekranie (ang. visual objects) WSZYSTKIE MOŻLIWE OPCJE. Można zatem powiedzieć w pewnym uproszczeniu, że odpowiedzialnoœć za sposób działania programu przejmuje użytkownik. Taki sposób programowania, polegajšcy na "złożeniu użytkownikowi pełnej oferty", pobraniu decyzji i przekazaniujed do programu w celu dalszej obsługi zyskał nazw‡ "event - driven programming" - programowania zdarzeniowego. Programy typu event - driven różniš się od "zwykłych" programów specyfcznš strukturš. W celu zapewnienia poprawnej i jednoczeœnie możliwie szybkiej reakcji programu na "wydarzenie" (event - ualnoœć) program jest dzielony na niewielkie moduły. Każdy moduł zajmuje się obsługšjednej z możliwych ewentualnoœci. Œrodowisko Windows posługuje się najczęœciej obiektami kilku prostych klas - klawiszami, okienkami tekstowymi itp.. Obiekty te sš wzajemnie niezależne i mogš pojawiać się na ekranie w różnych konfiguracjach. Programy dla Windows, zmuszone do "rozpatrywania" ogromnej iloœći możliwych wydarzeń majš tendencję do "puchnięcia". Aby umożliwić skrócenie programu stosowa- najest w nich specyficzna konwencja oznaczeń tzw. KONWENCJA WĘGIERSKA (ang. Hungarian Notation) polegajšca na zastosowaniu przedrostki"w (ang. prefix) okreœ- lajšcych typ. Znajomoœć tej konweneji powinna pomóc Ci w zrozumieniu działania i organizacji programów przeznaczonych dla Windows. Prefix Skrót Znaczenie a array tablica b bool zmienna logiczna by unsigned char znak (bajt) c char znak cb count of bytes liczba bajtów cr color reFerence value okreœlenie koloru cx, cy short (count x, y len.) x-iloœć, y-długoœć dw unsigned long double word podwójne słowo fn function funkeja h handle .,uchwyt" i integer całkowity n short or int krótki lub całkowity np near pointer wskaŸnik bliski pointer wskaŸnik długi lp long pointer wskaŸnik typu long int s string łańcuch znaków sz string terminated \'ll' łańcuch ASCIIZ tm text metrie miara tekstowa w unsigned int (word) słowo x,y short x,y coordinate współrzędne x,y 198 str 199 W przykładach zastosowania notacji zwróć uwagę na możliwoœć kombinacji przedrost- ków. CString hello = Ahoj; c - łańcuch znaków. pszMoje; p-wskaŸnik do s-łańcucha znaków z-zakończonego przez '\0' pod nazwš "Moje". Uzbrojony w wiedzę i trochę doœwiadczeń możesz teraz sam rozpoczšć zwiedzanie. Oprócz przykładów w pakiecie Borland C+ + 3.0 masz do dyspozycji wersje Ÿródłowe biblioteki wykonawczej (runtime library), Turbo Vision i ObjectWindows. Możesz zatem sam przekonać się jak to działa. Ideš niniejszej ksišżki, majšcej raczej z założenia charakter wprowadzenia a nie encyklopedii, nie jest opisanie meandrów programowania dla Windows. Podanie w DODATKU tej garstki szczegułów ma na celu jedynie zasygnalizować istnienie takiego problemu i ułatwić Ci zrozumienie podtawowych różnic. Autor jest głęboko przekonany, że i Ty wybierzesz kiedyœ (może jeszcze nie w tym tygodniu) właœnie Windows jako œrodowisko pracy niektórych własnych programów użytkowych. STRUKTURY W C+ + TEŻ MOGĽ DZIEDZICZYĆ! Wielu Autorów piszšcych na temat C+ + opisuje dziedziczenie w taki sposób, że u Czytelnika może pojawić się mylne przekonanie, jakoby właœciwoœć ta była dostępna tylko dla klas i obiektów. Wyjaœniamy zatem: * dziedziczenie zostało wprowadzone przez C+ + (stšd rozszerzenie CPP w przy- kładzie); * dziedziczyć mogš także struktury. Próba przekompilowania zamieszczonego poniżej przykładu np. przy pomocy Turbo C 2.0 nie powiedzie się. W przykładzie operujemy strukturami punkt, kolo i elipsa. [STRUCT.CPP] //Przyklad dziedziczenia na strukturach ~include "stdio.h" ~include '~conio.h" struct punkt int x; //wspolrzedne punktu na ekranie int y; struct kolo: punkt int promien; //wspolrzedne srodka x,y dziedziczymy 199 str 200 struct elipsa: kolo //dziedziczymy x,y i promien int mniejszy~promien; punkt P; //deklarujemy trzy struktury kolo C; elipsa E; main() clrscr(); P.x = C.x = E.x = 1; //Nadajemy wartosci polom struktur P.y = C.y = E.y = 2; C.promien = E.promien = 4; E.mniejszy~promien = 3; //Sprawdzamy zawartosc pol struktur. Jak widzisz, //nastapilo dziedziczenie ! printf(""/od %d "/od "/od "/od "/od \n", P.x, C.x, E.x, P.y, C.y, E.y); printf(""/od %d "/od", C.promien, E.promien, E.mniejszy~promien ); getch(); return 0; Po uruc;homieniu programu przekonasz się, że pola x, y i promień podlegajš dziedziczeniu zgodnie ze znanymi Ci zasadami. Zwróć uwagę, że w strukturze domyœlnie przyjmowany jest status pól "public" - dostępne, nie musimy zatem korzystać z metod. W stosunku do struktur możemy posługiwać się operatorem przypisania i zewnetrznš funkcjš printf(). Bardzo podobnie rzecz ma się z konstruktorem i destruktorem. Także i tę, charakterys- tycznš dla C + + technikę można zastosować wobec struktur. Zwróć uwagę na drobnš różnice: * W przypadku dziedziczenia wobec struktur nie stosujemy słowa kluczowego public. 200 str 201 [STRKONS.CPP] //Przyklad konstruktora struktury ~include "stdio.h" ~include "conio.h" struct punkt int x; //wspolrzedne punktu na ekranie int y; struct kolo: punkt int promien; //wspolrzedne srodka x,y dziedziczymy //KONSTRUKTOR STRUKTURY kolo: kolo() x= 10; y = 20; promien = 30; punkt P; //deklarujemy dwie struktury kolo C; //KONSTRUKTOR zainicjuje strukture float const PI = 3.14; main() clrscr(); //Sprawdzamy zawartosc pol struktury kolo: printf(""/od "/od "/od \n, C.x, C.y, C.promien ); printf("Pole wynosi: "/of", (PI * C.promien * C.promien) ); getch(); return 0; 201 str 202 Opisane powyżej cechy, działajšce niejako wstecz, pozwalajš użytkownikowi na rozbudowę istniejšcych już wczeœniej programów napisanych w klasycznym, nieobiek- towym C bez potrzeby: * opanowania nowego języka programowania; * tworzenia programu od nowa. O PROGRAMACH PRZYKŁADOWYCH. Program przykładowy STUDIUM 1.CPP stanowi tekst żródłowy programu narzędzio- wego AMCOPY.EXE. Jest to narzędzie pozwalajšce cišć duże pliki na fragmenty, których wielkoœć może wybrać sam użytkownik stosownie do możliwoœci posiadanego sprzętu - pojemnoœci dyskietek. Trzeci parametr wywołania programu podzial (o- znaczajšcy wielkoœć fragmentu pliku W BAJTACH) jest opcjonalny. Jeœli nie wystšpi, program podzieli plik wejœciowy w taki sposób, by wykorzystać całš wolnš przestrzeń na dyskietce, którš włożysz do napędu. Dyskietki używane do kopiowania muszš być wczeœniej sformatowane, natomiast NIE MUSZĽ BYĆ PUSTE. Program AMCOPY nie uszkodzi plików znajdujšcych się na dyskietce docelowej . Autor ma nadzieję, że program AMCOPY okaże się dla Ciebie przydatnym w praktyce narzędziem. A oto tekst programu. [STUDIUM 1.CPP] //C. Adam Majczak - "C/C+ + w 48 godzin"; 1993. //STUDIUM 1.CPP - program do podzialu duzych plikow na fragmenty //UWAGA: Moze zostac skompilowany z zastosowaniem modelu COMPACT //LARGE i HUGE. //Jesli masz MS-DOS 5.0 lub //MS-DOS 6.0 skopiuj AMCOPY.EXE do C:\DOS ~include "dir.h" ~include "dos.h" ~include "stdio.h" ~include "conio.h" ~include "io.h" ~include "alloc.h" ~inelude "stdlib.h" ~include "string.h" ~include "ctype.h" ~define Bufor 32767 ~define IloseElementow(item) (sizeof(item)/sizeof(item[0])) long dlogosc; ~define DIugNazwy 70 char PIikWe[DIugNazwy]; char drive[2] ; 202 str 203 char curdrive; struct Dyskietki long I I BajtowWe; char *Nazwa; } TabDysk[4] = { { 362496L, "360k" }, { 1213952L, "1.2M"}, { 730112L, "720k" }, { 1457664L, "1.44M"} char *Kopia = "AMCOPY."; char Kopiaf Nazwa [14) ; int KopiaNrPl = 0; long FunIleMiejsca(char drive); int main(int argc, char **argv) long wolne, przeczytane, ZostaloBajtow; long podzial = OL, temp, do odczytu; int i, ile dyskietek, flaga = 0; void *bufptr; unsigned odczytano, reszta; FILE *str we, *str wy; for(") if(argc < 3 ; ; argc > 4 ; ; flaga == 1 ) printf("\nProgram narzedziowy do kopiowania duzych plikow\n\n"); printf("Tworzy kopie AMCOPY.001, AMCOPY.002, AMCOPY.003 itd\n"); printf("ktore po skopiowaniu na dysk mozesz polaczyc \n"); printf("w jeden plik zwyczajnym DOSowskim COPY:\n\n"); printf("COPY /B AMCOPY.001 +AMCOPY.002 DUZYPLIK.EXT\n\n); printf("Skladnia: AMCOPY {podzial}\n\n); printf("Przyklad:\n\n"); printf("AMCOPY D:\TC\BIN\TC.EXEA:\nlub\n"); printf("AMCOPY C:\PLIK.EXE B:100000 - UWAGA: liczba w bajtach\n\n); printf("...nacisnij cosik..."); getch(); if(flaga) break; else exit(1 ); strcpy( PIikWe, strupr(argv [1 ] ) ) ; 203 str 204 curdrive = getdisk(); curdrive+ + ; drive[0] = '(argv[2]); drive[0] = (char) toupper(drive[0]); drive[1 ] = (char) (drive[0] - A + 1 ); if (argc == 4) podzial = atol(argv[3]); if (!(str we = fopen(PIikWe, "rb"))) printf("Nie moge otworzyc pliku %s\n", PIikWe); exit(1 ); if(!(bufptr = malloc(Bufor))) printf("Zbyt malo RAM, nie moge utworzyc bufora...\n"); exit(1 ); dlugosc = filelength(fileno(str we)); wolne = FunIleMiejsca(drive[1 ] ); if(wolne dlogosc) printf("o/os ma o/old bajtow \n", PIikWe, dlogosc); printf( " Potrzebujemy: \n" --\n"); for(i = 0; i < IloscElementow(TabDysk); i++) if(TabDysk[i].IIBajtowWe > dlogosc) ile dyskietek = 1; else ile dyskietek = (int)(dlogosc / TabDysk[i].IIBajtowWe); if(ile dyskietek == 0) ile dyskietek++ ; if((reszta = (unsigned)(dlogosc o/o TabDysk[i].IIBajtowWe)) > 0) ile dyskietek++ ; printf("o/o7s o/o5d dysko/os\n", TabDysk[i].Nazwa, ile dyskietek, (ile dyskietek > 1 ? "ietek" : "ietki")); for(ZostaloBajtow = dlogosc; ZostaloBajtow != OL; ) if(drive[1 ] != curdrive) 204 str 205 printf("Wloz dyskietke "/od do kieszeni "/oc\n", KopiaNrPl, drive[0) ); printf("Start kopiowania - dowolny klawisz lub ~ C - STOP\n"); if(getch() == 3) exit(0); wolne = FunIleMiejsca(drive[1]); do odczytu = (wolne < ZostaloBajtow ? wolne : ZostaloBajtow); if(podzial > OL && podzial < do odczytu) do odczytu = podzial; sprintf(KopiafNazwa,""/oc:"/os"/o0.3d",drive[0],Kopia,+ + Kopia NrPl); printf("\nKopiuje "/old bajtow z "/os do "/os\n", do odczytu, PIikWe, KopiafNazwa); if(!(str wy = fopen(KopiafNazwa, "wb"))) printf("\nNie moge utworzyc pliku: "/os\n", KopiafNazwa); exit(1 ); for ( przeczyta ne = 0 L; przeczyta ne < do odczytu; przeczytane + = odczytano) temp = Bufor; if(do odczytu-przeczytane > temp) odczytano= Bufor; else odczytano = (unsigned)(do odczytu - przeczytane); odczytano = fread(bufptr, sizeof(char), odczytano, str we); if(odczytano == 0) break; if((fwrite(bufptr,sizeof(char),odczytano,str wy)) != odczytano) printf("\nBlad zapisu "/os !!! ", KopiafNazwa); exit( 1 ) ; printf(" "); if(kbhit()) if(getch() == 3) break; printf("\n"); 205 str 206 ZostaloBajtow -= przeczytane; fclose(str wy); fclose(str we); free(bufptr); flaga + + ; return 0; long FunIleMiejsca(char drive) long WoIneBajtow; long avail; struct dfree diskfree; getdfree(drive, &diskfree); if(diskfree.df sclus == Oxffff) printf("Niewlasciwy naped: "/oc\n", drive + 'A' - 1 ); exit(1 ); WoIneBajtow=((long)diskfree.df avail*diskfree.df sclus*diskfree. df bsec) ; return WoIneBajtow; Nazwy zmiennych, pól i funkcji zostały w programie dobrane w taki sposób, by ułatwić Ci zrozumienie działania programu. 206 str 207 Pełny spis literatury: 1. Peter Norton - Inside the IBM PC, Brady Books, New York 1986. 2. B.W Kerninghan, D. M. Ritchie - Język C, WNT 1987. 3. Borland Int. - Turbo C+ + Users Guide. Borland Int. 1990. 4. Borland Int. - Turbo C+ + Programmers Guide, Borland Int. 1990. J. Bielecki: 5. Od C do C+ + programowanie obiektowe w języku C, WNT 1990. 6. Język C interpretacja standardu, WNT 1988. 7. Turbo C dla programistów WKiŁ 1989. 8. A. Ragen - Leksykonjęzyka C, WNT 1990. 9. C. H. Small - How C+ + works - special report, EDN 6.IX.1992, European edition. 10. S. Mikes - New Ways to Program in Object-Oriented X, UnixWorld, McGraw Hill Inc. August 1992. 11. J. R. Hidalgo - Languages, PC Magazine 12.X1.1992. 12. R. Wacławek - Turbo C v.2, Intersoftland 1990. 13. E. Kosek - Programowanie w języku C, skrypty uczelniane, wyd. II, Politechnika Œlšska 1991. 207 str 208 PROPONUJEMY PAŃSTWU ORYGINALNE OPROGRAMOWANIE DO KOMPUTERÓW PC Posiada~y w sprzedaży produkty nastgpujšcych fir~ ~ ALDUS APPLICATION TECHNIQUES BLAISE COMPUTING BORLAND COMPUTER ASSOCIATES COREL SYSTEMS HORSTMANN SOFTWARE LOGITECH LOTUS MICROGRAFX MICRORIM MICROSOFT NOVELL OWL/PROSOFT PC KWIK CORPORATION SYMANTAC TOYOGO WORD PERFECT oraz inne Wszystkie programy sš licencjonowane i sprzedawane przez nas legalnie za zgodš producentów (Application Techniques, Microrim, PC Kwik Corporation, Toyogo) lub dystrybutorów (pozostałe). Zakupu programu w naszej firmie daje dostęp do wszystkich zwišzanych z nim bieżšcych informacji. Każdy program objętyjest gwarancjš. Po nabyciu niektórych programów istnieje możliwoœć (jeżeli przewiduje to producent) przyszłego zakupu ich nowszych wersji po bardzo niskiej cenie (tzw. upgrade). Programy sprzedajemy także wysyłkowo (za poœrednictwem Servisco). Polecamy także duży wybór ksišżek o tematyce komputerowej. Prosimy pytać o nasze ksišżki w księgarniach technicznych na terenie całego kraju. Służymy fachowš informacjš i gwarantujemy rozsšdne ceny oraz zniżki przy większych zakupach. Zapraszamy także Dealerów, którym gwarantujemy zniżki już przy zakupie jednego programu. Nasze aktualne ogłoszenie ukazuje się w czasopiœmie "PC KURIER". Zapraszamy ~ ~~~~~0~~~~~( 00-116 WARSZAWA, ul. Œwigtokrzyska 30 m.143, tel./fax 20-36-17 Stały adres dla korespondencji: 02-777 Warszawa, ul. Kulczyńskiego 24 m. 9 ~ ~~~~~ w u~ ~ rarrner Borland A uthorized 0ealer Lotus Authorised 0ealer