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;

Podobne dokumenty