OSNOVA: a) Preprocesor b) ANSI-C knihovny c) Příklady Jiří Šebesta Ústav radioelektroniky, FEKT VUT v Brně Počítače a programování 1 pro obor EST BPC1E PŘEDNÁŠKA 8
Preprocesor (1/13) Úkolem preprocesoru je: odstranit komentáře ze zdrojo- vého kódu vložit soubory, které mají být kompilovány odstranit části kódu určeného direktivami #if rozvinout předdefinovaná makra Proces generování spustitelného kódu
Činnost preprocesoru lze řídit direktivami: - začínají vždy znakem # Vložení jiného souboru do souboru – direktiva #include #include nový_řádek – pro vložen í standardn í ho hlavičkov é ho souboru (mus í být v adres á ři pro standardn í hlavičkov é soubory) př. #include #include "jméno souboru" nový_řádek – pro vložen í vlastn í ho zdrojov é ho (hlavičkov é ho) souboru (mus í být v adres á ři projektu nebo lze specifikovat i s cestou) př. #include "my_lib.h" Preprocesor (2/13)
#include MAKRO nový_řádek – pro vložen í (rozvinut í ) MAKRA specifikovan é ho dř í ve uvedenou direktivou #define (viz d á le) (nesm í obsahovat " ) př. #include my_macro #define identifikátor řetězec nový_řádek – makro bez parametrů př. #define PI za všechna PI nahrad í ve zdrojov é m textu Definice maker – direktiva #define Makro (makroinstrukce) nahrazuje posloupnost znaků posloup- nost í nahrazuj í c í ch znaků Preprocesor (3/13)
#define identifikátor(seznam_parametrů) řetězec nový_řádek – makro s parametry př. #define ABS(re,im) sqrt(((re)*(re))+((im)*(im))) Každý výskyt identifik á toru je nahrazen řetězcem, to neplat í v ko- ment á ř í ch, řetězc í ch mezi " " a mezi v direktivě #include Nen í -li řetězec uveden, identifik á tor je preprocesorem ze zdrojo- v é ho textu odstraněn Vkl á d á n í z á vorek je důležit é pro specifikaci priority oper á torů: Pokud bude makro #define ABS(re,im) sqrt(re*re+im*im) a ve zdrojovém kódu c=ABS(a+1,b-1) pak po nahrazen í preprocesorem bude c=sqrt(a+1*a+1+b-1*b-1) po zjednodušen í výrazu c=sqrt(2*a) Preprocesor (4/13)
#include #define PI #define ABS(re,im) sqrt(((re)*(re))+((im)*(im))) int main() { float r=12, ar=1.6, ai=2.2; printf("Circle area is %f\n", PI*r*r); printf("Absolute value of %f + j*%f is %f\n", ar, ai, ABS(ar,ai)); return 0; } Příklad: BPC1E_Ex62.c Př. Makro pro absolutní hodnotu komplexního čísla Preprocesor (5/13)
#include #define ROOT1(a,b,c) (-(b)+sqrt((b)*(b)-4*(a)*(c)))/(2*(a)) #define ROOT2(a,b,c) (-(b)-sqrt((b)*(b)-4*(a)*(c)))/(2*(a)) int main() { float fa=1.3, fb=5.6, fc=1.2; int ia=1, ib=5, ic=2; printf("Roots for %3.1fx^2+%3.1fx+%3.1f=0 are %5.3f and %5.3f\n", fa, fb, fc, ROOT1(fa, fb, fc),ROOT2(fa, fb, fc)); printf("Roots for %dx^2+%dx+%d=0 are %5.3f and %5.3f\n", ia, ib, ic, ROOT1(ia, ib, ic), ROOT2(ia, ib, ic)); return 0; } Příklad: BPC1E_Ex63.c Parametry v makru mohou reprezentovat různé datové typy – makro je univerzáln í (v příkladu float a int ) Preprocesor (6/13)
Nevhodné použití makra #include #define POW3(A) ((A)*(A)*(A)) int main() { int x = 10; printf("%d^3 = %d\n", x, POW3(x)); printf("%d\n", POW3(++x)); return 0; } Příklad: BPC1E_Ex64.c V prvním použití makra POW3 je vše v pořádku, při druhém použití makra POW3 bude rozvoj následující: ((++x)*(++x)*(++x)), výsledek bude 12*12*13 = 1872 proč? Preprocesor (7/13)
Operátory # a ## v makrech Operátory # a ## se používají v makrech s parametry. Za operátor # se dosad í řetězec, který je stejný jako parametr makra. Operátor ## spojí své dva parametry v jeden řetězec. #include #define SUM(A,B) printf("%s = %d\n",#A " + " #B,(A)+(B)) #define JOINT(A,B) A ## B int main() { int n1=10, n2=20; SUM(5,6); SUM(JOINT(n,1),JOINT(n,2)); return 0; } Příklad: BPC1E_Ex65.c Preprocesor (8/13)
Standardn í makra (ANSI C) __TIME__ - aktuální čas spuštění preprocesoru (vrací řetězec) __DATE__ - aktuální datum spuštění preproc. (vrací řetězec) __FILE__ - jméno souboru zpracovávaného preprocesorem __LINE__ - číslo aktuálního řádku (vrací int ) __STDC__ - definuje, zda překladač splňuje normu ANSI (vrací int – 1 splňuje / 0 nesplňuje) #include int main() { printf("%s\n", __TIME__); printf("%s\n", __DATE__); printf("%s\n", __FILE__); printf("%d\n", __LINE__); printf("%d\n", __STDC__); return 0; } Př í klad: BPC1E_Ex66.c Preprocesor (9/13)
#undef identifikátor – od daného místa se ruší platnost makra př. #define PI … #undef PI … // PI se už dále nenahrazuje #define PI 3.14 …// PI se zde nahrazuje za 3.14 Rušení platnosti maker – direktiva #undef Preprocesor (10/13)
#if podmínka_A zdrojový kód pro splněnou podmínku_A #elif podmínka_B zdrojový kód pro splněnou podmínku_B (nesplněna podmínka_A ) #elif podmínka_C zdrojový kód pro splněnou podmínku_C (nesplněna A i B ) #else zdrojový kód pro stav, kdy žádná z předchozích podmínek nebyla splněna #endif Podmíněný překlad – direktivy #if, #endif, #elif, #else V podmínkách musí být výrazy, které může vyhodnotit preproce- sor (nelze používat hodnoty proměnných atd.). Preprocesor (11/13)
Podmíněný překlad lze využít při různých úrovních ladění #include #define DEBUGING 2 int main() { int a = 10; #if DEBUGING==1 a++; #elif DEBUGING==2 a=a*a*a; #else a=0; #endif printf("%d\n", a); return 0; } Příklad: BPC1E_Ex67.c Preprocesor (12/13)
Pomocí #if defined lze vyhodnocovat, zda již existuje určité makro (bylo-li definováno), zkráceně #ifdef. Pomocí #if !defined lze vyhodnocovat, zda neexistuje určité makro (nebylo-li definováno), zkráceně #ifndef. Podmíněný překlad – direktivy #ifdef, #ifndef Nejčastěji se používá pro ošetření násobné definice: #ifndef PI #define PI #endif #ifdef PI c=2*PI*r; #endif Preprocesor (13/13)
Trigonometrické funkce (úhly vždy v Radiánech): double sin(double x); double cos(double x); double tan(double x); double asin(double x); double acos(double x); double atan(double x); double atan2(double y, double x); Knihovna matematických funkcí math.h ANSI-C knihovny (1/6) double sinh(double x); double cosh(double x); double tanh(double x); printf("%f", 180/3.1415*atan2(sqrt(3)/3,1))
Exponenciální, logaritmické a mocninné funkce: double exp(double x); double log(double x); double log10(double x); double pow(double x, double y); //x^y double sqrt(double x); ANSI-C knihovny (2/6) Ořezávání a další pomocné funkce (viz př.): double ceil(double x); double floor(double x); double fabs(double x); double ldexp(double x, int n); double frexp(double x, int* exp); double modf(double x, double* ip); double fmod(double x, double y);
Př. #include int main() { double a=-1.11, b=2.21, i, f; int c=2, e; printf("atan2(%.2f,%.2f)=arctg(%.2f/%.2f)=%.2f\n\n", a, b, a, b, atan2(a,b)); printf("exp(%.2f)=e^%.2f=%.2f\n\n", a, a, exp(a)); printf("log(%.2f)=ln(%.2f)=%.2f\n\n", b, b, log(b)); printf("log10(%.2f)=%.2f\n\n", b, log10(b)); printf("fabs(%.2f)=%.2f\n\n", a, fabs(a)); printf("ceil(%.2f)=%.2f\n\n", a, ceil(a)); printf("floor(%.2f)=%.2f\n\n", a, floor(a)); ANSI-C knihovny (3/6)
printf("ldexp(%.2f, %d)=%.2f*2^%d=%.2f\n\n", a, c, a, c, ldexp(a,c)); printf("f=frexp(%.2f, &e): ", b); f=frexp(b, &e); printf("f=mantissa=%.4f; e=exponent=%d, ", f, e); printf("i.e. x=mantissa*2^exponent=%.4f*2^%d= %.2f\n\n", f, e, f*pow(2,e)); printf("f=modf(%.2f, &i): ", a); f=modf(a, &i); printf("f=fractional_part=%.2f;i=integral_part=%.2f\ n\n", f, i); printf("fmod(%.2f,%.2f)=remainder(%.2f/%.2f)=%.2f\n\ n", b, a, b, a, fmod(b,a)); return 0; } Příklad: BPC1E_Ex68.c ANSI-C knihovny (4/6)
Další knihovny assert.h – makro assert pro účely ladění ctype.h – funkce pro testování znaků, do jaké skupiny patří, např. islapha(char x) vrátí 1 (true) pokud je znak x z rozsahu 'A' až 'Z' nebo 'a' až 'z' errno.h – makra a proměnná errno pro definici chyby float.h – předdefinované konstanty pro maxima a minima hodnot racionálních typů limits.h – předdefinované konstanty pro maxima a minima hodnot celočíselných typů locale.h – nastavení a čtení národního nastavení, např. des. tečka/čárka apod. setjmp.h – funkce pro nastavení parametrů volání u funkcí ANSI-C knihovny (5/6)
signal.h – makra a funkce pro reporty signálů vznikajících během výkonu programu stdarg.h – makra pro práci s argumenty stddef.h – standardní definice (např. NULL ) stdio.h – funkce pro vstupy a výstupy stdlib.h – různé základní funkce, např. konverze z řetězců na číselné hodnoty a naopak string.h – funkce pro práci s řetězci time.h – funkce pro práci s časem ANSI-C knihovny (6/6)
Příklady (1/8) Příklad 69: Ve zdrojov é m souboru BPC1E_Ex69.c je definov á n a inicializov á n řetězec text[] s anglickým textem z novin. Doplňte program tak, aby se provedl výpočet procentu á ln í pravděpodobnosti výskytu p í smene v textu v rozsahu anglick é abecedy (26 p í smen 'A' až 'Z' ) bez ohledu na to, zda je p í smeno mal é nebo velk é. Nezapomeňte vyloučit v š echny znaky mimo abecedu. Pozn. Příklady níže uvedené budou řešeny společně na přednášce, případně i jako domácí příprava (bonusové body pro prvních 10 studentů), zdrojové kódy budou na webu zveřejněny
Výsledky pro zadaný text: Příklady (2/8)
Pro srovnání: obecná pravděpodobnost četnosti písmen v anglic- kém textu Příklady (3/8)
Příklad 70: Sestavte program, který generuje palindromická čísla ze zadaného vstupního inicializačního čísla. Palindromické číslo je „symetrické“ číslo. Jeho hodnota se nezmění, pokud jeho cifry napíšeme v opačném pořadí. Jako příklady uveďme 55, 222, 151, 12921, , atd. Palindromické číslo lze vygenerovat tak, že vezmeme-li „libovolné“ nepalindromické číslo a přičteme k němu jeho zrcadlový obraz (stejné číslo napsané v opačném pořadí) a tuto operaci budeme stále opakovat, získáme po konečném počtu opakování palindromické číslo. Existují však čísla, u nichž se neví, zda se po konečném počtu opakování algoritmu se lze k palindromickému číslu dostat. Příkladem je číslo 196 (podle nějž se uvedený algoritmus nazývá, tzv. 196-Algorithm). Počet opakování algoritmu k získání palindromického čísla může být různý. Příklady (4/8)
Sestavte tedy program, který načte celé číslo z klávesnice a pro toto číslo provede algoritmus generování palindromického čísla. Operaci zrcadlení je nejvhodnější provést převodem čísla na řetězec s následným obrácením pořadí cifer a převodem zpět na celé desítkové číslo. Pro převody celého čísla na řetězec a zpět využijte funkce atoi() a itoa() z knihovny stdlib.h, jejich aplikaci lze najít v nápovědě na známých stránkách Stejně tak test, zda již bylo vygenerováno palindromické číslo proveďte na úrovni řetězce. Nejvhodnější je využít cyklus typu do-while. Program doplňte o opakování výpočtu pro zadání jiných inicializačních čísel s tím, že se ukončí po zadání 0. Pokud se pro nějaké inicializační číslo nepodaří najít palindromické číslo (např. pro zmíněných 196) nebo už nebude dostačovat 4 bytové rozlišení celého čísla typu int, přerušení běhu programu (resp. smyčky do-while ) provedete přes současný stisk kláves CTRL-BREAK. Příklady (5/8)
Ukázka výsledků pro příklad 70: Příklady (6/8)
Příklad 71: Vytvořte program, který vypočte hodnotu funkce bez použití knihovních matematických funkcí s využitím rozvoje v Taylorovu řadu (viz Matematika I): Vstupn í parametr x ve stupn í ch vygenerujte jako ú hly v rozsahu s ekvidistantn í m rozestupem step°. Sestavte algoritmus, který bude postupně přid á vat členy Tay- lorovy řady pro dan é x, až bude relativn í chyba výpočtu men ší než 1% ve vztahu k hodnotě funkce sinus vypočten é pomoc í funkce sin() z knihovn í funkce math.h. Testujte v algoritmu splněn í podm í nky relativn í chyby a na jej í m z á kladě ukončete výpočet. Příklady (7/8)
Výsledky pro př í klad 71: Příklady (8/8)
Téma následující přednášky DĚKUJI ZA POZORNOST – Struktury – Unie – Výčtový typ – Dynamické proměnné - úvod – Příklady