include - Ústav radioelektroniky
Download
Report
Transcript include - Ústav radioelektroniky
Počítače a programování 1 pro obor EST
BPC1E
PŘEDNÁŠKA 8
OSNOVA:
a) Preprocesor
b) ANSI-C knihovny
c) Příklady
Jiří Šebesta
Ústav radioelektroniky, FEKT VUT v Brně
Preprocesor (1/13)
Proces generování spustitelného kódu
Úkolem preprocesoru je:
• odstranit komentáře ze zdrojové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
Preprocesor (2/13)
• Činnost preprocesoru lze řídit direktivami:
- začínají vždy znakem #
Vložení jiného souboru do souboru – direktiva #include
#include <jméno souboru> nový_řádek
– pro vložení standardního hlavičkového souboru
(musí být v adresáři pro standardní hlavičkové soubory)
př. #include <stdio.h>
#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 (3/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
Definice maker – direktiva #define
Makro (makroinstrukce) nahrazuje posloupnost znaků posloupností nahrazujících znaků
#define identifikátor řetězec nový_řádek
– makro bez parametrů
př. #define PI 3.1415
- za všechna PI nahradí ve zdrojovém textu 3.1415
Preprocesor (4/13)
Každý výskyt identifikátoru je nahrazen řetězcem, to neplatí v komentářích, řetězcích mezi " " a mezi < > v direktivě #include
Není-li řetězec uveden, identifikátor je preprocesorem ze zdrojového textu odstraněn
#define identifikátor(seznam_parametrů) řetězec
nový_řádek
– makro s parametry
př. #define ABS(re,im) sqrt(((re)*(re))+((im)*(im)))
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 (5/13)
Př. Makro pro absolutní hodnotu komplexního čísla
#include <stdio.h>
#include <math.h>
#define PI 3.1415
#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
Preprocesor (6/13)
Parametry v makru mohou reprezentovat různé datové typy –
makro je univerzální (v příkladu float a int)
#include <stdio.h>
#include <math.h>
#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
Preprocesor (7/13)
Nevhodné použití makra
#include <stdio.h>
#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;
}
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č?
Příklad: BPC1E_Ex64.c
Preprocesor (8/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 <stdio.h>
#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 (9/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 <stdio.h>
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 (10/13)
Rušení platnosti maker – direktiva #undef
#undef identifikátor
– od daného místa se ruší platnost makra
př. #define PI 3.1415
…
…
#undef PI
…
// PI se už dále nenahrazuje
#define PI 3.14
…
// PI se zde nahrazuje za 3.14
Preprocesor (11/13)
Podmíněný překlad – direktivy #if, #endif, #elif, #else
#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
V podmínkách musí být výrazy, které může vyhodnotit preprocesor (nelze používat hodnoty proměnných atd.).
Preprocesor (12/13)
Podmíněný překlad lze využít při různých úrovních ladění
#include <stdio.h>
#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 (13/13)
Podmíněný překlad – direktivy #ifdef, #ifndef
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.
Nejčastěji se používá pro ošetření násobné definice:
#ifndef PI
#define PI 3.1415
#endif
#ifdef PI
c=2*PI*r;
#endif
ANSI-C knihovny (1/6)
Knihovna matematických funkcí math.h
Trigonometrické funkce (úhly vždy v Radiánech):
double
double
double
double
sin(double x);
tan(double x);
acos(double x);
atan2(double y,
double
double
double
double
cos(double x);
asin(double x);
atan(double x);
x);
printf("%f", 180/3.1415*atan2(sqrt(3)/3,1))
double sinh(double x); double cosh(double x);
double tanh(double x);
ANSI-C knihovny (2/6)
Exponenciální, logaritmické a mocninné funkce:
double
double
double
double
exp(double x); double log(double x);
log10(double x);
pow(double x, double y); //x^y
sqrt(double x);
Ořezávání a další pomocné funkce (viz př.):
double
double
double
double
double
double
ceil(double x); double floor(double x);
fabs(double x);
ldexp(double x, int n);
frexp(double x, int* exp);
modf(double x, double* ip);
fmod(double x, double y);
ANSI-C knihovny (3/6)
Př.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
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 (4/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 (5/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 (6/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
Příklady (1/8)
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 29.11.2014
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.
Příklady (2/8)
Výsledky pro zadaný text:
Příklady (3/8)
Pro srovnání: obecná pravděpodobnost četnosti písmen v anglickém textu
Příklady (4/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, 33488433, 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 (5/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
www.cplusplus.com. 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 (6/8)
Ukázka výsledků pro příklad 70:
Příklady (7/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):
sin( x ) x
x
3
3!
x
5
5!
x
7
7!
x
9
9!
...
1
n
2 n 1!
x
2 n 1
n0
Vstupní parametr x ve stupních vygenerujte jako úhly v rozsahu
<-start°; stop°> s ekvidistantním rozestupem step°.
Sestavte algoritmus, který bude postupně přidávat členy Taylorovy ř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 (8/8)
Výsledky pro příklad 71:
DĚKUJI ZA POZORNOST
Téma následující přednášky
–
–
–
–
–
Struktury
Unie
Výčtový typ
Dynamické proměnné - úvod
Příklady