310 likes | 444 Views
Počítače a programování 2. Tutori ál 1 8.2.2014 Jiří Šebesta. Kompletní informace naleznete na. http://www.urel.feec.vutbr.cz/~sebestaj/KPC 2E. T ÉMA. Organizace předmětu Preprocesor Funkce main() s argumenty Souborový vstup a výstup. Organizace předmětu (1/3). Rozvrh předmětu.
E N D
Počítače a programování 2 Tutoriál 1 8.2.2014 Jiří Šebesta • Kompletní informace naleznete na http://www.urel.feec.vutbr.cz/~sebestaj/KPC2E
TÉMA Organizace předmětu Preprocesor Funkce main() s argumenty Souborový vstup a výstup
Organizace předmětu (1/3) Rozvrh předmětu
Organizace předmětu (2/3) Domácí úlohy
Organizace předmětu (3/3) Hodnocení
Proces generování spustitelného kódu Preprocesor (1/13) Ú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) 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> Činnost preprocesoru lze řídit direktivami: - začínají vždy znakem# #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“
#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 Preprocesor (3/13) Definice maker – direktiva #define Makro (makroinstrukce) nahrazuje posloupnost znaků posloup-ností nahrazujících znaků #defineidentifikátor řetězec nový_řádek – makro bez parametrů př. #definePI 3.1415 - za všechna PI nahradí ve zdrojovém textu 3.1415
Preprocesor (4/13) Není-li řetězec uveden, identifikátor je preprocesorem ze zdrojového textu odstraněn #defineidentifikátor(seznam_parametrů) řetězec nový_řádek – makro s parametry př. #defineABS(re,im) sqrt(((re)*(re))+((im)*(im))) Každý výskyt identifikátoruje nahrazen řetězcem, to neplatí v komentářích, řetězcích mezi “ “ a mezi < > v direktivě#include Vkládání závorek je důležité pro specifikaci priority operátorů: Pokud bude makro #defineABS(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) #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)); return0; } Makro pro absolutní hodnotu komplexního čísla Příklad: Ex76.c
Preprocesor (6/13) #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 eq. %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 eq. %dx^2+%dx+%d=0 are %5.3f and %5.3f\n", ia, ib, ic, ROOT1(ia, ib, ic), ROOT2(ia, ib, ic)); return0; } Parametry v makru mohou reprezentovat různé datové typy – makro je univerzální (v příkladu float a int) Příklad: Ex77.c
Nevhodné použití makra Preprocesor (7/13) #include <stdio.h> #define POW3(A) ((A)*(A)*(A)) int main() { intx= 10; printf("%d^3 = %d\n", x,POW3(x)); printf("%d\n",POW3(++x)); return0; } V prvním použití makra POW3 je vše v pořádku Přidruhém použití makra POW3 bude rozvoj následující: ((++x)*(++x)*(++x)) , výsledek bude 12*12*13 = 1872 (proč?) Příklad: Ex78.c
Operátory # a ## v makrech Preprocesor (8/13) #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)); return0; } 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. Příklad: Ex79.c
Standardní makra (ANSI C) Preprocesor (9/13) #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__); return0; } __TIME__- aktuální čas spuštění preprocesoru (vrací řetězec) __DATE__- aktuální datum spuštění preprocesoru (vrací řetězec) __FILE__- jméno souboru zpracovávaného preprocesorem (vrací řetězec) __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) Příklad: Ex80.c
Rušení platnosti maker – direktiva #undef Preprocesor (10/13) #undefidentifikátor – od daného místa se ruší platnost makra př. #definePI 3.1415 … … #undef PI … // PI se už dále nenahrazuje #define PI 3.14 … // PI se zde nahrazuje za 3.14
Podmíněný překlad – direktivy #if, #endif, #elif, #else Preprocesor (11/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 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) #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); return0; } Podmíněný překlad lze využít při různých úrovních ladění Příklad: Ex81.c
Podmíněný překlad – direktivy #ifdef, #ifndef Preprocesor (13/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. 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
Funkce main() s argumenty (1/4) Definicí argumentů (parametrů) u funkce main() můžeme definovat chování programu při spuštění. Program s parametry se spouští z příkazové řádky názvem programu (projektu) následovaný parametry oddělené mezerou, např. Muj_program 10 input.txt output.txt -x • Hlavička funkce main() s argumenty • int main(int argc, char *argv[]) • proměnné argc a argv jsou lokální proměnné funkce main(), (mohou mít i libovolné jiné názvy, typ musí být zachován) • argc udává počet zadaných parametrů při spuštění programu • argv je pole řetězců s jednotlivými zadanými argumenty
Funkce main() s argumenty (2/4) Po spuštění programu je do proměnné argc uložena hodnota odpovídající počtu řetězců uvedených na příkazovém řádku v okamžiku spuštění programu. Tento počet zahrnuje i vlastní název programu. Proměnná argv je dvourozměrné pole typu char (jednorozměrné pole řetězců). Do tohoto pole řetězců jsou uloženy všechny řetězce uvedené na příkazovém řádku, do argv[0] je tedy uložen název programu. Počet řetězců v poli odpovídá hodnotě uložen v proměnné argc. Délka jednotlivých řádků pole argv odpovídá délce vložených řetězců. Pokud je potřeba jako vstup programu číselná hodnota, musí se v těle funkce main() provést konverze na požadovaný číselný formát.
Funkce main() s argumenty (3/4) #include <stdio.h> int main(int argc,char*argv[]) { int n; printf("Number of arguments: %d\n\n", argc); printf("List of arguments:\n"); for(n =0; n < argc; n++) printf("argv[%d] = %s\n", n, argv[n]); return0; } Příklad programu, který vypíše počet argumentů a všechny řetězce argumentů Příklad: Ex82.c
Funkce main() s argumenty (4/4) #include <stdio.h> #include <stdlib.h> int main(int argc,char*argv[]) { int n, m; n = atoi(argv[1]); m = n; while(n <=1000) { printf("%3d\n", n); n += m; } return0; } Příklad programu, který vypíše všechny násobky vstupního argumentu do 1000 včetně Příklad: Ex83.c
Souborový vstup a výstup (1/2) Binární vs. textový soubor • Binární soubor • - překladač neprovádí žádnou úpravu čtených a zapisovaných dat • uspořádání dat v souboru je nezávislé a specifické pro dané využití • přenositelnost binárních souborů mezi různými systémy může být omezena rozdílnou délkou základních typů v těchto různých systémech – nutno vždy vhodně ošetřit ANSI C používá tzv. bufferovaný přístup k souborům pomocí proudu (stream) nebo-li ukazatele na strukturu typu FILE (definice v knihovně stdio.h) V ANCI C není rozdíl mezi souborem a zařízením (portem) – přistupuje se stejně
Textový soubor • uspořádán po řádcích, každý zakončen znakem \n • v některých OS se před \n vkládá \r(úpravu zajišťují funkce ze standardní knihovny stdio.h. • lze zobrazit libovolným editorem Souborový vstup a výstup (2/2) • FILE • uspořádán po řádcích, každý zakončen znakem \n • v některých OS se před \n vkládá \r(úpravu zajišťují funkce ze standardní knihovny stdio.h. • lze zobrazit libovolným editorem
Souboru typu WAV – bezkompresní audio Souborový vstup a výstup (3/2) Struktura souboru WAV (podmnožina RIFF)
Příklad: Vygenerujte soubor typu WAV, který bude obsahovat jeden tón dané frekvence, amplitudy, doby trvání a jména cílového WAV souboru (argumenty při spouštění programu) Souborový vstup a výstup (4/2) #include <stdio.h> #include <stdlib.h> #include <math.h> #define TPI 6.283 int fwrite_int(int val,char len, FILE *p) { char byte; while(len-->0) { byte = val &0xFF; fwrite(&byte, 1, 1, p); val >>=8; } return0; } Pomocná funkce pro zápis různě dlouhých dat hlavičky do WAV souboru
int main(int argc, char*argv[]) { doubleph, deltaph, tdur, ampl, freq; intnumsa, srate; unsigned char sig; FILE *ptrf; if(argc ==5) { srate =8000;// fixed sample rate 8 kHz freq = atof(argv[1]);// tone freq - 1. argument in Hz ampl = atof(argv[2]);// tone amplitude - 2. argument tdur = atof(argv[3]);// tone duration - 3. argument in s numsa = tdur*srate;// overall number of samples ph =0;// initial phase 0 rad deltaph = TPI * freq /srate;// delta phase ptrf = fopen(argv[4],"wb"); fwrite("RIFF",4,1, ptrf);// chunk_id RIFF fwrite_int(36+ numsa,4, ptrf);// chunk_size fwrite("WAVE",4,1, ptrf);// spec. RIFF form for WAV fwrite("fmt",4,1, ptrf);// subchunk1id - format Souborový vstup a výstup (5/2)
fwrite_int(16,4, ptrf);// subchunk1size 16 for PCM fwrite_int(1,2, ptrf);// audio_format, 1 = PCM fwrite_int(1,2, ptrf);// channels, 1=mono fwrite_int(srate,4, ptrf);// sample rate fwrite_int(srate *1*8/8,4, ptrf);// byte rate fwrite_int(1*8/8, 2, ptrf);// block align fwrite_int(8,2, ptrf);// bits per sample, 8 bits fwrite("data",4,1, ptrf);// subchunk2id - data fwrite_int(numsa *1*8/8,4, ptrf);// subchunk2 size while(numsa-->0) { sig =128*(ampl*sin(ph)+1); fwrite(&sig,1,1, ptrf); ph += deltaph; } fclose(ptrf); } else printf("Wrong number of arguments!!!!\n"); return0; } Souborový vstup a výstup (6/2) Příklad: Ex84.c
Příklad: Vygenerujte soubor typu WAV, který bude měnit tón od zadané frekvence do zadané frekvence, s danou amplitudou, dobou trvání a jménem cílového WAV souboru (argumenty při spouštění programu) Souborový vstup a výstup (7/2) Příklad: Ex85.c - bude zveřejněn 17.2.2014
Příklad – domácí úloha: Sestavte program, který vygeneruje soubor typu WAV, který bude obsahovat DTMF signální volbu podle zadaného kódu. Kód a cílový soubor jsou argumenty při spouštění programu. V programu proveďte rovněž korekci možných znaků DTMF kód, nepovolené znaky vylučte. Jeden dvojtón trvá 50 ms následuje mezera 50 ms. Souborový vstup a výstup (8/2) Příklad: Ex86.c– bude zveřejněn 24.2.2014
TÉMA NÁSLEDUJÍCÍHOTUTORIÁLU Úvod do objektově orientovaného DĚKUJI ZA POZORNOST