8 pednka 27 3 2008 typedef preprocesor Studijn

  • Slides: 23
Download presentation
8. přednáška 27. 3. 2008 -typedef - preprocesor Studijní materiály najdete na adrese: http:

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

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

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 f 1(); int *u 1; int *f 2(); typedef int *intuk; intuk u 1; intuk f 2(); int far *u 2; int far *f 3(); typedef int far *faruk; faruk u 2; faruk f 3(); int (*fu 1)(int); typedef int (*fnuk 1)(int); fnuk 1 fu 1; int (*fu 2)(int *iu); funkce vracející int ukazatel na int fce vracející ukazatel na int vzdálený ukazatel na int funkce vracející vzdálený ukazatel na int ukazatel na fci s jedním int parametrem, která vrací int typedef int (*fnuk 2)(intuk); fnuk 2 fu 2; ukazatel na fci s parametrem směrník na int, která vrací int

Preprocesor - úprava zdrojového textu před vlastním překladem - rozvoj maker - náhrada symbolicky

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á, nevzniká žádný výstupní text, pokud je potřeba, používá se samostatný 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 void main(void) { // komentar #define DESET 10 #define SYMBOL if(DESET ==

test. c void main(void) { // komentar #define DESET 10 #define SYMBOL if(DESET == 9) printf("ahoj"); /* dalsi komentar */ } test. i CPP 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:

Preprocesor #define - používá se k definici konstant a maker #define symbol - takto

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 #define beep putch(7) /* konstanta - symbolické jméno pro hodnotu 10 */ /* 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,

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

#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

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 #define MAX(a, b) (((a) > (b)) ? (a) : (b)) i=MAX(3, y); i=(((3) > (y)) ? (3) : (y)); - 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 NA_DRUHOU(x) x*x i = 6; j = NA_DRUHOU(i); k = NA_DRUHOU(i+2); l = 100/NA_DRUHOU(5); #define NA_DRUHOU(x) špatně j = 6*6; k = i+2*i+2; l = 100/5*5; ((x)*(x)) správně /* 36 OK */ /* 20 CHYBA */ /* 100 CHYBA */

Preprocesor - totožná syntaxe volání funkce a makra může vést k chybám, neboť u

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) int na_druhou(int x) { return x*x; } …. . i = 2; j = NA_DRUHOU(i++); …. . i = 2; k = na_druhou(i++); ((x)*(x)) /* j=((i++)*(i++))=2*3=6, i=4 */ /* 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 preprocesor vzniklý text ještě

Preprocesor - makra mohou být vnořována, po rozvoji makra projde preprocesor vzniklý text ještě jednou, aby mohl provést event. rozvoj dalšího makra #define NA_DRUHOU(x) #define MAX(a, b) ((x)*(x)) (((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í preprocesorem zpracována #define VLOZENI #include <stdio. h> se rozvine jako #include <stdio. h> /* chyba */

Preprocesor - jako jména maker lze použít i klíčová slova. Využívání této vlastnosti se

Preprocesor - jako jména maker lze použít i klíčová slova. Využívání této vlastnosti 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řevedeny na textový řetězec. K tomu se používá symbol #. #define VYSLEDEK(x) printf(#x “=%fn”, x) VYSLEDEK(Obvod); /* printf(“Obvod” “=%fn”, Obvod); - při chybné definici makra může dojít k chybné interpretaci záměrů programátora #define VYSLEDEK_1(x) …. . VYSLEDEK_1(Obvod); printf(“x=%fn”, x); /* printf(“x=%fn”, Obvod); */

Preprocesor - symbol definovaný pomocí direktivy #define platí až do konce překládaného modulu (včetně

Preprocesor - symbol definovaný pomocí direktivy #define platí až do konce překládaného modulu (včetně vkládaných modulů), pokud se nevyskytne direktiva #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; …. . #undef DELKA_BLOKU /* pocet * 512 */ /* dale nelze pouzivat */

Preprocesor #define DELKA_BLOKU 128 …. . delka=pocet*DELKA_BLOKU; …. . /* pocet * 128 */

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 S 1. C S 2. C #define KOLIK 20 typedef struct { int

Preprocesor S 1. C S 2. C #define KOLIK 20 typedef struct { int i, j, k; float d; char a[20]; } MOJE_STR; #include “S 1. C” MOJE_STR a, b, *c; int p=KOLIK; void moje_funkce(MOJE_STR *x, int a) {…. . } void main(void) { …. . moje_funkce(&a, KOLIK); …. . } - vlivem vložení souboru S 1. C do souboru S 2. 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 #include “myinclude. h” /* vkládá se hlavicka. h */ /* vkládá se myinclude. h */

Preprocesor - direktiva #include se používá pro vkládání souborů s definicemi maker, konstant, datových

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řekladu 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

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 #define BLOK 512 #define POCET 3 …. . #if BLOK > 200 …. . #endif …. . #if (BLOK == 128) && (POCET > 2) …. . #endif …. . /* preklad OK */ /* preklad vzdy */ /* nepreklada se */ /* preklad vzdy */ - jako podmínka pro řízení překladu se často používá test, zda je nějaký symbol definován

#ifdef symbol …. . #endif …. . #ifndef symbol …. . #endif /* preklada

#ifdef symbol …. . #endif …. . #ifndef symbol …. . #endif /* preklada se , pokud je symbol definovany */ /* preklada se, pokud symbol neni definovany */ #if defined symbol #if defined (symbol) #if !defined symbol /* totez jako #ifdef symbol */ /* totez, zavorky jsou nepovinne */ /* totez jako #ifndef */ - výhodou direktivy defined je možnost testovat definici více symbolů pomocí jedné direktivy #define SYMBOL 1 #define SYMBOL 2 #if defined SYMBOL 1 && !defined SYMBOL 2 …. . /* nepreklada se */ #endif #undef SYMBOL 2 #if defined SYMBOL 1 && !defined SYMBOL 2

Preprocesor - pokud má mít generovaný kód v některém úseku v závislosti na určitých

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_vyraz 1 …. . #elif konst_vyraz 2 …. . #elif konst_vyraz 3 …. . #else …. . #endif /* překládá se pro pravdivý konst_vyraz 1 */ /* překládá se pro !konst_vyraz 1 && konst_vyraz 2 */ /* !konst_vyraz 1 && !konst_vyraz 2 && konst_vyraz 3 */ /* překládá se, pokud není pravdivý žádný výraz */ - 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 textu vnořovány do libovolné hloubky, každá if musí mít odpovídající endif

Preprocesor #if SYMBOL<50 …. . #elif SYMBOL<300 …. . #elif SYMBOL<1000 …. . #else

Preprocesor #if SYMBOL<50 …. . #elif SYMBOL<300 …. . #elif SYMBOL<1000 …. . #else …. . #endif #define X 10 #define Y 80 …. . #if X<25 …. . #if Y>100 …. . #endif /* překládá se pro <50 */ /* překládá se pro 50<=SYMBOL<300 */ /* překládá se pro 300<=SYMBOL<1000 */ /* překládá se pro 1000<=SYMBOL */ /* překládá se pro X<25 */ /* překládá se pro (X<25)&&(Y>100) */ /* ukončení podmínky Y>100 */ /* 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

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ě zpracová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 #line umožň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í naposledy nastavené direktivou line, resp. originální

Preprocesor - je-li vynechán nepovinný parametr jmeno_souboru, platí naposledy 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 … ifndef __LARGE__ #error Nevhodny pametovy model #endif #if BUFFER < 1000 #error Buffer je prilis maly #endif

Preprocesor Pragmy - tato direktiva je mlhavě definovaná normou ANSI, pomocí ní se zapisují

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í). Pokud překladač narazí na direktivu pragma, jejíž název nezná, direktivu 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 parametrech příkazového řádku #pragma argsused #pragma inline #pragma startup jmeno_fce priorita #pragma saveregs #pragma warn