Rozszerzenia języka C Typ wyliczeniowy - prz
Transkrypt
Rozszerzenia języka C Typ wyliczeniowy - prz
Rozszerzenia języka C Typ wyliczeniowy Typy wyliczeniowe poszerzają możliwości definiowania stałych (ułatwiają grupowanie stałych związanych z daną nazwą typu) i są stosowane wszędzie tam, gdzie wygodniej jest operować nazwami, a nie wartościami liczbowymi. enum nazewnik {lista elementów} lista zmiennych; lub typedef enum nazewnik (lista elementów) nazwa typu; Przykład: enum karta {dwojka, trojka, ... , walet, dama, krol, as}; enum karta talia_1, talia_2; Poszczególnym elementom danego typu wyliczeniowego przypisane są wartości całkowitoliczbowe. Wartości domyślne: stałe wyliczeniowe otrzymują standardowo wartości całkowite 0,1,2, itd. np. enum kolory {czarny, niebieski, zielony, fioletowy, bialy}; czarny =0 niebieski = 1 zielony = 2 fioletowy = 3 bialy =4 Wartości ustalone: stałe wyliczeniowe mogą otrzymać wartości narzucone z góry np. enum poziomy {niski=100, sredni=500, wysoki=2000}; niski = 100 sredni = 500 wysoki = 2000 W przypadku, gdy po wartości narzuconej z góry następują stałe którym nie przypisano wartości, stałe definiowane są automatycznie przez kompilator np. enum kolory {czarny= –1, niebieski, zielony, fioletowy=10, bialy}; czarny = –1 niebieski = 0 zielony = 1 fioletowy = 10 bialy = 11 Operacje na zmiennych typu wyliczeniowego w języku C (jak na zmiennych typu całkowitego): enum kolory {CZARNY, NIEBIESKI, ZIELONY, FIOLETOWY, BIALY}; enum kolory k1; k1=0; k1=(enum kolory)1; k1=NIEBIESKI; k1=BIALY+2; k1++; if(k1==ZIELONY) printf(”Zielona jest trawa”); Ponieważ elementy składowe typu wyliczeniowego są stałymi definiowanymi, dla lepszego rozróżnienia ich w tekście programu zaleca się je pisać dużymi literami. #include <stdio.h> main() { enum auta {SYRENKA, FIAT125P, TRABI, ZAPORO, ZASTAWKA} au; enum asortyment {AUTA, WALCE, DESKI, TRAMPKI, LALKI=8} as; au=SYRENKA+2; switch (au) { case SYRENKA: case FIAT125P: printf(”\nNasze dobre auto”); break; case TRABI: printf(”\nModel biedny, ale zachodni”); break; case ZAPORO: case ZASTAWKA: printf(”\nModel post-kom...”); break; default: printf(”\nNiewiadomo co to za model”); break; } as=TRAMPKI; printf(”\nAsortyment = %d”,as); if(au==TRAMPKI) printf(”\n Trabant jest trampek!”); else printf(”\n Trabant nie jest trampek!”); } Wydruk: Model biedny, ale zachodni Asortyment=3 Trabant nie jest trampek! Parametry funkcji main Funkcja main może posiadać parametry formalne. Oznacza to, że program może być wywoływany (uruchamiany) z listą parametrów aktualnych napisanych w linii zlecenia systemowego. Funkcja main posiada następujący nagłówek: void main(int lpar, char *par[]) gdzie: lpar – oznacza liczbę parametrów wywołania, par – tablica dla zmiennych tekstowych o rozmiarze lpar. Ogólna postać linii zlecenia jest następująca: nazwa_programu parametr_1 parametr_2 ... parametr_n np. #include <stdio.h> main(int lpar, char *par[]) { int n; printf(”liczba parametrow funkcji main = %d\n”,lpar); for(n=0; n<lpar; n++) printf(”Parametr [%d] = %s\n”,n,par[n]); } Dla wywołania: program aaa bbb cccc zielony niebieski karo Wynik: Liczba parametrow funkcji main = 7 Parametr [0] = program.exe Parametr [1] = aaa Parametr [2] = bbb Parametr [3] = cccc Parametr [4] = zielony Parametr [5] = niebieski Parametr [6] = karo Przykład programu kopiującego plik. Zakłada się, że nazwy pliku wejściowego i wyjściowego przekazywane są jako parametry wywołania funkcji main. #include <stdio.h> main (int lpar, char *par[]) { FILE *plik_we, *plik_wy; int znak; plik_we=fopen(par[1],”r”); plik_wy=fopen(par[2],”w”); while((znak=fgetc(plik_we))!=EOF) /*czytanie*/ putc(znak,plik_wy); /*kopiowanie*/ fclose(plik_we); fclose(plik_wy); } Uruchomienie programu: program nazwa_pliku_we nazwa_pliku_wy np. program plik plik_kop program plik.c con program con plik.txt W przypadku braku parametru wywołania na ekranie pojawi się komunikat o błędzie: Null pointer assignment Funkcje ze zmienną liczbą parametrów #include <stdio.h> #include <stdarg.h> #include <string.h> main() { void sumal(long *suma, int n, ...); void sumat(char *text, int n, ...); long suma1=0, suma2=0; int dod=5; char text1[100]=””, text2[200]=””, napis[]=”komputer”; sumal(&suma1, 3, 4, 5, 6); printf(”suma1=%ld \n”,suma1); sumal(&suma2, 9, 1, 2, 3, 4, dod, 6, 7, 8, 9); printf(”suma2=%ld \n”,suma2); sumal(&suma2, 5, 1, 2, 3, 4, dod); printf(”suma2=%ld \n”,suma2); sumat(text1, 2, ”aaaaa”, ”bbbb”); printf(”tekst1 = %s\n”,text1); sumat(text2, 4, ”Program ”, ”w jezyku C ”, napis, ” Pentium IV”); printf(”tekst2 = %s\n”,text2); strcpy(napis,”CD-ROM”); sumat(text2, 3, ” + drukarka”, ” + ”,napis); printf(”tekst2 = %s\n”,text2); } /* sumal – sumowanie liczb calkowitych*/ void sumal (long *suma, int n, ...) { va_list stan; va_start (stan, n); while (n--) *suma+=va_arg(stan,int); va_end(stan); } /* sumat – ”sumowanie” tekstow*/ void sumat (char text[], int n, ...) { va_list stan; va_start (stan, n); while (n--) strcat(text, va_arg(stan,char*)); va_end(stan); } Wydruk: suma1=15 suma2=45 suma2=60 tekst1=aaaaabbbb tekst2=Program w jezyku C komputer Pentium IV tekst2=Program w jezyku C komputer Pentium IV + drukarka + CDROM Wykorzystano trzy makroinstrukcje biblioteczne i typ pomocniczy va_list. Makroinstrukcja va_start dokonuje przygotowania do udostępnienia parametrów; makroinstrukcja va_arg udostępnia kolejne parametry określonego typu, reprezentowane przez ... (trzykropek); makroinstrukcja va_end kończy czynności związane z udostępnianiem parametrów reprezentowanych przez trzykropek. Ogólna postać wywołania tak opracowanych funkcji jest następująca: nazwa_funkcji (&zm, lp, par1, par2, ..., parn); gdzie: zm lp par1 ... parn – oznacza nazwę zmiennej wynikowej, – liczba parametrów bieżącego wywołania, – kolejne parametry wywołania (określonego typu). Funkcja max – wartość największa z kilku liczb #include <stdio.h> #include <stdarg.h> main() { void max(int *imax, int n, ...); int a=12, b=55, c=33, d=99, e=44, f=77, imax; max(&imax, 6, a, b, c, d, e, f); printf(”Max =%d\n”,imax); max(&imax, 4, a, b, c, e); printf(”Max =%d\n”,imax); } void max(int *imax, int n, ...) { va_list stan; int pom; *imax=-32767; va_start (stan, n); while(n--) { pom=va_arg(stan, int); if(pom>*imax) *imax=pom; } va_end(stan); } Wynik: Max =99 Max =55 Funkcje otwarte: Funkcja zamknięta – akcje wykonywane będą od początku do końca w sposób nieprzerwany – bez oddania sterowania. Funkcja otwarta – wykonuje część swoich akcji, zwraca sterowanie do innej funkcji, po pewnym czasie otrzymuje sterowanie z powrotem, wykonuje dalsze akcje, znowu oddaje sterowanie, itd. Funkcja otwarta pracuje w przeplocie czasowym z innymi funkcjami. Typowym przykładem aplikacyjnym mogą być procesy równoległe obsługiwane przez jeden procesor lub symulacja systemów dyskretnych zdarzeń. Rozważmy program, który realizuje pracę funkcji otwartych w sposób skuteczny, aczkolwiek nieco prymitywny, przy użyciu instrukcji skoku („zakazanego”) goto oraz etykiet kontynuacyjnych. #include <stdio.h> enum etykiety_kontynuacji {ET1, ET2, ET3, ET4, ET5, ETmax}; main() { void funkcja1 (int et, int a, float b); void funkcja2 (int et, float x, int y); int i; /* sekwencja uporzadkowanych wywolan*/ for(i=0; i<2; i++) { funkcja1(ET1, i+5, 7.7); funkcja2(ET1, 8.8, i+6); funkcja1(ET2, i+9, 1.1); funkcja2(ET2, 5.5, i-6); funkcja1(ET3, i-5, 1.1); funkcja2(ET3, 5.5, i+6); } i=10; /*wywolania ”swobodne”*/ funkcja1(ET3, i+5, 7.7); funkcja2(ET1, 8.8, i+6); funkcja1(ET3, i+9, 1.1); funkcja2(ET4, 5.5, i-6); funkcja1(ET1, i-5, 1.1); funkcja2(ET2, 5.5, i+6); funkcja2(ET3, 8.5, i-6); } void funkcja1(int et, int a, float b) { switch (et) { case ET1: goto et1; case ET2: goto et2; case ET3: goto et3; default: printf(”\nFunkcja1...”); printf(” niepoprawna kontynuacja!”); return; } et1: /*@@@@ - sekwencja zaraz po starcie*/ printf(”\nFunkcja1 (po et1) a=%d”, a); return; et2: /*@@@@ - dalszy ciag*/ printf(”\nFunkcja1 (po et2) b=%g”, b); return; et3: /*@@@@ - dalszy ciag*/ printf(”\nFunkcja1 (po et3) b=%g”, b); return; } void funkcja2(int et, float x, int y) { switch (et) { case ET1: goto et1; case ET2: goto et2; case ET3: goto et3; default: printf(”\nFunkcja2...”); printf(” niepoprawna kontynuacja!”); return; } et1: /*@@@@ - sekwencja zaraz po starcie*/ printf(”\nFunkcja2 (po et1)”); if(y>(int)x) { printf(”y wieksze od x”); return; et2: /*@@@@ - dalszy ciag*/ printf(”\nFunkcja2 (po et2) x=%g”, x); return; } else { printf(”x wieksze lub rowne y”); return; et3: /*@@@@ - dalszy ciag*/ printf(”\nFunkcja2 (po et3) y=%i”, y); return; } } Wynik: Funkcja1 (po et1) a=5 Funkcja2 (po et1) x wieksze lub rowne y Funkcja1 (po et2) b=1.1 Funkcja2 (po et2) x=5.5 Funkcja1 (po et3) b=1.1 Funkcja2 (po et3) y=6 Funkcja1 (po et1) a=6 Funkcja2 (po et1) x wieksze lub rowne y Funkcja1 (po et2) b=1.1 Funkcja2 (po et2) x=5.5 Funkcja1 (po et3) b=1.1 Funkcja2 (po et3) y=7 Funkcja1 (po et3) b=7.7 Funkcja2 (po et1) y wieksze od x Funkcja1 (po et3) b=1.1 Funkcja2 ... niepoprawna kontynuacja! Funkcja1 (po et1) a=5 Funkcja2 (po et2) x=5.5 Funkcja2 (po et3) y=4 Program bardziej zdyscyplinowany, każdy proces ma swoje lokalne atrybuty a w wywołaniach podawana jest tylko wartość etykiety kontynuacji. #include <stdio.h> enum etykiety_kontynuacji {ET1, ET2, ET3, ET4, ET5, ETmax}; main() { void proces1 (int et); void proces2 (int et); /*uruchomienie i wznowienie ”procesow” krok po kroku*/ proces1(ET1); proces2(ET1); proces1(ET2); proces2(ET2); proces1(ET3); proces2(ET3); proces1(ET1); proces1(ET2); proces1(ET3); proces2(ET1); proces2(ET2); proces2(ET3); } void proces1(int et) { static int a=10; /*atrybut procesu*/ static float b=100.; /*atrybut procesu*/ switch (et) { case ET1: goto et1; case ET2: goto et2; case ET3: goto et3; default: printf(”\nProces1...”); printf(” niepoprawna kontynuacja!”); return; } et1: /*@@@@ - sekwencja zaraz po starcie procesu*/ printf(”\nProces1 (po et1) a=%d”, a); a++; return; et2: /*@@@@ - dalszy ciag procesu1 – (akcja)*/ printf(”\nProces1 (po et2) b=%6.2f”, b); b+=200.; return; et3: /*@@@@ - dalszy ciag procesu1 – (akcja)*/ printf(”\nProces1 (po et3) b=%6.2f”, b); b+=300.; return; } void proces2(int et) { static float x=50.; static int y=20; switch (et) { case ET1: goto et1; case ET2: goto et2; case ET3: goto et3; default: printf(”\nProces2...”); printf(” niepoprawna kontynuacja!”); return; } et1: /*@@@@ - sekwencja zaraz po starcie procesu*/ printf(”\nProces2 (po et1)”); y+=20; if(y>(int)x) { printf(”y wieksze od x”); x++; return; et2: /*@@@@ - dalszy ciag procesu2 – (akcja)*/ printf(”\nProces2 (po et2) x=%6.2f”, x); x++; return; } else { printf(”x wieksze lub rowne y”); x+=5; return; et3: /*@@@@ - dalszy ciag procesu2 – (akcja)*/ printf(”\nProces2 (po et3) y=%i”, y); y+=(int)x; return; } } Wynik: Proces1 (po et1) a=10 Proces2 (po et1) x wieksze lub rowne y Proces1 (po et2) b=100.00 Proces2 (po et2) x=55.00 Proces1 (po et3) b=300.00 Proces2 (po et3) y=40 Proces1 (po et1) a=11 Proces1 (po et2) b=600.00 Proces1 (po et3) b=800.00 Proces2 (po et1) y wieksze od x Proces2 (po et2) x=57.00 Proces2 (po et3) y=116 Dwie funkcje biblioteczne setjmp i longjmp – istota ich działania. #include <stdio.h> #include <setjmp.h> #include <process.h> jmp_buf Bufor_adresu; main() { int wartosc =999; printf(”Zapamietanie stanu programu \n”); wartosc=setjmp(Bufor_adresu); /*wartosc =0*/ printf(”Miejsce dla powrotu...............\n”); printf(”Zwracana wartosc = %d.......\n”,wartosc); if(wartosc) { printf(”Akcja druga ............\n”); printf(”Zwracana warosc = %d........\n”,wartosc); exit(wartosc); } else printf(”Akcja pierwsza ............\n”); printf(”Uruchomienie longjmp().....\n”); longjmp(Bufor_adresu, wartosc); } Wynik Zapamietanie stanu programu Miejsce dla powrotu............... Zwracana warosc = 0........ Akcja pierwsza ............ Uruchomienie longjmp()..... Miejsce dla powrotu............... Zwracana warosc = 1........ Akcja druga ............ Zwracana warosc = 1........ Funkcja setjmp umożliwia zapamiętanie stanu programu, a konkretniej stanu rejestrów oraz flag procesora, w zmiennej typu jmp_buf – predefiniowanego w pliku nagłówkowym setjmp.h. Funkcja setjmp posiada nagłówek: int setjmp(jmp_buf stprog) Rezultatem funkcji jest dana typu int o wartości 0, albo dana o wartości określonej przez longjmp w chwili powrotu. Stan programu zostaje zapamiętany w zmiennej stprog. Funkcja longjmp umożliwia powrót w miejsce, w którym zapamiętano stan wykonywania programu. Nagłówek funkcji longjmp jest następujący: void longjmp(jmp_buf stprog, int zm) Jeżeli zm reprezentuje daną o wartości 0, to przyjmuje się, reprezentuje daną o wartości 1 (po to, aby w miejscu odtworzenia /powrotu/ program mógł „pójść” dalej, w przypadku użycia instrukcji if). Program bez „wymuszeń etykietowych” switch oraz goto za to z funkcjami setjmp i longjmp. #include <stdio.h> #include <setjmp.h> #define AKCJA static jmp_buf stan; /*stan programu*/ static int pro=-1; /*zm. pomocnicza*/ if(pro!=-1) longjmp(stan,-1); #define POWROT pro=setjmp(stan); if(!pro) return; main() { void proces1(void); void proces2(void); \ \ \ \ \ \ \ /*uruchomianie i wznawianie ”procesow”*/ proces1(); proces2(); proces1(); proces2(); proces1(); proces2(); } void proces1(void) { static int a=10; /*atrybut procesu*/ static float b=100.; /*atrybut procesu*/ AKCJA /*@@@@ - sekwencja zaraz po starcie procesu*/ printf(”\nProces1(po dawnym et1) a=%d”,a); a++; POWROT /*@@@@ - dalszy ciag procesu1 – (akcja)*/ printf(”\nProces1(po dawnym et2) b=%6.2f”,b); b+=200.; POWROT /*@@@@ - dalszy ciag procesu1 – (akcja)*/ printf(”\nProces1(po dawnym et3) b=%6.2f”,b); b+=300.; POWROT } void proces2(void) { static float x=50.; /*atrybut procesu*/ static int y=20; /*atrybut procesu*/ AKCJA /*@@@@ - sekwencja zaraz po starcie procesu*/ printf(”\nProces2 (po dawnym et1)”); y+=20; if(y>(int)x) { printf(”y wieksze od x”); x++; POWROT /*@@@@ - dalszy ciag procesu2 – (akcja)*/ printf(”\nProces2 (po dawnym et2) x=%6.2f”, x); x++; POWROT } else { printf(”x wieksze lub rowne y”); x+=5; POWROT /*@@@@ - dalszy ciag procesu2 – (akcja)*/ printf(”\nProces2 (po dawnym et3) y=%i”, y); y+=(int)x; POWROT } } Wynik Proces1 (po dawnym et1) a=10 Proces2 (po dawnym et1) x wieksze lub rowne y Proces1 (po dawnym et2) b=100.00; Proces2 (po dawnym et3) y=40 Proces1 (po dawnym et3) b=300.00;