Typ wyliczeniowy enum KURS C/C++ WYKŁAD 5 Istnieje inna często wygodniejsz niż deklaracja const metoda nazywania stałych całkowitych Deklaracja: enum {WRITE, READ, DELETE); definiuje trzy stałe całkowite WRITE, READ, DELETE, które nazywamy stałymi wyliczanymi i przypisuje im kolejno wartości całkowite 0,1,2. Deklaracja ta jest równoważna zapisowi: const WRITE = 0; const READ = 1; const DELETE = 2; Wyliczenie może mieć nazwę. enum RIGHT {WRITE, READ, DELETE); Nazwa wyliczenia staje się odrębnym typem. enum nazwa_typu { lista wyliczeniowa } Przykład: enum miesiace {styczeń, luty, marzec, kwiecień, maj, czerwiec}; enum miesiace {styczen =1, luty, marzec, kwiecien=6, maj, czerwiec=luty+marzec}; enum miesiace {styczen =1, luty, marzec, kwiecien, maj, czerwiec}; W ten sposób zdefiniowaliśmy nowy typ o nazwie miesiace. Definiujemy zmienną tego typu: miesiace mm; Sposób użycia tych zmiennych jest następujący: mm = styczen; if (mm = = luty) {...} Kompilator ostrzega przy przypisaniach np.: mm = 2; powinno być mm = miesiace (2); mm = 3; mm = miesiace (3); mm = miesiace (23); // nie ma błędu, pakiet Borland 3.1 Kompilator sygnalizuje błąd przy przypisaniu: styczeń = 1; ponieważ zmienne wyliczeniowe sa traktowane jak constans. // enum {BLACK, BLUE, GREEN, CYAN, RED, MAGENTA);
enum {black, blue, green, cyan, red, magenta); czyli black = 0, blue = 1, itd textcolor (green); cprintf ( Zmiana koloru na zielony ); printf ( \n ); Operacje we/wy Język C/C++ nie posiada zdefiniowanych instrukcji realizujących operacje we/wy. Do tego celu służą funkcje znajdujące się w standardowych bibliotekach. standardowe strumienie predefiniowane wejścia/wyjścia stdin - strumień wejściowy (konsola) stduot - strumień wyjściowy (konsola) stderr - strumień komunikatów błędów (konsola) stdaux - strumień pomocniczy (konsola) stdprn - strumień drukarki np.: fputs ( Nazwa firmy, stdprn); Funkcja printf(); Funkcje printf() i scanf(), służą do realizacji tzw. formatowanego we/wy. Składnia funkcji jest następująca: int printf (const char *format [,argument,...]); Parametr format wskazuje tzw. łańcuch formatujący. Pewne sekwencje tego łancucha są specjalnie traktowane przez funkcje printf(). int a=1; float b=2.45; char c= d ; printf("wartość zmiennej a = %d zmiennej b = %4.2f \ zmiennej c = %c zmiennej d = %s \n",a,b,c, C++ ); char text[]= C++ ; printf("wartość zmiennej a = %.2d zmiennej b = %4.2f \ zmiennej c = %c zmiennej d = %s \n",a,b,c,text); Aby wydrukować znak % trzeba napisać: %% Ogólny format sekwencji formatującej przedstawia się następująco: %[flagi][szerokość][.precyzja][h l L]znak-typu.
flagi: flagi znaczenie default - dosunięcie argumentu do lewego krańca jego pola znak tylko dla ujemnej + liczba zawsze ze znakiem +/- # dla formatu x,x, poprzedzenie liczby 0x, 0X bez znaków wiodących dla formatu o, poprzedzenie liczby 0 dla formatu e,e,f wstawia zawsze znak kropki szerokość: n - wyświetlanych jest przynajmniej n znaków. Jeśli wartość zajmuje mniej niż n znaków szerokość pola jest uzupełniana dodakowymi spacjami z lewej lub prawj strony w zależności od ustawionej flagi. float y=10.96 printf ("y=%5.2f ",y); y=10.96 printf ("y=%10.2f ",y); y= 10.96_ printf ("y=%#.0f ",y); y=11. int i = 12; printf ("y=%#x",y); y=0xc. int y=23; printf ("y=%6d",y); y = 23_ printf ("y=%-6d",y); y =23 _ printf ("y=%+6d",y); y = +23 _ znak_typu: znak typu argument wejściowy argument wejściowy d,i całkowity liczba dziesiętna ze znakiem o całkowity liczba ósemkowa bez znaku x,x całkowity liczba szesnastkowa bez znaku u całkowity liczba dziesiętna bez znaku
c znak pojedynczy znak s wskaźnik łańcucha ciąg znaki łańcucha, aż do wystapienia \0 lub tyle ile określono w precyzji f zmiennoprzecinkowy [-]m.ddd gdzie liczbę cyfr d określa precyzja (domyślnie 6). e, E zmiennoprzecinkowy [-]m.dde+-xx. Przed kropką dokładnie jedna cyfra, liczba cyfr po kropce zależna od precyzji(domyślnie 6). g, G zmiennoprzecinkowy liczba będzie wyświetlana w postaci e lub f w zależności od zadanej precyzji. Nie wypisuje zbędnej kropki dziesiętnej i nie znaczących zer. y=0.000002; printf ("y=%f y=%g y=%e",y); wyświetla: y=0.000002 y=2e-06 y=2.000000e-06 y=2000000; wyświetla: y=2000000.000000 y=2e+06 y=2.000000e+06 modyfikatory: modyfikator h l znaczenie argument traktowany jak typu short int dla znaków typu d,i,o,u,x,x argument traktowany jak long int dla znaków typu d,i,o,u,x,x dla znaków e, E, g, G jako typu double L argument traktowany jak long double dla znaków typu e,e,f,g,g precyzja:.0 - dla e, E, f nie jest drukowany znak kropka dziesiętna liczba zero nie zostanie wydrukowana.n znak typu d,i,o,u,x,x znaczenie n cyfr znaczących zostanie wydrukowanych, jeśli jest ich mniej
uzupełnienie zerami z lewej strony., jeśli więcej nie będzie obcięta. e,e, f s n cyfr znaczących zostanie wydrukowanych po przecinku dziesiętnym. Ostatnia cyfra jest zaokraglana. co najmniej n znaków zostanie wydrukowanych long double y=2.1e45; printf ("y=%le,y); y = 2.100000e+45 printf ("y=%.2le,y); y = 2.10e+45 printf ("y=%lf,y); long int y=50000; printf ("y=%ld,y); y = 50000 printf ("y=%d",y); y=902 printf ("y=%.6d",y); y=000902 char nazwa[]="microsoft"; printf ("%.5s", nazwa); Micro Przyjmowane precyzje domyślne: 1 - dla znaków typu d, i,o, u, x, X 6 - dla znaków typu e, E, f - wszystkie cyfry znaczące dla znaków typu G,g - łańcuchy są drukowane do pierwszego znaku \0., znaków c nie dotyczy. Jeśli wartość zajmuje mniej niż n znaków szerokość pola jest uzupełniana dodatkowymi z lewej lub prawj strony w zależności od ustawionej flagi. spacjami Funkcja scanf() Funkcja scanf() służy do realizacji formatowanego wejścia. Składnia tej funkcji jest następująca: int scanf (const char* format [,address,...]); Parametr format wskazuje tzw. łańcuch formatujący. łańcuch ten zawiera sekwencje określające typy wprowadzanych danych. Funkcja wczytuje znaki ze standardowego wejścia, interpretuje je zgodnie ze specyfikacjami zawartymi w formacie i zapamiętuje wyniki w miejscach określonych przez pozostale argumenty. Argumenty muszą być adresami, określają adres obszaru pamięci gdzie dane mają być umieszczone. int i,j;
scanf("%d%d", &i, &j); Ogólny format sekwencji formatującej przedstawia się następująco: %[*][szerokość][h l L] znak-typu. * oznacza, że dane pole zostanie odczytane ale nie zostanie zapamiętane we wskazanym miejscu szerokość znak-typu określa maksymalną liczbę znaków danego pola tak jak opisane w funkcji printf(), dodatkowo można używać dużych liter: D, O, I, U scanf ("%d,%d,%d", &d, &m, &r); - wprowadzane dane oddzielane muszą być przecinkami. float f; scanf ("%5f", &f); wprowadzamy 1234567 printf ("f=%f", f); f=12345.0 char tab[20]; scanf ("%7s", tab); wprowadzamy Wolszczan printf ("s=%s", tab); s=wolszcz Funkcja gets(), puts() char *gets (char *s); // #include <stdio.h> Funkcja odczytuje znaki ze standardowego strumienia wejściowego (stdin) i umieszcza je w łańcuchu s; char znaki[20]; gets (znaki); Czytanie jest przerywane po napotkaniu znaku końca wiersza, ktry zostaje zamieniony na znak końca łańcucha(null). int puts (const char *s); char znaki[20]= Przykład wyprowadzania łańcucha na ekran ; puts (znaki); Ekran: Przykład wyprowadzania łańcucha na ekran Strumienie predefiniowane C++ Wprowadzanie i wyprowadzanie informacji można potraktować jako strumień bajtów płynący od źródła do ujścia. Jeśli chcemy zmiennej x nadać wartość wczytaną z klawiatury, wtedy strumień bajtów płynie od urządzenia zewnętrznego - klawiatura do tego miejsca w pamięci przeznaczonego dla zmiennej x. Dla operacji we/wy kompilator predefiniuje kilka strumieni (predefiniuje czyli zakłada stumień, otwiera strumień i po zakończeniu programu zamyka strumień) : cout, cin, cerr. Aby skorzystać z tych strumieni w programie trzeba umieścić dyrektywę: #include<iostream.h> podobnie jak dla C <stdio.h>.
Ze strumieniem wyjściowym cout został stowarzyszony operator << << insert operator (put to wstaw do strumienia cout) Ze strumieniem wyjściowym cin został stowarzyszony operator >> >> extract operator (get from pobierz ze strumienia cin ) Przykład #include<iostream.h> void main(){ } int x = 123; cout<<x; Można wysyłać różne typy zmiennych i stałych przy pomocy jednej instrukcji np. int x=123; cout<<"wartość x = "<< x <<'.' W przykładzie tym, za pomocą jednej instrukcji przesłano na ekran napis Wartość x =, wartość zmiennej i pojedynczy znak kropki. Na ekranie pojawi się napis: Wartość x = 123. Poszczególne typy oddzielone są operatorami <<. W C++ ze strumieniem cout współpracuje zestaw manipulatorów dec, oct, hex. Zmieniają one bieżący format dla całkowitego argumentu. Przykład #include<iostream> main(){ } int x=123; cout<<dec<<x<<' ' <<oct<<x<<' ' <<hex<<x; Przykład pokazuje jak można wyświetlić wartość całkowitą w trzech systemach liczbowych: dec, oct, hex. Wynik działania programu to :123 173 7b. Ze strumieniem wejściowym cin został stowarzyszony operator >>, odpowiadający za wczytanie informacji ze strumienia wejściowego (którego początek jest na klawiaturze), czyli program Przykład. void main(){ int x; cout<<"wprowadź wartość x :\n"; cin>>x; // >> pobieranie ze strumienia płynącego z klawiatury
} cout<<"wartość x = "<<x; powoduje wczytanie ze strumienia wejściowego (klawiatury) wartości do zmiennej x. Jak widać operacje we/wy dla standardowych typów realizowane są bardzo prosto. Nie musimy pamiętać o formacie wprowadzania i wyprowadzania wyników, a strumienie cout i cin interpretują je prawidłowo. Trochę trudniej przedstawiają się te operacje dla typów zdefiniowanych przez użytkownika, ale o tym opowiemy dużo później. Mamy również możliwość specyfikacji szerokości pola, precyzji i sposobu wypełniania pola. Do tego celu służą funkcje składowe klasy o nazwie ios. Są nimi: int width (int) - określa minimalny rozmiar pola, dotyczy tylko jednej następnej operacji we/wy int width () - zwraca bieżący rozmiar pola int precission (int) - dokładność wypisywania liczb zmiennoprzecinkowych. Przez domniemanie=6. Efekt trwały. int precission() - zwraca bieżąca wartość precyzji int fill (int) - znak wypełniający obszar. Efekt trwały int fill () - podaje bieżący parametr. Przykłady wykorzystania funkcji i manipulatorów formatujących int liczba; cout.width (9); cout.fill ( * ); cout<<liczba; ******107 - musi być wywołana przed operacją na strumieniu char string[12]; cin.width(11); cin>>string - nie ma wpływy na liczby, a jedynie na stringi. Zapobiega przekraczaniu zakresu char napis[7]; cin.width (sizeof(napis)); cin>>napis; //1 miejsce jest przeznaczane na \0 Odpowiednikiem tych funkcji są manipulatory które można wstawić do strumienia: setw (długość) <=> cout.width (dlugosc) setfill (znak) <=> cout.fill (znak) setprecision <=> cout.precision (liczba); setw(9); setfill ( * ); setprecision(2); // muszą być umieszczone w strumieniu setprecision(n); - działa do odwołania
setfill ( * ); - działa do odwołania. setw(n); - dla następnej wyprowadzanej wartośći. liczba=67.237; cout<< setw(9) << setfill ( * ) << setprecision(2)<<liczba; cout<<f; Ekran:****67.24 Porównanie bibliotek stdio oraz iostream Jak widać z powyższej analizy, język C++, wprowadził znaczne uproszczenia w realizacji mechanizmów we/wy. Ponadto, biblioteka stdio ma wiele wad, które zniwelowały w/w mechanizmy. Należą do nich między innymi: 1. printf(), scanf() są funkcjami ze zmienną ilością argumentów. Kompilator nie ostrzega więc o przypadku niedostosowania formatu do listy argumentów np.: printf( %i %f %d, a, b); 2. nie są kontrolowane przypadki złego typu argumentów (patrz wyżej) 3. dla funkcji scanf, jeśli liczba argumentów jest mniejsza niż w formacie - efekt jest nie zdefiniowany, w przeciwnym przypadku, dane są pobierane choć nie zdefiniowane. Mieszanie bibliotek Pomiędzy predefiniowanymi strumieniami stdio... cout... nie ma żadnej łączności. W związku z tym, gdy mieszamy operacje we/wy realizowane mechanizmami C i C++, możemy spodziewać się nieprzewidzianych efektów z ich realizacją. cout<< tekst1 ; printf( tekst2 ); cout<< tekst3 ; printf( tekst4 ); Powyższy zapis nie musi wcale prowadzić do oczekiwanego rezultatu: tekst1tekst2tekst3tekst4. Może być np. tekst1tekst3tekst2tekst4 lub odwrotnie. Strumień cout jest buforowany a stdout nie. Inna jest więc zasada wypisywania. Język C++ przewidział mechanizm synchronizujący oba te strumienie. Jest nim funkcja ios::sync_with_stdio(); Powinna być użyta na początku programu. Użycie tej funkcji powoduje jednak utratę szybkości, ponieważ strumienie przestają byc buforowane.