230 likes | 328 Views
8. přednáška 27. 3. 2008 -typedef - preprocesor Studijní materiály najdete na adrese: http://www.uai.fme.vutbr.cz/~vdumek/. Typedef. - pro zpřehlednění deklarací nových typů typedef typ identifikátor_typu
E N D
8. přednáška 27. 3. 2008 -typedef - preprocesor Studijní materiály najdete na adrese: http://www.uai.fme.vutbr.cz/~vdumek/
Typedef • - pro zpřehlednění deklarací nových typů • typedef typ identifikátor_typu • - pomocí tohoto příkazu nevytvoříme nový sémantický typ, pouze tvoříme nové jméno pro existující datový typ (omezení spojená s původním typem zůstávají) • - používá se hlavně pro zvýšení přehlednosti a čitelnosti programů, pro usnadnění změn typů (operátor přetypování) a snazší modifikaci typů • typedef int LENGTH; • typedef float *P_FLOAT; • P_FLOAT p_m, p_n; /* použití */ • typedef char *STRING; • typedef int (*PFI)(); /* ukazatel na funkci */
Typedef - dává možnost definovat nové typy, kterým můžeme deklarovat proměnné, zlepší se čitelnost programů, změny se dají dělat z jednoho místa int f1(); int f1(); funkce vracející int typedef int *intuk; int *u1; intuk u1; ukazatel na int int *f2(); intuk f2(); fce vracející ukazatel na int typedef int far *faruk; int far *u2; faruk u2; vzdálený ukazatel na int int far *f3(); faruk f3(); funkce vracející vzdálený ukazatel na int typedef int (*fnuk1)(int); int (*fu1)(int); fnuk1 fu1; ukazatel na fci s jedním int parametrem, která vrací int typedef int (*fnuk2)(intuk); int (*fu2)(int *iu); fnuk2 fu2; ukazatel na fci s parame- trem směrník na int, která vrací int
Preprocesor - úprava zdrojového textu před vlastním překladem - rozvoj maker - náhrada symbolicky označených částí skutečným textem - substituce textu - podmíněný překlad - výběr z různých variant podle podmínek - vložené soubory - možnost před překladem přidat soubor - odstraňuje komentáře, nadbytečné mezery, tabelátory - činnost preprocesoru je řízena direktivami, které jsou na tzv. řídicíchřádcích (označení #), řídicí řádek může obsahovat i komentář - u moderních překladačů není fáze předzpracování oddělená, ne- vzniká žádný výstupní text, pokud je potřeba, používá se samo- statný program (CPP -> přípona .I) Definice konstant a maker - možnost symbolického pojmenování konstant, příkazů, výrazů, … možnost modifikovat textové náhrady pomocí parametrů
test.c test.i void main(void) { // komentar #define DESET 10 #define SYMBOL if(DESET == 9) printf("ahoj"); /* dalsi komentar */ } test.cpp 1: void main(void) test.cpp 2: { test.cpp 3: test.cpp 4: test.cpp 5: test.cpp 6: test.cpp 7: if(10 == 9) test.cpp 8: printf("ahoj"); test.cpp 9: test.cpp 10: test.cpp 11: } test.cpp 12: CPP
Preprocesor #define - používá se k definici konstant a maker #define symbol - takto definovaný symbol nemá žádnou hodnotu, pouze existuje, lze jej použít pro řízení podmíněného překladu (direktiva #ifdef) #define jméno_makra text - text vzniklý náhradou makra se nazývá rozvoj makra, text po první mezeře za identifikátorem, pozor na poznámky, nahrazování se neprovádí v řetězcích #define DESET 10 /* konstanta - symbolické jméno pro hodnotu 10 */ #define beep putch(7) /* symbolické označení pro výpis znaku BELL */ if (k == DESET) beep; rozvine se if(k == 10) putch(7); - dosazení hodnoty se provádí na úrovni textu před překladem
Preprocesor - při ukončení definice se nepoužívá středník, symbol se nahradí textem při rozvoji, středník se použije v místě použití - definice se dá použít i na více řádků #define DLOUHY_TEXT “Toto je velmi dlouhy text, který se nevejde ani za nic na \ jeden radek “ #define CHYBA if (chyba==1) \ printf(“\nstala se nejaka chyba”); \ else if (chyba==2) \ printf(“\nstala se nejaka jina chyba”); \ else \ printf(“\nstala se uplne jina chyba”); - definice makra s parametry #define jmeno_makra(seznam formalnich parametru) text - pozor na umístění mezer
#include <stdio.h> #define DLOUHY_TEXT "Toto je velmi dlouhy text, kterě se nevejde ani za nic \ na jeden radek" #define CHYBA if (chyba==1) \ printf("\nstala se nejaka chyba"); \ else if (chyba==2) \ printf("\nstala se nejaka jina chyba"); \ else \ printf("\nstala se uplne jina chyba"); void main(void) { chyba=2; printf ("%s", DLOUHY_TEXT); CHYBA chyba=1; CHYBA } Toto je velmi dlouhy text, který se nevejde ani za nic na jeden radek stala se nejaka jina chyba stala se nejaka chyba
Preprocesor • - volání makra je formálně shodné s voláním funkce (mnoho knihovních funkcí jsou ve skutečnosti makra), počet formálních a skutečných parametrů musí souhlasit • zdánlivě nadbytečné množství závorek zamezí chybám při rozvoji maker v případě použití složených výrazů nebo v souvislosti s prioritou operátorů #define MAX(a,b) (((a) > (b)) ? (a) : (b)) i=MAX(3, y); i=(((3) > (y)) ? (3) : (y)); #define NA_DRUHOU(x) x*x špatně i = 6; j = NA_DRUHOU(i); j = 6*6; /* 36 OK */ k = NA_DRUHOU(i+2); k = i+2*i+2; /* 20 CHYBA */ l = 100/NA_DRUHOU(5); l = 100/5*5; /* 100 CHYBA */ #define NA_DRUHOU(x) ((x)*(x)) správně
Preprocesor - totožná syntaxe volání funkce a makra může vést k chybám, neboť u maker není prováděna (na rozdíl od funkcí) žádná typová kontrola, není předávána hodnota parametru, ale jeho textová podoba, zrychluje se běh programu #define NA_DRUHOU(x) ((x)*(x)) int na_druhou(int x) { return x*x; } ….. i = 2; j = NA_DRUHOU(i++); /* j=((i++)*(i++))=2*3=6, i=4 */ ….. i = 2; k = na_druhou(i++); /* k=4, i=3 */ - definovaný symbol nemůže být definován znovu
Preprocesor - makra mohou být vnořována, po rozvoji makra projde prepro- cesor vzniklý text ještě jednou, aby mohl provést event. rozvoj dalšího makra #define NA_DRUHOU(x) ((x)*(x)) #define MAX(a,b) (((a)>(b)) ? (a) : (b)) i = MAX(k, NA_DRUHOU(j)); po rozvoji: i = (((k) > (NA_DRUHOU(j))) ? (k) : (NA_DRUHOU(j))); i = (((k) > (((j)*(j)))) ? (k) : (((j)*(j)))); - rozvoj makra nesmí vést na další direktivu, která již není pre- procesorem zpracována #define VLOZENI #include <stdio.h> VLOZENI se rozvine jako #include <stdio.h> /* chyba */
Preprocesor - jako jména maker lze použít i klíčová slova. Využívání této vla- stnosti se nedoporučuje #define int long /* formálně správně, zcela nevhodné */ - parametry maker se dosazují buď přímo jako text, který se přidá do zdrojového textu programu, nebo mohou být napřed převe- deny na textový řetězec. K tomu se používá symbol #. #define VYSLEDEK(x) printf(#x “=%f\n”, x) VYSLEDEK(Obvod); /* printf(“Obvod” “=%f\n”, Obvod); - při chybné definici makra může dojít k chybné interpretaci zá- měrů programátora #define VYSLEDEK_1(x) printf(“x=%f\n”, x); ….. VYSLEDEK_1(Obvod); /* printf(“x=%f\n”, Obvod); */
Preprocesor - symbol definovaný pomocí direktivy #define platí až do konce překládaného modulu (včetně vkládaných modulů), pokud se ne- vyskytne direktiva #undef #undef jmeno_makra pokud mělo rušené makro parametry, tyto se neuvádějí. Symboly přestávají existovat, vrací se do stavu před použitím #define. Lze testovat pomocí #ifdef, #ifndef u podmíněného překladu. - symbol lze definovat po direktivě #undef zcela jiným způsobem, při pokusu o novou definici bez předchozího použití #undef je hlášena chyba #define DELKA_BLOKU 512 ….. delka=pocet*DELKA_BLOKU; /* pocet * 512 */ ….. #undef DELKA_BLOKU /* dale nelze pouzivat */
Preprocesor #define DELKA_BLOKU 128 ….. delka=pocet*DELKA_BLOKU; /* pocet * 128 */ ….. Vložené soubory - lze vložit do zdrojového souboru text, obsažený v jiném souboru samotná direktiva #include je odstraněna #include <jmeno_souboru> #include “jmeno_souboru” - jméno souboru může být uvedeno včetně specifikace cesty k souboru, to má význam u syntaxe s uvozovkami, pro úhlové zá- vorky platí předdefinovaný adresář s vkládanými soubory - direktiva include se nepoužívá pouze pro hlavičkové soubory, ale je použitelná i pro zdrojové texty
Preprocesor S1.C S2.C #define KOLIK 20 #include “S1.C” typedef struct { int i,j,k; MOJE_STR a, b, *c; float d; int p=KOLIK; char a[20]; } MOJE_STR; void main(void) { void moje_funkce(MOJE_STR *x, int a) ….. {…..} moje_funkce(&a, KOLIK); ….. } - vlivem vložení souboru S1.C do souboru S2.C lze používat všechny proměnné a funkce, probíhá zde jeden překlad - ANSI překladače (C++) povolují i vkládání makra - může být i vnoření (8), pozor na cyklické vnoření #define myinclude “hlavicka.h” #include myinclude/* vkládá se hlavicka.h */ #include “myinclude.h” /* vkládá se myinclude.h */
Preprocesor - direktiva #include se používá pro vkládání souborů s definicemi maker, konstant, datových typů, deklaracemi globálních promě- nných a prototypy funkcí, hlavičkové soubory, rozdělení pro různé problematické okruhy - zmenšení překládaných objemů Podmíněný překlad - mechanismus k vyřazení některé části zdrojového textu z přek- ladu na základě splnění nějaké podmínky. Vyřazovaná část je nahrazena prázdnými řádky. Používají se direktivy: #if, #ifdef, #ifndef, #elif, #else, #endif Nejjednodušší forma: #if konst_vyraz ….. /* podmíněně překládaný text */ #endif
Preprocesor • - úsek mezi direktivami je překládán pouze tehdy, když má konstantní výraz nenulovou hodnotu, každé if musí mít své endif a musí být obě ve stejném překládaném souboru • - výraz, který řídí překlad musí být vyčíslitelný v době překladu, nesmí obsahovat proměnné, sizeof, enum a casting • jako podmínka pro řízení překladu se často používá test, zda je nějaký symbol definován #define BLOK 512 #define POCET 3 ….. #if BLOK > 200 ….. /* preklad OK */ #endif ….. /* preklad vzdy */ #if (BLOK == 128) && (POCET > 2) ….. /* nepreklada se */ #endif ….. /* preklad vzdy */
#ifdef symbol ….. /* preklada se , pokud je symbol definovany */ #endif ….. #ifndef symbol ….. /* preklada se, pokud symbol neni definovany */ #endif #if defined symbol /* totez jako #ifdef symbol */ #if defined (symbol) /* totez, zavorky jsou nepovinne */ #if !defined symbol /* totez jako #ifndef */ - výhodou direktivy defined je možnost testovat definici více symbolů pomocí jedné direktivy #define SYMBOL1 #define SYMBOL2 #if defined SYMBOL1 && !defined SYMBOL2 ….. /* nepreklada se */ #endif #undef SYMBOL2 #if defined SYMBOL1 && !defined SYMBOL2
Preprocesor - pokud má mít generovaný kód v některém úseku v závislosti na určitých podmínkách více než dvě varianty, používá se vedle if a else ještě elif #if konst_vyraz1 ….. /* překládá se pro pravdivý konst_vyraz1 */ #elif konst_vyraz2 ….. /* překládá se pro !konst_vyraz1 && konst_vyraz2 */ #elif konst_vyraz3 ….. /* !konst_vyraz1 && !konst_vyraz2 && konst_vyraz3 */ #else ….. /* překládá se, pokud není pravdivý žádný výraz */ #endif - počet direktiv elif není omezen, pro každou direktivu if ale může být nejvýše jedna direktiva else, je poslední řídící direktivou před endif - podmíněně překládané sekce mohou být v rámci zdrojového tex- tu vnořovány do libovolné hloubky, každá if musí mít odpoví- dající endif
Preprocesor #if SYMBOL<50 ….. /* překládá se pro <50 */ #elif SYMBOL<300 ….. /* překládá se pro 50<=SYMBOL<300 */ #elif SYMBOL<1000 ….. /* překládá se pro 300<=SYMBOL<1000 */ #else ….. /* překládá se pro 1000<=SYMBOL */ #endif #define X 10 #define Y 80 ….. #if X<25 ….. /* překládá se pro X<25 */ #if Y>100 ….. /* překládá se pro (X<25)&&(Y>100) */ #endif /* ukončení podmínky Y>100 */ ….. #endif /* ukončení podmínky X<25 */
Preprocesor - podmíněný překlad používáme v případech, kdy potřebujeme generování různých variant kódu z jediného zdrojového textu (různé kompilátory, různé operační systémy, různé paměťové modely) Ovládání čísel řádků - překladač i preprocesor si během své práce pamatují jméno právě zpracovávaného zdrojového souboru a číslo právě zpra- covávaného řádku tohoto souboru. Tyto informace jsou obsa- ženy ve výstupním textu generovaném preprocesorem, objevují se v chybových hlášeních - direktiva #lineumožňuje nastavit jméno souboru i číslo řádku tak, jak programátor potřebuje #line konstanta [jmeno_souboru] - konstanta - další číslo, které bude přiřazeno
Preprocesor - je-li vynechán nepovinný parametr jmeno_souboru, platí napo- sledy nastavené direktivou line, resp. originální jméno souboru - v běžné programátorské praxi se používá ojediněle Generování chyb při překladu - slouží k zabránění určitých podmínek při překladu (nevhodný rozsah konstant, nevhodný překladač, nevhodný paměťový model, nevhodný operační systém, …) #error text - text je hlášení, které se objeví při překladu #define BUFFER 2000 #if BUFFER < 1000 … #error Buffer je prilis maly ifndef __LARGE__ #endif #error Nevhodny pametovy model #endif
Preprocesor Pragmy - tato direktiva je mlhavě definovaná normou ANSI, pomocí ní se zapisují instrukce pro překladač závislé na implementaci #pragma jmeno_direktivy - jména direktiv mají být pro konkrétní překladač navržena tak, aby nedocházelo ke kolizím (pro každý překladač unikátní). Po- kud překladač narazí na direktivu pragma, jejíž název nezná, di- rektivu ignoruje aniž by generoval chybovou zprávu - direktiva pragma se většinou používá pro nastavení některých voleb překladače na úrovni zdrojového textu místo v parame- trech příkazového řádku #pragma argsused #pragma startup jmeno_fce priorita #pragma inline #pragma saveregs #pragma warn