Stáhnout prezentaci
Prezentace se nahrává, počkejte prosím
1
Základy jazyka C
2
Charakteristika jazyka:
Univerzální programovací jazyk Nízkoúrovňový jazyk (obsahuje standardní datové typy-znaky,celá čísla,reálná čísla) Úsporné vyjadřování Strukturovaný Efektivní překlad , rychlý kód Navržen pro operační systém UNIX, celý Unix je v C napsán V současnosti většina systémového SW je v C napsána
3
Umožňuje snadné napsání překladače jazyka
Dosahuje vysokou efektivitu překladu blížící se efektivitě jazyka Assembler HISTORIE jazyka C První standard jazyka (K&R) v roce 1978 [ Brian W.Kernighan,Denis M.Ritchie ] Z něho vychází standard ANSI C (1988), který obsahuje navíc i specifikaci knihovních funkcí a hlavičkových souborů, které implementace C musí obsahovat
4
V současnosti byl standard ANSI C rozšířen (1990)
Jazyk je přenositelný na libovolný operační systém a libovolný počítač Modernější programovací prostředky: konec devadesátých let: C++ , JAVA začátek 21.stol.: C#
5
Způsob zpracování programu v jazyku C :
Proces editace zdrojového kódu, překladu do OBJECT kódu, sestavení do výsledného EXE-kódu, proces ladění programu:
6
Preprocesor (součást kompilátoru)
Textový editor (nízkoúrovňový) – pokud je C součástí integrovaného prostředí, pak zpravidla obsahuje i tento editor Preprocesor (součást kompilátoru) Upravuje/opravuje zdrojový soubor Vynechává komentáře Vkládá hlavičkové soubory (.H) Zajišťuje makrosubstituce Výsledkem jeho činnosti je opět textový soubor, který je předán ke zpracování přímo kompilátoru Kompilátor (překladač) Transformuje (překládá) textově napsaný program do relativního kódu (tzv.Object-kód)
7
LINKER (sestavovací program)
(soubor s příponou .OBJ). Každá programová jednotka = relativní modul Produkuje na požádání výpis překládaného programu – tzv. listing (soubor s příponou .LST) LINKER (sestavovací program) Vyřeší reference na externí podprogramy pomocí standardních systémových event. uživatelských knihoven (soubory s příponou .LIB). Produkuje výsledný spustitelný modul (soubor s příponou .EXE)
8
DEBUGER (prostředek pro ladění)
Pomáhá při procesu ladění, zobrazuje během výpočtu hodnoty určených proměnných, dokáže výpočet krokovat apod. Toto je princip jakéhokoliv překladače libovolného jazyka. Prostředí BORLAND C++
9
Základní pojmy v jazyce C
Zdrojové a hlavičkové soubory Zdrojový soubor (.C) obsahuje text programu Hlavičkový soubor (.H) je vložen ( #include) k vlastnímu programu před jeho překladem po-mocí tzv. preprocesoru. Direktiva #include řídí tento proces. Protože program používá např. i knihovní funkce, hlavičkové soubory umožňují jejich správné volání. Zpřehlednění programu „štábní kultura“ , časté komentování Strukturování jmen souborů:
10
ASCII kódová tabulka , dolní/horní část Identifikátory
edi.main.c edi.sub1.c Bílé znaky ASCII kódová tabulka , dolní/horní část Identifikátory C jazyk je „case sensitive“ – rozlišuje malá a velká písmena , jména objektů psát malými Klíčová slova musí být psána malými písmeny jinak se berou jako identifikátory ( for , while ) Délka identifikátoru není omezena,ale rozezná prvních 31 znaků (ANSI C),ostatní neuvažuje Možnost užití podtržítka _ mezi slovy, ne na začátku (syst.identifikátor), ne na konci
11
ANSI C nedovoluje komentáře hnízdit
Nerozlišovat 2 identifikátory jen typem písma Dodržovat konvence (např.: i,j,k – prom.cyklu) Komentáře /* ….. */ ANSI C nedovoluje komentáře hnízdit Jméno autora programového modulu a datum poslední aktualizace,číslo verze Okomentovat ucelené logické části hned při tvorbě programu – po odladění se už na to zpravidla zapomene Možnost víceřádkových komentářů
12
Základy Jednoduché datové typy Pascal C INTEGER int , short int ,short
long int , long CHAR char REAL float , double, float double Typy char , int , short int , long int mohou být signed nebo unsigned, implicitní je signed (mimo char) Rozsahy pro unsigned : 0 až 2n – 1 , pro signed : - 2n-1 až +2n-1 – 1 unsigned char (8 bitů): 0 až 255 , pro signed char (7 bitů):-128 až 127 C neposkytuje typ Boolean , logické hodnoty jsou však representovány pomocí celočíselných hodnot ( int ) ( 0=false , nenulová hodnota=true ) Typ double má přesnost 20 desetinných míst
13
int i ; /* globální proměnná od začátku řádky */
Definice proměnných definice přidělí proměnné jméno a paměť deklarace udává typ a jméno proměnné Pascal C VAR i : INTEGER; int i ; c , ch : CHAR; char c , ch ; f , g : REAL; float f , g ; doporučeno komentovat proměnné definice se vyskytují buď vně (globální), nebo uvnitř (lokální) programové jednotky: int i ; /* globální proměnná od začátku řádky */ int main() { int j; /* lokální proměnná je odsazena od začátku */ }
14
Celočíselné: (signed) int (signed) short int (short) (signed) long int (long) unsigned int (unsigned) Rozsah znaménkových proměnných je : Reálné: float double (20 des.míst) Logické (C je přímo neposkytuje) – vyjadřuje se pomocí celočíselných int: … false <>0 (1) … true Znakové char: délka vždy 1 byte (8 bit) signed char – rozsah -128 až +128 unsigned char – rozsah 0 až 255
15
Přiřazení l-hodnota (l-value) představuje adresu – to, co může být na levé straně přiřazovacího příkazu ( konstanta 123 nebo výraz (x+3) není adresa, ale hodnota , proměnná x je l-hodnotou ) Pravá strana přiřazovacího příkazu musí být vždy vyčíslitelná (hodnota) výraz výraz i*2 + 3 přiřazení l-hodnota=výraz j=i*2 + 3 příkaz l-hodnota=výraz; j=i*2 + 3; Př.: PASCAL C j := 5; j = 5; d := ‘z’ ; d = ‘z’; f := f *i ; f = f *i ; Existuje možnost násobného přiřazení i = j = k = x + 5; Vyhodnocení: i = ( j = ( k=x+5) ) )
16
terminologie: česky: symbolicky: prakticky: výraz výraz i*2+3 přiřazení l-hodnota = výraz j=i*2+3 příkaz l-hodnota = výraz ; j=i*2+3 ;
17
Hlavní program Hlavní programový modul se jmenuje povinně main a musí být uveden (zpravidla za definicemi podprogramů / funkcí ). Je volán po spuštění programu. Př.: PASCAL C Program Pokus(input,output); int main() /*bez středníku*/ var i , j : integer ; { int i , j ; Begin i:=5 ; i = 5 ; j:= -1 ; j = -1 ; j:= j + 2*i ; j = j + 2*i ; End return 0; }
18
Složené závorky otevírají / uzavírají :
{ /* blok */ { /* složený příkaz */ int i; i=5; i=5; j=6; j=6; } } Jazyk C umožňuje inicializaci proměnné přímo v definici : int i = 5;
19
Konstanty Celočíselné konstanty : desítkové 15 , 0 , 1
Osmičkové , , 01 Šestnáctkové 0x3A , 0xCD , 0x1 Pro konstanty typu long se použije přípona L Pro unsigned konstanty se použije přípona U Záporné konstanty jsou uvozeny znaménkem -
20
Reálné konstanty : Znakové konstanty :
Jsou implicitně double a jsou tvořeny podle běžných pravidel: 15. , , .84 , 5e6 Konstanta typu float má příponu F ( 56.8F ) Konstanta typu long má příponu L ( 5e6L ) Znakové konstanty : Jsou stejně jako v Pascalu uzavřeny do apostrofů: ‘a’ , ‘*’ Hodnota znakových konstant je odvozena z použité kódové tabulky Potřebujeme-li konstantu z neviditelného znaku, pak použijeme zápis šestnáctkový (/X0A) nebo osmičkový (/012)
21
Lze zdvojovat /“ (zobrazení úvozovek)
Lze použít i znakový ekvivalent (escape sekvence) sekvence hodnota význam \n 0x0A nová řádka (LF) \r 0x0D návrat na začátek řádky (CR) \f 0x0C nová stránka (FF) \t 0x09 tabulátor (HT) \b 0x08 posun doleva (BS) \a 0x07 písknutí (BELL) \\ 0x5C zpětné lomítko (backslash) \’ 0x2C apostrof (single quote) \0 0x00 nulový znak (NUL)
22
Řetězcové konstanty (literály)
Jsou uzavřeny mezi uvozovky a mohou obsahovat všechny způsoby zápisu znakových konstant Příklad: ” Tohle je znakový řetězec ” ANSI C možňuje dlouhé řetězcové konstanty zřetězovat, jednotliné subřetězce mohou být odděleny mezerami, nebo novými řádky Příklad: ” Tohle je ” ”znakový řetězec ” ” Tohle je ” ”znakový řetězec ”
23
Aritmetické výrazy Výraz ukončený středníkem se stává příkazem
Př.: j = … výraz j = 5; … příkaz Unární operátory Binární operátory Pascal C sčítání odečítání násobení * * reálné dělení / / celočíselné dělení DIV / dělení modulo MOD %
24
Speciální unární operátory – incrementální
To zda bude dělení celočíselné, nebo reálné záleží na operandech – pouze int/int je celočíselné,ostatní reálné. Speciální unární operátory – incrementální 1) ++výraz … incrementování před užitím Výraz je zvětšen o jedničku a pak je nová hodnota vrácena výrazu. 2) výraz++ … incrementování po užití Je vrácena původní hodnota výrazu a teprve pak je výraz zvětšen o jedničku. Výraz musí být proměnná (l-hodnota), nikoli konst.nebo výraz !! Př.: int i = 5 , j = 1 , k ; i++; … i = 6 j = ++i; … j = 7 , i = 7 j = i++; … j = 7 , i = 8 k = --j + 2 ; … k = 8 , j = 6 , i = 8
25
Operátory přiřazení základní přiřazení : PASCAL C := =
:= = ostatní přiřazovací příkazy dostupné pouze v C : l-hodnota += výraz l-hodnota = l-hodnota + výraz l-hodnota -= výraz l-hodnota = l-hodnota – výraz l-hodnota *= výraz l-hodnota = l-hodnota * výraz l-hodnota /= výraz l-hodnota = l-hodnota / výraz l-hodnota %= výraz l-hodnota = l-hodnota % výraz …celoč.dělení l-hodnota >>= výraz l-hodnota = l-hodnota >> výraz …bit. posuny l-hodnota <<= výraz l-hodnota = l-hodnota << výraz …bit. posuny l-hodnota &= výraz l-hodnota = l-hodnota & výraz …bit.součin l-hodnota ^= výraz l-hodnota = l-hodnota ^ výraz ..bit.exkl.suma l-hodnota |= výraz l-hodnota = l-hodnota | výraz …bit.součet
26
Terminálový vstup a výstup
Hlavičkový soubor stdio.h I/O operace nejsou v C součástí jazyka,ale jsou řešeny voláním knihovních funkcí Aby bylo možné tyto funkce vyvolat, je třeba připojit popis těchto funkcí v hlavičkovém souboru stdio.h pomocí příkazu: #include <stdio.h> Vstup a výstup znaku getchar(), putchar() vstup zajišťuje getchar() … píšeme znaky tak dlouho, dokud nestiskneme ENTER, pak se přečte první znak a ostatní se ignorují
27
Formátovaný vstup a výstup
výstup znaku zajišťuje putchar() Př.: { int c; c = getchar(); putchar(c); putchar(‘\n’); return 0; } Formátovaný vstup a výstup Analogie funkcí Read a Write v Pascalu jsou v jazyku C funkce scanf() a printf() Příkazy: scanf( “%d” , &i ); %d znamená dekadický formát čtení znak & znamená adresu proměnné printf( “%d” , i ); Řídící řetězec formátu Formátové specifikace začínají znakem % Znakové specifikace se používají jen u printf()
28
Některé formátové specifikace
c … znak (při čtení i „white spaces“) d … číslo int zobrazené desítkově (signed) u … číslo int zobrazené desítkově (unsigned) x … číslo int zobrazené šestnáctkově (mal.) X … číslo int zobrazené šestnáctkově (vel.) o … číslo int zobrazené oktalově ld … číslo long desítkově lx … číslo long šetnáctkově malými písmeny lX … číslo long šetnáctkově velkými písmeny f … čtení float / tisk float i double lf … načítání double (nepoužívat pro tisk) Lf … long double s … řetězec
29
Vypíše: Je presně 13:30 Příklady:
printf(“ Znak ‘%c’ má ASCII kód %d (%XH) \n ”,c,c,c); Vypíše: Znak ‘A‘ má ASCII kód 65 (41H) printf(“ Je presně %2d: %2d\n “,hodiny,minuty); Vypíše: Je presně 13:30 (Formátování je podobné jako u stejnojmenných příkazů v Matlabu.)
30
Kódy pro printf: Kódy pro scanf:
48
Řídící struktury Pro zpřehlednění programu dodržujte grafická členění hnízdění cyklů a úrovní závorkování { }. Závorkujte pro zpřehlednění výrazů i když to není nutné Každý neřídící příkaz by měl být na vlastní řádce Pokud se výraz nevejde na řádku a musí se pokračovat, pak začínejte na stejné pozici, kde začíná výraz
49
Logické výrazy (Booleovské) Operátory Pascal C
rovnost = == nerovnost <> != logický součin AND && logický součet OR || negace NOT ! relace: menší < < menší nebo rovno <= <= větší > > větší nebo rovno >= >=
50
POZOR na rozdíl mezi = a == ! ! Priorita vyhodnocování výrazů:
Operátory směr vyhodnocení ! zprava doleva * / % zleva doprava zleva doprava < <= >= > zleva doprava == != zleva doprava && zleva doprava || zleva doprava ( ? : ) zprava doleva = += -= *= zprava doleva , zleva doprava
51
Podmíněný výraz , ternární operátor
Pozor: Nejsme-li si jisti, prioritu vyhodnocení výrazu upravíme závorkováním ! ! Podmíněný výraz , ternární operátor Syntaxe: podmínka ? Výraz1 : Výraz2 a má význam (Pascalovsky zapsáno): if podmínka then výraz1 else výraz2 Příklad: int i , k , j = 2 ; i = ( j == 2) ? 1 : 3 ; … i nabude hodnoty 1 k = ( i > j ) ? i : j ; … k nabude hodnoty 2 Ternární operátor nemusí být použit jen pro přiřazení: ( i == 1 ) ? i++ : j++ … increment buď i nebo j
52
Operátor čárky Pouze 4 operátory && || ( ? : ) a oper. čárky zaručují přednostní vyhodnocení levého operandu před pravým. Syntaxe: výraz1 , výraz2 Výraz1 je vyhodnocen, výsledek zapomenut , pak se vyhodnotí výraz2 a to je závěrečný výsledek. Není to l-hodnota. Používá se jen v řídících příkazech for a while . Příklad: int i = 2 , j = 4 ; j = ( i++, i – j ) ; /* i bude 3 a j bude -1 */
53
Neúplný a úplný podmíněný příkaz syntaxe: PASCAL C
IF podmínka if (podmínka) THEN příkaz příkaz1; ELSE příkaz2; else příkaz2; Př.: Program čte znak. Pokud je to velké písmeno, pak vypíše znak jako dekadické číslo: #include <stdio.h> int main() { int c ; c=getchar(); if (c >= ‘A’ && c <= ‘Z’) printf(“%d\n”,c); return 0 ; }
54
Příklady na IF: PASCAL C IF i > if (i > 3 ) THEN j := 5 ; j = 5; THEN j := 5 j = 5; ELSE j := 1 ; else j = 1; _____________________________________ THEN BEGIN { j := 5 ; j = 5 ; i := 7 ; i = 7 ; END } ELSE j := 1; else j = 1 ;
55
Přepínač – příkaz SWITCH
Pro mnohonásobné větvení programu existuje v C příkaz switch. ( Je to analogie pascalovského příkazu CASE ). PASCAL C CASE výraz switch výraz OF { hodnota 1 : příkaz 1; case hodnota 1 : příkaz 1; break; … … hodnota n : příkaz n; case hodnota 1 : příkaz 1; break ELSE příkaz def; default : příkaz def; break; END }
56
Odlišnosti: Nelze napsat více hodnot pro jednu větev,ale prázdné bez break postupují řízení na další větev Logický výraz musí být typu int Každá větev musí být ukončena příkazem break V každé větvi může být více příkazů, které není nutno závorkovat Příklad na switch : (pro zadané a b c vypíše 1,pro d 2 jinak 3) switch ( getchar() ) { case ‘a’ : case ‘b’ : case ‘c’ : putchar(‘1’); break ; case ‘d’ : putchar(‘2’); break ; default : putchar(‘3’); break ; }
57
Příkazy cyklu V C existují 3 druhy: while , for , do-while
Pomocné příkazy break a continue : break – okamžitě opouští cyklus a jde na první příkaz za příkazem cyklu continue – ukončí pouze nejvnitřnější neuzavřenou smyčku , ale příkaz cyklu neukončí Příkaz while Příkaz testuje podmínku před průchodem cyklu (nemusí proběhnout ani jednou). Podmínka je závislá na tom, co se děje v tělu cyklu. Nekonečná smyčka má podm.=1 PASCAL C WHILE podmínka DO while (podmínka) příkaz ; příkaz ;
58
Příklady na while: PASCAL C WHILE x < 10 DO while ( x < 10 ) x := x + 1 ; x++ ; Program přečte a vytiskne viditelné znaky a pokud načte znak ‘z’ , pak skončí s cyklem načítání znaků: #include <stdio.h> int main() { int c ; while (c = getchar() != ‘z’ ) { if ( c >= ‘ ‘ ) putchar(c); printf ( “Načítání znaku bylo ukončeno … \n” ); return 0 ; }
59
Je to ekvivalent pascalovského REPEAT-UNTIL. syntaxe : PASCAL C
Příkaz do-while Je to ekvivalent pascalovského REPEAT-UNTIL. syntaxe : PASCAL C REPEAT do { příkazy ; příkazy ; UNTIL NOT podmínka ; } while (podmínka ) ; Příklad na do while: ( cyklus odečítá jedničku tak dlouho, dokud se neznuluje ) REPEAT do { i := i - 1 ; i-- ; UNTIL i <= 0 ; } while ( i > 0 ) ;
60
Program přečte a vytiskne viditelné znaky a pokud načte
znak ‘z’ , vytiskne ho a pak skončí s načítáním znaků: #include <stdio.h> int main() { int c ; do { if ( ( c = getchar() ) >= ‘ ‘ ) putchar(c); } while ( c != ‘z’ ) printf ( “Načítání znaku bylo ukončeno … \n” ); return 0 ; }
61
Příkaz for Je to ekvivalent pascalovského FOR. syntaxe : PASCAL C FOR prom:=start TO stop for ( výraz_start ; [ STEP krok ] výraz_stop ; DO výraz_iter ) příkaz ; příkaz ; Příklad na for: FOR i :=1 TO 10 DO for ( i=1 ; i<=10; i++ ) writeln(i) ; printf(“%d\n”,i) ; Nechá se přepsat pomocí cyklu while takto: výraz_start ; while ( výraz_stop ) { příkaz ; výraz_iter ; }
62
Příkaz skoku - goto Ve strukturovaném programu ho lze vždy nějak
obejít a proto je dobré se mu vyhnout. Na rozdíl od Pascalu není nutné návěští předem definovat definují se svým výskytem. Příklad: goto cast2; cast1: ….. ….. goto konec; cast2: ….. konec: …..
63
Příkaz return int nasobeni() { int i , j , k ;
Narazí-li běh programu na příkaz return , ukončí stávající pro- gramovou jednotku a navrátí se za příkaz svého volání do nad- řazené programové jednotky. Často se používá také k předání ( funkční ) hodnoty volající jednotce. Příklad: int nasobeni() { int i , j , k ; for ( i=0 ; i<10 ; i++ ) { for ( j=0 ; j<10 ; j++ ) { for ( k=0 ; k<10 ; k++ ) { if ( x[k] == 0 ) return 0; /* chyba */ a[i] = a[i] + b[j] / x[k] ; } return /* správně */
64
Souborové vstupně-výstupní operace
ANSI C dovoluje rozlišit při otevírání soubory textové a binární Základní datový typ pro práci se soubory je FILE . Proměnná typu FILE je ukazatel na soubor (píše se s * na začátku). Identifikátor FILE musí být velkými písmeny ! ! ! Př.: FILE *fr , *fw Je dobré mnemotechnicky rozlišit , která proměnná slouží pro soubor pro čtení ( *fr ) a která pro zápis ( *fw ) (Od slova read-číst nebo write-psát)
65
Otevření souboru pro čtení
Pascal C VAR t : TEXT; FILE *f ; … … ASSIGN( f , ‘a.txt’); f = fopen( “a.txt” , “r” ) ; RESET(f); Otevření souboru pro zápis ASSIGN( f , ‘a.txt’); f = fopen( “a.txt” , “w” ) ; REWRITE(f); Udáváme-li se souborem i cestu , pak pozor na znak \ , musíme ho zdvojit : f = fopen( “C:\\a.txt” , “w” ) ;
66
Základní operace s otevřeným souborem
Některé kompilátory vyžadují ještě specifikovat typ souboru, tj. textový ( t ) , nebo binární ( b ). f = fopen( “a.txt” , “wt” ) ; resp. f = fopen( “a.txt” , “wb” ) ; Základní operace s otevřeným souborem Čtení ze souboru c = getc(f) Zápis do souboru putc( c , f ) Formátované čtení ze souboru fscanf(f,“formát“,argumenty) Formátovaný zápis do souboru fprintf( f,“formát“,argumenty) Základní operace se std.vstupem/výstupem Čtení z klávesnice c = getchar() Zápis na obrazovku putchar(c) Formátované čtení z klávesnice scanf(“formát“,argumenty) Formátovaný zápis na obrazovku printf( “formát“,argumenty)
67
Ukončení práce se souborem
Aby bylo možné ukončit práci se souborem , je třeba uvolnit použitou vyrovnávací paměť. To zajišťuje knihovní funkce , která se jmenuje fclose as jediným parametrem určujícím jméno souborové proměnné : fclose( f ) ; Příklady: (pro práci se soubory) #include <stdio.h> Int main() { FILE *fw ; int i ; fw = fopen( “pokus.txt” , “w” ) ; for ( i=1 ; i<=10 ; i++ ) fprintf( fw, “%d\n”, I ); fclose( fw ); return 0 ; }
68
Otevření souboru souborová_proměnná = fopen( “jméno_souboru” , “režim” ) ; Režimy otevírání souboru: r … otevře textový soubor pro čtení w … vytvoří textový soubor pro zápis a … otevře textový soubor o připisování rb … otevře binární soubor pro čtení wb … vytvoří binární soubor pro zápis ab … otevře binární soubor pro připisování r+ … otevře textový soubor pro čtení/zápis w+ … vytvoří textový soubor pro čtení/zápis a+ … otevře textový soubor pro připisování r+b … otevře binární soubor pro čtení/zápis.Lze použít také rb+ w+b … vytvoří binární soubor pro čtení/zápis.Lze použít také wb+ a+b … otevře binární soubor pro čtení/připisování.Lze použít také ab+ 68
69
Testování konce řádky Příklad:
V textových souborech se často přečte najednou celá řádka. K tomu slouží funkce fgets() , která uloží obsah řádky do řetězce. Pokud ale čteme řádku znak po znaku, pak je třeba zjistit , který znak je konec řádky ( EOLN – end of line ). K tomu existují 3 možnosti: Samostatný znak CR ( 0xD ) Samostatný znak LF ( 0xA ) Dvojznak CRLF ( 0x0D0A ) V C se uvažuje za konec řádky standardně znak “nový řádek” \ n ( 0x0A ) Příklad: #include <stdio.h> Int main() { FILE *fw ; int c ; fr = fopen( “dopis.txt” , “r” ) ; while ( ( c = getc( fr ) ) != ’\n’ ) putchar( c ); fclose( fw ); return 0 ; }
70
Testování konce souboru
V textových souborech se testuje konec souboru buď pomocí makra feof() , nebo předdefinované konstanty EOF . Čtení všech znaků do konce souboru: if ((c=getchar(fr))!= EOF ) Proměnná nesmí být char, protože konstanta EOF je typu int. Příklad: (na kopírování souboru ) #include <stdio.h> Int main() { FILE *fr , *fw ; int c ; fr = fopen( “orig.txt” , “r” ) ; fw = fopen( “kopie.txt” , “w” ) ; while ( ( c = getc( fr ) ) != EOF ) putc( c , fw ); fclose( fr ); fclose( fw ); return 0 ; }
71
Příklad: (na kopírování souboru – test pomocí feov() )
#include <stdio.h> Int main() { FILE *fr , *fw ; int c ; fr = fopen( “orig.txt” , “r” ) ; fw = fopen( “kopie.txt” , “w” ) ; while ((c = getc(fr)) , feof( fr ) == 0 ) { putc(c, fw); putchar(c); /* Kontrolni vypis kopirovaneho souboru. */ } fclose( fr ); fclose( fw ); return 0 ;
72
Testování správnosti otevření a uzavření
Operace otevírání a uzavírání jsou funkce, které vrací hodnotu obsahující návratový kód informující o úspěchu či neúspěchu operace. Např.udáme-li chybné neexistující jméno souboru, či cesty, nebo otevíráme příliš mnoho souborů , nebo při uzavírání se zjistí, že chybí místo na disku apod. Při nesprávné činnosti těchto funkcí je návratový kód nulový při otevření souboru, či je roven konstantě EOF při uzavření if ( ( fr = fopen( “TEST.TXT” , ”r” ) == NULL ) printf(“Soubor se nepodařilo otevřít …\n”) ; if ( fclose( fr ) == EOF ) printf(“Soubor se nepodařilo uzavřít …\n”) ;
73
Přímý přístup do souboru
Používá se převážně v binárních souborech, kde musíme znát přesně velikosti jednotlivých položek. Můžeme si pak nastavit ukazovátko v souboru na libovolné místo. Od tohoto čísla můžeme buďto dále ze souboru číst, nebo do něho zapisovat., bez nutnosti zdržování se zpracováním předchozích dat. (Ovšem i binární soubor můžeme číst sekvenčně). Instrukce pro přímý přístup do souboru Posun v souboru na udané místo fseak() : int fseek(FILE f*,long posun; int odkud) kde:f … ukazatel na otevřený soubor posun … počet bytů od pozice dané parametrem odkud odkud … SEEK_SET … od začátku souboru SEEK_CUR … od aktuální pozice SEEK_END … od konce souboru 73
74
Zjištění aktuální pozice v souboru ftell() :
long ftell(FILE f*) kde:f … ukazatel na otevřený soubor Příklad: akt_pozice = ftell(fr); /* pamatuj si aktualni pozici */ fseek(fr,0L,SEEK_SET); /* posun na začátek souboru */ if ( hledej(fr,”ahoj”) == NULL ) /* selhalo-li hledani ,vrat se */ fseek( fr , akt_pozice ,SEEK_SET); /*posun na začátek souboru */ Instrukce pro blokový READ() a WRITE(): int fread(void *kam , size_t velikost , size_t počet, FILE *fr) int fwrite(void *odkud , size_t velikost , size_t počet, FILE *fw) kam,odkud … adresa paměti, kam se uloží/odkud se přečte blok velikost …velikost 1 prvku bloku v bytech ( SizeOf(prvek-typ) ) počet …předpokládaný počet prvků v bloku fr , fw …ukazatel na otevřený soubor návratová hodnota … skutečný počet zpracovávaných prvků 74
75
Příklad: (zápis a čtení binárních dat v binárním souboru)
#include <stdio.h> int main() { FILE *f; int i = 10; double d = ; f= fopen(“POKUS.DAT”,”wb+”); fwrite(&i, sizeof(i),1,f); /* zapis dat do souboru */ fwrite(&d, sizeof(d),1,f); /* zapis dat do souboru */ fflush(f); /* vyprazdneni bufferu */ printf(“pozice v souboru je %ld\n”, ftell(f)); fseek(f,-ftell(f),SEEK_CUR); /* posun na začátek souboru */ fseek(f , 0L , SEEK_SET); /* posun na začátek souboru */ fseek(f , -ftell(f) , SEEK_SET); /* posun na začátek souboru */ i = 0 ; d = 0; fread( &i , sizeof(i) , 1 , f ) ; /* cteni a zobrazeni dat */ fread(&d , sizeof(d) , 1 , f ); printf(“Nactena data i=%d d=%f\n”,i,d); system(“PAUSE”); } 75
76
Příklad: (práce se změnami a zjišťování pozice v textovém souboru)
(kombinace binárního a textového přístupu) #include <stdio.h> #include <stdlib.h> int main(void) { FILE *soubor; unsigned int i; char pole[100]; soubor = fopen("332-pokus.txt", "w+t"); /* w ... zapis , prepis , t ... textovy */ for (i = 0; i < 50; i++) fputc('a', soubor); /* nejdrive zapiseme do souboru 50 znaku 'a' */ fseek(soubor, 5L, SEEK_SET); /* posuneme kurzor za prvnich 5 bajtu */ for (i = 0; i < 5; i++) fputc('-', soubor); /* zapiseme 5 znaku '-' */ fseek(soubor, 0L, SEEK_END); /* posuneme se na konec souboru a zjistime jeho velikost */ printf("Velikost souboru je %lu bajtu.\n", ftell(soubor)); fseek(soubor, 0L, SEEK_SET); /* presuneme se opet na zacatek souboru */ while (!feof(soubor)) { /* a precteme jeho obsah - muže obsahovat vice radek (~vet) */ fgets(pole, sizeof(pole) / sizeof(char) - 1, soubor); /* cteni retezce do souboru */ printf("%s", pole); /* tisk retezce "pole" */ } printf("\n"); fclose(soubor); system("PAUSE");
77
#include <stdio.h>
#include <stdlib.h> char blok[20]; int main() { /* kopirovani textoveho souboru binarne po zvolenych blocich 20 bytu */ FILE *fr,*fw; int skutecne_precteno , zapsano , i ; fr =fopen("331-vstupni_bin.txt","rb"); fw=fopen("331-vystupni_bin.txt","wb"); while (1) { skutecne_precteno = fread( blok , sizeof(char) , 20 , fr ); if ( skutecne_precteno != 20 ) break; /* skonci, pokud uz neni cely blok */ zapsano = fwrite( blok , sizeof(char) , 20 , fw ); if ( zapsano != 20 ) /* nebyl zapsan cely blok */ { printf("Chyba pri zapisu do souboru"); exit(4); } } /* cteni skoncilo prectenim a prepsanim vsech celych bloku nebo s chybou */ if ( ferror(fr ) != 0 ) printf("Chyba pri cteni souboru"); else { zapsano=fwrite(blok,sizeof(char),skutecne_precteno , fw ); /* zápis zbylého bloku */ if ( zapsano != skutecne_precteno ) printf("Chyba pri zapisu do souboru"); } fclose(fr); fclose(fw); system("notepad 331-vystupni_bin.txt");
78
Standardní vstup a výstup
C jazyk ve skutečnosti pracuje s obrazovkou a klávesnicí, jako se souborem. Existují pro to standardní deklarace, FILE *stdin , *stdout které tyto proměnné definují. Tyto standardní proudy je možné zpravidla ještě prostředky operačního systému (parametry při vyvolání programu, vytvořeného v C) někam přesměrovat (redirecting). Např.: tiskni.exe > C:\user\tiskni.txt getc(stdin) … je ekvivalentem funkce … getchar() putc(c,stdout) … je ekvivalentem funkce … putchar()
79
Preprocesor jazyka C Příkazová řádka určená pro zpracování v preprocesoru začíná znakem # . Za ní nesmí být mezera. Preprocesor rozeznává následující konstrukce : Definování makra: #define <jméno_makra> <libovolný text makra> Zrušení definice makra: #undefine <jméno_makra> Podmíněný překlad textu v závislosti na konst_výraz : #if konst_výraz #elif #else #endif Vložení textu ze specifikovaného souboru: #include <jméno souboru>
80
Podmíněný překlad textu v závislosti na tom, zda je makro definováno/nedefinováno:
#ifdef <jméno_makra> #elif #else #endif Podmíněný překlad textu v závislosti na tom, zda je makro nedefinováno/definováno: #ifndef <jméno_makra> Výpis chybových zpráv během preprocesingu: #error Chybová zpráva Příklad definice makra: #define DVE_PI (2 * 3.14) #define je_velke(c) ( (c) >= ‘A’ && (c) <= ‘Z’ )
81
Vkládání souborů: Jakýkoliv soubor (i text programu) lze vložit pomocí direktivy #include .Jako příklad použití je uveden způsob překladů více programových modulů najednou versus oddělený překlad:
82
#define SOUBOR “D:\\data\\senzor.txt” #else
Řízení překladu: #if WINDOWS #define SOUBOR “D:\\data\\senzor.txt” #else #define SOUBOR “/data/senzor.txt” #endif Pokud budeme překládat na windowsových počítačích , pak na začátku nastavíme konstantu na: #define WINDOWS 1 jinak (např.pro unixové počítače) nastavíme: #define WINDOWS 0 Potom se mám nadefinuje soubor podle horní nebo dolní definice.
83
Funkce a práce s pamětí Jazyk C je založený na funkcích, které tvoří vlastní programové jednotky – uživatelských a systémových (knihovních). Jednoduchý program se může napsat do jednoho hlavního programového modulu ( main ). Většina programů však obsahuje větší počet funkcí Pojmy : Definice identifikátoru: kompletní specifikace včetně alokace paměti pro danný typ Pole působnosti identifikátoru: v programové jednotce v níž je definován Doba života identifikátoru: časový interval během kterého program pracuje v poli působnosti identifikátoru Oddělený překlad
84
Dynamická alokace paměti: místo v paměti ( Heap-hromada / Stack-zásobník ) , které se alokuje za běhu programu Statická alokace paměti: místo je alokováno v datové oblasti programu už během překladu Globální proměnná: je definována vně jakékoliv funkce a její existence začíná se spuštěním programu a končí s jeho ukončením Lokální proměnná: její existence začíná se vstupem do funkce v níž je definována a končí s ukončením této funkce. Překladač ji uloží do zásobníku Statická globální proměnná: jako globální proměnná , ale klíčové slovo static omezuje její viditelnost na část programu Statická lokální proměnná: existence začíná se vstupem do programové jednotky a končí výstupen. Překladač ji uloží do datové části programu
85
Alokace paměti Statická: používá se nejčastěji a to tehdy, pokud jsme schopni překladači sdělit konečné programové nároky.(Pozor na rekursivní volání). Globální proměnné mohou být alokovány pouze staticky. Dynamická – na hromadě: alokaci provádíime pomocí volání knihovní funkce, kdy za běhu programu volíme velikost paměti. Paměť je dostupná přes ukazatele (pointery). Fragmentace – čistič paměti. Dynamická – v zásobníku: alokaci provádíme jako běžnou definici lokální proměnné. Paměť využívá symbolického jména proměnné Pro naprostou většinu lokálně definovaných proměnných uvnitř funkce je použit tento druh alokace. Existence takovéto proměnné je pouze, po dobu zpracovávání funkce, při výstupu se její obsah ztrácí.
86
Funkce POZOR: Abychom odlišili jméno proměnné od jména funkce, uvádíme na jménem funkce důsledně kulaté závorky ! Funkce se nemohou hnízdit – tj.uvnitř jedné funkce nemůže být definována jiná funkce. Všechny funkce v C jsou skutečné – čili vrací hodnotu, ale existuje možnost, jak funkci použít jako proceduru: funkce je definována jako funkce vracející typ void , nebo funkce sice hodnotu vrací, ale nikdo ji nechce. Definice funkce: Definice: specifikace hlavičky a těla funkce Deklarace: specifikace hlavičky PASCAL C_____________ FUNCTION max(a,b:INTEGER): int max(int a, int b) INTEGER;
87
Hlavička není ukončena středníkem
Všechny parametry jsou volány hodnotou Parametry uvnitř závorek se nazývají formálními parametry funkce Typ funkce (její návratová hodnota) mohl být ve starších překladačích vynechán – implicitně byl int Tělo funkce je uzavřeno do složených závorek { } Na rozdíl od Pascalu, kdy se jménu funkce přiřadí návratová hodnota se v C používá příkaz return výraz či return (výraz) Funkce bez parametrů musí být definována i volána s () PASCAL C_____________ FUNCTION max(a,b:INTEGER): int max(int a, int b) INTEGER; { BEGIN return ( a>b ? a : b ); IF a > b THEN max := a } ELSE max := b END;
88
Procedury a datový typ void
Pokud nechceme využít předání funkční hodnoty, pak můžeme toto obejít: a) necháme vrátit návratovou hodnotu, ale nevyužijeme ji (nepřiřadíme l-hodnotě funkci – necháme ji bez přiřazení) b) typ funkce se definuje jako void (prázdný) void tisk1(int i) { printf(“%d”,i); } Vyvolání pak může vypadat jen takto: tisk1(a+b);
89
Rekurzivní funkce Rekurzivní funkce – (tj.funkce, které volají samy sebe) vypadají, jako všechny ostatní. Př.: (Výpočet faktoriálu) #include <stdio.h> int fakt(int n) { return ( ( n <= 0 ) ? 1 : n * fakt( n – 1 ) ) ; } int main() { int i ; printf( “Zadej cele celé číslo: “ ) ; scanf(“%d” , %i ) ; printf( “Faktoriál je: %d \n“ , fakt(i) ) ; return 0 ;
90
Umístění definice funkcí
Funkce nesmí být definována uvnitř jiné funkce. Jednotlivé funkce jsou definovány postupně (za sebou). Z toho plyne možný problém s pořadím definic. Pokud první funkci v druhé funkci vyvoláváme a nebyla dosud definována, nemá překladač dosud informaci o typu funkce,počtu a typu parametrů (tzv.hlavičkové informace). Jak sdělit překladači tyto informace dopředu ? Deklarací návratového typu a jména funkce (nepoužívá se , protože nepodává informaci o eventuálních parametrech – K&R). Deklarace je umístěna v deklarační části programové jednotky Pomocí funkčního prototypu (přináší verze ANSI C) Jedná se o uvedení celé hlavičky funkce. Deklarace je umístěna v před jednotlivými programovými jednotkami (je globální). Lze též použít i klíčové slovo EXTERN.
91
Konverze návratové hodnoty
Pokud není typ návratové hodnoty totožný s typem výsledku, provede se automatická konverze int konverze(double n) { return ( d ) ; } Např.je-li d=4.5 , pak je návratová hodnota oříznuta na 4 Parametry funkcí Parametry: formální při definici,skutečné při vyvolání . C-jazyk umožňuje předávání parametrů pouze jedním způsobem a to „volání hodnotou“. Výhoda: pokud je parametr aritmetický výraz, před předáním se napřed vyčíslí Nevýhoda:skutečné parametry nemohou být ve funkci změněny a předány zpět Automatická typová konverze skutečných parametrů.
92
Pointery (ukazatele) a funkce
Pokud chceme trvale změnit hodnotu skutečného parametru (volání odkazem), pak musíme použít ukazatel jako parametr. void vymen(int *p_x, int *p_y) { int pom; pom=*p_x; *p_x=*p_y; *p_y=pom; } void main() { int i=5; j=3; vymen(&i,&j); printf(“\n\n\i=%d j=%d\n\n”,i,j);
93
Lokalita a globalita proměnných
Lokální proměnné jsou dostupné jen v programové jednotce, v jejíž deklarační části jsou definovány. Globální proměnné jsou dostupné od místa definice do konce .C-souboru a definovány jsou před jednotlivými programovými jednotkami - funkcemi. Globální proměnné jsou automaticky inicializovány na nulu. Lokální proměnné nejsou automaticky inicializovány, obsah je náhodný. Některé globální stejnojmenné proměnné mohou být stejně jako v Pascalu zastíněny proměnnými lokálními. Lokální definice jsou od místa definice do konce funkce.
94
Paměťové třídy Třídy určují , v které části paměti bude proměnná umístěna. Může být umístěna před typem proměnné. auto extern static register Třída auto je implicitní pro lokální proměnné a proměnná je uložená v zásobníku. Vzniká při vstupu do funkce a zaniká výstupem. Třída extern je implicitní pro globální proměnné a proměnné jsou umístěny v datové části. Používá se při odděleném překladu programových modulů chceme-li sdílet tutéž proměnnou.
95
Třída static není pro žádý implicitní případ
Třída static není pro žádý implicitní případ. Proměnné této třídy jsou uloženy v datové oblasti. U globálních proměnných nebo funkcí: jsou viditelné pouze v modulu, kde jsou definovány. U lokálních proměnných – kdy si nechávají hodnotu i po opuštění funkce, kde jsou definovány. Třída register umožňuje definovat, že některá proměnná může být z důvodu rychlosti umístěna nikoliv v operační paměti, ale přímo v registru počítače. (C = nízkoúrovňový jazyk) Typové modifikátory const – říká, že po inicializaci proměnné již nesmí být měněna jení hodnota. volatile – upozorňuje, že udaná proměnná může být modifikována nějakou blíže nespecifikovanou činností mimo náš program.
96
Pointery (ukazatele) Definice:
Pointer představuje proměnnou v paměti, která obsahuje adresu paměti , v níž se skrývá vlastní obsah. Operátor * se nazývá dereferenční operátor a získáme pomocí něho obsah paměti na adrese , na níž ukazuje příslušný pointer. Pointer je vždy svázán s nějakým datovým typem.
97
Operátor & vrací adresu proměnné, před kterou stojí.
Každý ukazatel musí mít svůj typ – tj typ proměnné (objektu) na něž ukazuje. Operátor & vrací adresu proměnné, před kterou stojí. Operátor * vrací hodnotu uloženou na adrese před níž stojí. Příklad: #include <stdio.h> int main(void) { int q, *p_q ; q = 199; /* přiřadí proměnné q hodnotu 199 */ p_q = &q; /* přiřadí p_q adresu proměnné q */ printf("%d \n" , *p_q ) ; /* vypíše hodnotu q pomocí ukazatele */ while( ! kbhit() ) ; return ; }
98
Definice dat typu „pointer na typ“ . Uvažujeme pointer na typ int .
PASCAL C VAR p_i : ^INTEGER; int *p_i Jak získat adresu proměnné, která již existuje? Pomocí tzv. referenčního operátoru & (je možné ho použít pouze na proměnné). int i , *p_i = &i ; Inicializační příkaz , který v programu provede naplnění proměnné p_i hodnotou adresy proměnné i : p_i = &i Před práci s hodnotou je nutné vždy přiřadit pointeru adresu ! ! Přiřazení hodnoty do oblasti paměti, kam ukazuje pointer: *p_i = 1 ;
99
Přiřazení hodnoty pointerům:
Každá pointerová proměnná je l-hodnota a je tedy možné napsat: *p_i = 1; Referenční operátor & je možné aplikovat pouze na pro-měnné (mají vždy adresu na rozdíl od konstant a výrazů) p_i = &i; /* v proměnné p_i je adresa proměnné i */ 2 typy přiřazení: statické – přiřazení je správné v době překladu (levá strana musí být stejného typu jako pravá) dynamické – je správné jak během překladu, tak během výpočtu (levá strana musí být stejného typu jako pravá) (přiřazovat jen přes inicializované nebo nastavené pointery)
100
Příklady: staticky dynamicky i = 3; p_i = &i ; *p_i = 3 ; *p_i = 4; je totožné s *p_i = i; i = 3 ; i = *p_i ; p_i=&i ;
101
*p_f = 100.23; /* CHYBA */ Pozor na stejný typ pointeru a objektu, na
který pointer ukazuje: int q ; double *p_f; p_f = &q; *p_f = ; /* CHYBA */ Ukazateli p_f , který je typu double je přiřazena adresa na prvek int . Tato adresa je pak použita na levé straně přiřazovacího příkazu. Protože prvek int je obvykle kratší než double, může způsobit přepsání sousedící paměti.
102
Nulový pointer Příklad: (načtení 2 čísel a zobrazení většího z nich)
#include <stdio.h> int main(void) { int i , j , *p_i ; scanf( “%d %d” , &i , &j ); p_i = ( i > j ) ? &i ,&j ; printf(“%d je větší \n” , *p_i ) ; } Nulový pointer V Pascalu se tomuto pointeru říká nil , zatímco v C existuje podobná konstanta NULL definovaná v <stdio.h> : #define NULL 0 NULL je možné bez přetypování přiřadit všem pointerům na libovolný typ dat
103
Zobrazení hodnot pointerů (~adres)
Funkce printf() umožňuje zobrazit adresu obsaženou v ukazateli pomocí specifikátoru %p . Tuto schopnost printf() můžeme použít pro předvedení pointerové aritmetiky, jakým způsobem závisí na základním typu ukazatele Příklad: char *p_c , ch ; int *p_i , i ; float *p_f , f ; double *p_d , d ; p_c = &ch ; p_i = &i ; p_f = &f ; p_d = &d ; printf(“%p %p %p %p\n” , p_c , p_i , p_f , p_d ); p_c++; p_i++; p_f++; p_d++; /* incrementace ukazatelů */
104
Zvětšení hodnoty, na kterou ukazuje ukazatel
Pokud chceme incrementovat místo ukazatele samotnou hodnotu objektu , na něhož ukazuje ukazatel, pak je nutné použít ozávorkování ( *ukazatel )++ Příklad: int *p_q , q ; p_q = &q ; q=1; printf(“%p\n” , p_q ); /* zobrazení adresy ukazatele na q */ (*p_q)++ /* incrementace obsahu ukazatele – tedy prom. q */ printf(“%d %p\n” , q , p_q );
105
Vícenásobný nepřímý odkaz
V jazyku C je možné, aby ukazatel ukazoval na jiný ukazatel. Nazývá se to vícenásobná dereference (tj. první ukazatel obsahuje adresu druhého ukazatele a ten teprve obsahuje adresu místa prvku). Při deklaraci se před jméno takového přidává další hvězdička navíc Příklad: char **mp_ch , *p_ch , ch ; p_ch = &ch ; /* získání adresy ch */ mp _ch = &p_pch; /* získání adresy adresy ch */ /* přiřazení hodnoty ‘A’ do proměnné ch pomocí vícenásobné dereference: */ **mp_ch = ‘A’;
106
V následujícím příkladu přiřaďte hodnotu pro proměnnou val pomocí vícenásobné dereference. Hodnotu zobrazte nejprve přímo a pak pomocí vícenásobné dereference. Příklad: float val , *p_val , **mp_val ; p_val = &val ; /* získání adresy proměnné val */ mp_val = &p_val ; /* získání adresy adresy val */ ** mp_val = ; printf(“ %f %f ” , val , **mp_val);
107
Konverze pointerů Zarovnání v paměti
Při přidělování dynamické paměti je přeba občas konverzi pointerů využít. Pak používáne explicitní přetypování (protože implicitní není vhodné) . Příklad: char *p_c; int *p_i ; p_c = (char *) p_i ; /* explicitní přetypování */ Zarovnání v paměti Pokud při přetypování dochází k neočekávané chybě, pak zkoušíme použít přetypování tam a zpět a vypsat adresu u obou.Při odlišných hodnotách to může být způsobeno automatickým zarovnáním určitých datových typů od sudé nebo od liché adresy bytu (memory alignment). Bez problémů je pouze konverze z vyšších datových typů na nižší.
108
Pointery a funkce Volání parametru funkce odkazem
Protože v C-čku existuje jen volání parametru hodnotou, pomocí pointeru lze umožnit i volání odkazem. ( V Pascalu je formální parametr funkce volaný odkazem uvozen klíčovým slovem var a tím je zajištěno předání adresy skutečného parametru do zásobníku.) V C-čku je proměnná opět volána hodnotou, ale je tam uložena adresa takovéhoto parametru.) void vymen(int *p_x , int *p_y) { int pom ; /* záměna 2 proměnných */ pom = *p_x ; *p_x = *p_y ; *p_y = pom ; }
109
Vyvolání funkce se provede následovně:
int i = 5 , j = 3 ; vymen( &i , &j ) ;
110
Jednorozměrná pole pole = datová struktura složená ze stejných prvků
základní práce s pojem je v C-čku podobná Pascalu dolní mez indexu je vždy 0 ! ! ! ( v Pascalu je volitelná ) C-čko zásadně nekontroluje definované meze polí rozsah prvků pole je od 0 do počet-1 a musí být znám v době překladu – takového pole nazýváme statické Vždy když je pole definované pomocí [ ] jedná se o pole statické ( i když je definováno uvnitř funkce ) Definice pole: Pascal C : VAR x : ARRAY [0..pocet-1] TYP x[pocet] ; OF TYP ;
111
Příklad definice pole: #define MAX 10
int x [ MAX ] , y [ MAX * 2 ] , z [MAX + 1 ] ; Definice nových typů pomocí typů známých: Pascal C : TYPE VEC5 = ARRAY [0..4] OF INTEGER; typedef int VEC5[5]; VEC3 = ARRAY [0..2] OF CHAR; typedef char VEC3[3]; VEC5 = ARRAY [0..4] OF INTEGER; typedef float FVEC[10]; Definice nových proměnných s využitím definovaných typů: VAR v5 : VEC5; VEC5 v5 ; v3 : VEC3; VEC3 v3 ; f : FVEC; FVEC f ; Syntaxe při použití indexované proměnné je stejná: tj. např.: v5[3] V C-čku jsou prvky pole l-hodnotami, tj. dají se měnit.
112
Příklad: (nasčítání počtu jednotlivých písmen v souboru)
#include <stdio.h> #include <ctype.h> #define POCET ( ‘Z’ - ’A’ +1 ) int main(void) { FILE *fr; int c , i ; int pole[POCET] ; for ( i=0 ; i < POCET ; i++ ) pole[ i ] = 0 ; Fr = fopen( “TEXT.TXT” , ”r” ) ; while ( ( c = getc(fr) ) != EOF ) { if ( isalpha(c) ) pole [ toupper(c) – “A” ] ++ ; } printf ( “V souboru byl tento počet jednotlivých písmen: \n” ) ; printf ( “ %c - %d \n ” , i + “A” , pole[i] ) ; fclose(fr) ; return 0 ;
113
Jednorozměrná pole a pointery
Začíná-li první prvek od nuly a známe délku typu, pak lze lehce spočítat adresu i-tého prvku od začátku pole: &x[i] = bázová adresa x + i * sizeof(typ) ; Indexování pole: x [ i ] je totožné s pointrovým zápisem *(x+i) ( pokud délka typu je 1)
114
Pointery a jednorozměrné pole
Příklad: ( vytiskněte a 3. prvek pole a) indexy b) pointery ) int main() { int a[10]={10,20,30,40,50,60,70,80,90,100}; int *p_a; /* přiřadí p_a adresu začátku pole a : */ p_a = a; /* vytiskne prvek */ printf("%d %d %d ",a[0],a[1],a[2]); /* vytiskne prvek přes pointery*/ printf("%d %d %d ",*p_a, *(p_a+1), *(p_a+2)); while(!kbhit()); return 0; }
115
Dynamická jednorozměrná pole
Definice (vzniká při běhu programu): int *p_i ; Přidělení dynamické paměti: p_i = ( int * ) malloc( 4 * sizeof(int) ) Ekvivalentní zápisy: p_i[0] == *p_i p_i[1] == *(p_i + 1) p_i[2] == *(p_i + 2) p_i[3] == *(p_i + 3)
116
Vícerozměrná pole Kromě polí jednorozměrných můžeme v C vytvářet pole se dvěma či více rozměry Příklad:( dvourozměrné pole celých čísel count 4 x 6 ) int count[4][6]; 1 2 3
117
Pointery a vícerozměrné pole
Příklad: Je definováno pole balance (5 řádků , 3 sloupce) jako typ float. Vytiskněte pomocí pointerové aritmetiky prvek [2][1] tohoto pole. int main() { float balance[5][3]={ 0,1,2, 3,4,5, 6,7,8, 9,10,11, 12,13,14 }; float *p_balance; /* priradi p_balance adresu zacatku pole balance : */ p_balance = (float *) balance; /* vytiskne prvek [2][1] */ printf("%.1f \n" , balance[2][1] ); /* a) pres indexy */ printf("%.1f \n", *(p_balance+(2*3)+1) ); /* b) pres pointery */ while(!kbhit()); return 0; }
118
Dynamická alokace 2D-pole
© Ing. P.Kropík
119
#include <stdio.h>
int main(int argc, char *argv[]) { double **p_pole; int i , radky = 10 , sloupce = 4 ; /* Prideleni pameti pro 2D pole */ /* alokace vektoru ukazatelu na jednotlive radky */ p_pole = (double **) malloc(radky * sizeof(double *)); if (p_pole == NULL) { printf("\nNeni pamet ..."); return(1); } /* alokace prvku jednotlivych radek */ for (i = 0; i < radky; i++) { p_pole[ i ] = (double *) malloc(sloupce * sizeof(double)); if (p_pole[ i ] == NULL) { printf("\nNeni pamet ..."); return(1); } } /* Uvolneni pameti pro 2D-pole */ for (i = 0; i < radky; i++) free((void *) p_pole[ i ]); free((void *) p_pole); while(!kbhit()); return 0;
120
Řetězce Řetězec (string) je speciální typ jednorozměrného pole
Je to pole znaků ( složené z prvků typu char ) , index začíná od 0 a je zakončen znakem \0 . Jsou definované staticky (známe dopředu maximální délku pole) nebo dynamicky: statické: char a[10] , b[20] = “inicializacni text” - aut.určením max.délky při překladu char s2[]=“….text….”
121
Pozor! Při statické definici nelze přiřadit ke jménu řetězce řetězec jako konstantu: char str[10];
str=“…retezec…” /* nelze */ Statická definice odkazem na pointer ukazujici na řetězcovou konstantu (není to dynamická deklarace): char *str=“Ahoj”; Dynamická deklarace (bere s hromady volné paměti ): char *s_dyn; s_dyn=(char *) malloc(10); strcpy(s_dyn,“…Text do 10 znaku …“); /* s_dyn=“…Text …”; je chybne ! ! ! */ Uvolnění dynamické paměti: free((void *) s_dyn); Nulový pointer … má hodnotu 0 Nulový řetězec … první znak řetězce je /0
122
Práce s řetězci #include <stdio.h> Int main(void) {
char str[11]; printf(“Zadejte retezec: “); scanf(“%s”, str); printf(“Retezec je %s\n\n”, str); return 0; } Přístup k jednotlivým znakům řetězce: char s[10]; for (i=0;i<10;i++) s[i]=‘*’; s[10]=‘\0’; /* Ukoncovaci znak – nutny !!! */
123
Standardní řetězcové funkce: <string.h>
délka řetězce: int strlen(char *s) -příklad: printf(“\n\n%d\n\n”,strcpy(“Ahoj!”)); kopírování řetězce: char *strcpy(char *s1, char *s2) -příklad: strcpy(str,”Ahoj “); spojení řetězců: char *strcat (char *s1, char *s2) -příklad: strcat(str,”+ Nazdar “); nalezení znaku: char *strchr(char *s1, char c) -příklad: strchr(str,’z’); /* neexistuje-li vrati NULL */ porovnání řetězců: int strcmp(char *s1, char *s2) nalezení podřetězu: char *strstr(char *s1, char *s2)
124
Výčtové typy enum jméno výčtu { seznam položek } seznam proměnných ;
V C-čku lze definovat seznam pojmenovaných celočíselných konstant, nazývaný výčet (enumerace). Pro definici výčtového typu lze použít tento obecný formát: enum jméno výčtu { seznam položek } seznam proměnných ; Jednu z položek jméno výčtu nebo seznam proměnných lze vynechat Příklad: enum typ_barvy {červená, zelená, žlutá} barva
125
enum typ_barvy mojebarva;
Standardně přiřazuje překladač konstantám výčtu celočíselné hodnoty. Konstanta ve výčtu stojící nejvíce vlevo má hodnotu 0, každá další napravo má hodnotu o 1 vyšší. Např.: (červená=0,zelená=1,modrá=2). Lze to explicitně změnit při definici výčtu (např. modrá=8). Máme-li už nadefinován výčet, lze definovat nové proměnné : enum typ_barvy mojebarva; Výčet je celočíselný typ a výčtová proměnná může obsahovat libovolnou celočíselnou hodnotu (nejen tu definovanou výčtem)
126
Příklad: #include <stdio.h> enum computer {klavesnice , CPU , obrazovka , tiskarna }; inst main() { enum computer comp; comp=CPU; printf(“%d”,comp); }
127
Struktury Struktura je sdružený datový typ, složený ze 2 a více proměnných – prvků struktury Každý prvek může mít svůj vlastní typ na rozdíl od pole struct jméno typu { typ prvek 1; typ prvek 2; : typ prvek N; } seznam proměnných ;
128
Položka seznam proměnných představuje konkrétní jména proměnných tohoto typu (nemusí být při definici typu uvedena). Jméno typu představuje konkrétní pojmenování definovaného typu (nemusí být uvedeno,pokud se struktura definuje jen pro proměnné uvedené v definici). Příklad : Struct katalog { char autor[40]; /* jmeno autora byte */ char nazev[40]; /* jmeno knihy byte */ char vydavatel[40]; /* vydavatel byte */ unsigned datum; /* datum vydani byte */ unsigned char vyd; /* cislo vydani byte */ } karta Přistupujeme strukturovaným jménem: karta.datum=1776; Printf(“datum vydani: %u ”, karta.datum);
129
strcpy(karta.autor,“Victor Hugo“);
strcpy(karta.nazev,“Bídníci“); strcpy(karta.vydavatel,“Olympia“); karta.vyd=2; Jakmile nadefinujeme strukturu, můžeme vytvořit další proměnné stejného typu: struct jméno typu seznam proměnných ; Příklad: struct katalog var1 , var2 , var3 ; Struktury lze ukládat také do pole jako jiné typy dat: struct katalog cat[100]; První strukturu z tohoto pole lze zpřístupnit následovně: cat[0] Přiřazení 33.-té položce katalogu – číslu vydání hodnotu 2: cat[33].vyd = 2;
130
Příklad na definici a použití struktury:
#include <stdio.h> Struct s_type { int i; char ch; double d; char str[80]; } s; int main() { printf("Zadej cele cislo : "); scanf("%d",s.i); printf("Zadej znak : "); scanf("%c",s.ch); printf("Zadej cislo s pohyblivou radovou carkou : "); scanf("%lf",s.d); printf("Zadej retezec : "); scanf("%s",s.str); printf ("%d %c %f %s", s.i , s.ch , s.d , s.str); while( ! kbhit() ); return 0; }
131
Zjištění velikosti struktury:
#include <stdio.h> struct s_type { int i; char ch; int *p; double d; } s; /* globalne definovana struktura */ int main() { printf("Struktura s_type ma delku : %d " , sizeof(struct s_type) ) ; printf("Struktura s_type ma delku : %d " , sizeof(s) ) ; while( ! kbhit() ); return 0; }
132
Jména prvků uvnitř struktury a lokální/globální proměnné:
#include <stdio.h> int main() { struct s_type { int i; int j; } s; /* lokalne definovana struktura */ int i; i = 10; /* stejnojmenne promenne uvnitr a */ s.i=100; /* vne struktury nejsou v konfliktu */ s.j=101; printf ("%d %d %d“ , i , s.i , s.j ) ; while( ! kbhit() ); return 0; }
133
Ukazatele na struktury
Ukazatel na proměnnou typu struktura je jako ukazatel na jiný typ dat : struct s_type { int i; char string[80]; } s , *p_s ; p_s = &s ; Místo tečkového operátoru používáme šipkový operátor: p_s->I = 1; strcpy(p_s->string,“.… textový řetězec ….“);
134
Vnořené struktury Dosud jsme pracovali se strukturami, kde základními stavebními prvky jsou typy jazyka C . Pokud jako prvky struktur figurují jiné struktury, říkáme jim vnořené struktury #define POCET_NA_LINCE 10 struct delnik { char jmeno[80]; int prum_pocet_jednotek_za_hodinu; int prum_pocet_chyb_za_hodinu; } ; struct vyrob_linka { int kod_vyrobku; double cena_materialu; struct delnik delnici[POCET_NA_LINCE]; } linka1 , linka2 ; Přiřazení: linka1.delnici[1]. prum_pocet_jednotek_za_hodinu = 1 ;
135
Unie (Uniony) Unie se nazývá v jazyku C jedno místo v paměti, které je sdíleno dvěma nebo více proměnnými. Proměnné mohou být různého typu, současně lze však používat pouze jednu. Definují se podobně jako struktury: union jméno unionu { typ prvek 1; typ prvek 2; : typ prvek N; } seznam proměnných ;
136
Podobně, jako u struktur , může i jméno unie a seznam proměnných chybět
Velikost unie musí být tak velká, aby se tam vešel největší prvek unie a je stanovená už při překladu ( sizeof( … ) ) Prvky mohou být libovolný datový typ jazyka C Příklad: union u_type { int i; /* 2 byty */ char c[2]; /* 2 byty c[0] a c[1] */ double d; /* 8 bytu */ } sample , *p_s ; p_s = &sample; Pro přístup k prvkům unie používáme jako u struktur tečkový operátor (přistupujeme-li pomocí ukazatele, pak šipkový operátor) : sample.d = ; p_s->i = 101 ;
Podobné prezentace
© 2024 SlidePlayer.cz Inc.
All rights reserved.