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); 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 /*kopiowanie*/ 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 + CD- ROM 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! ); et1: /*@@@@ - sekwencja zaraz po starcie*/ printf( \nfunkcja1 (po et1) a=%d, a); et2: /*@@@@ - dalszy ciag*/ printf( \nfunkcja1 (po et2) b=%g, b); et3: /*@@@@ - dalszy ciag*/ printf( \nfunkcja1 (po et3) b=%g, b); 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! ); et1: /*@@@@ - sekwencja zaraz po starcie*/
printf( \nfunkcja2 (po et1) ); if(y>(int)x) printf( y wieksze od x ); et2: /*@@@@ - dalszy ciag*/ printf( \nfunkcja2 (po et2) x=%g, x); else printf( x wieksze lub rowne y ); et3: /*@@@@ - dalszy ciag*/ printf( \nfunkcja2 (po et3) y=%i, y); 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! ); et1: /*@@@@ - sekwencja zaraz po starcie procesu*/ printf( \nproces1 (po et1) a=%d, a);
a++; et2: /*@@@@ - dalszy ciag procesu1 (akcja)*/ printf( \nproces1 (po et2) b=%6.2f, b); b+=200.; et3: /*@@@@ - dalszy ciag procesu1 (akcja)*/ printf( \nproces1 (po et3) b=%6.2f, b); b+=300.; 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! ); et1: /*@@@@ - sekwencja zaraz po starcie procesu*/ printf( \nproces2 (po et1) ); y+=20; if(y>(int)x) printf( y wieksze od x ); x++; et2: /*@@@@ - dalszy ciag procesu2 (akcja)*/ printf( \nproces2 (po et2) x=%6.2f, x); x++; else
printf( x wieksze lub rowne y ); x+=5; et3: /*@@@@ - dalszy ciag procesu2 (akcja)*/ printf( \nproces2 (po et3) y=%i, y); y+=(int)x; 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) \ 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 );
else x++; POWROT /*@@@@ - dalszy ciag procesu2 (akcja)*/ printf( \nproces2 (po dawnym et2) x=%6.2f, x); x++; POWROT 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;