Irányelvek speciális utasítások az előfeldolgozóhoz (előfeldolgozó irányelv) vagy a fordítóhoz (fordító irányelv), hogy hogyan kell feldolgozni a forráskód egy részét vagy egészét, vagy beállítani néhány zászlót a végső objektumon, és ezeket használják a forráskód írásának megkönnyítésére (például hordozhatóbbá) és a forráskód érthetőbbé tételére. Az irányelveket az előfeldolgozó kezeli, amely vagy egy külön program, amelyet a fordító hív meg, vagy maga a fordító része.
# includeEdit
A C-nek van néhány funkciója a nyelv részeként, mások pedig egy szabványos könyvtár részeként, amely egy kódtár, amely minden szabványnak megfelelő C fordító mellett elérhető. Amikor a C fordító lefordítja a programot, általában összekapcsolja a szokásos C könyvtárral is. Például egy #include <stdio.h>
irányelv találkozásakor az irányelvet az stdio tartalmával helyettesíti.h fejléc fájl.
amikor a könyvtár funkcióit használja, a C megköveteli, hogy deklarálja, mit használna. A program első sora egy előfeldolgozási irányelv, amelynek így kell kinéznie:
#include <stdio.h>
a fenti sor okozza a C deklarációkat, amelyek az stdio-ban vannak.h fejlécet kell használni a programban. Általában ezt úgy valósítják meg, hogy egyszerűen beillesztik a programba az stdio nevű fejlécfájl tartalmát.h, rendszerfüggő helyen található. Az ilyen fájlok helyét leírhatja a fordító dokumentációja. A szabványos C fejlécfájlok listája az alábbiakban található a fejlécek táblázatban.
az stdio.a H fejléc különféle deklarációkat tartalmaz az input/output (I/O) számára, az I/O mechanizmusok absztrakcióját használva, az úgynevezett patakokat. Például van egy stdout nevű kimeneti adatfolyam-objektum, amelyet a szöveg szabványos kimenetre történő kiadására használnak, amely általában a számítógép képernyőjén jeleníti meg a szöveget.
Ha a fenti példához hasonló szögletes zárójeleket használ, az előfeldolgozót arra utasítják, hogy keresse meg az include fájlt a standard includes fejlesztői környezet elérési útja mentén.
#include "other.h"
ha idézőjeleket ( “” ) használ, az előfeldolgozó várhatóan néhány további, általában felhasználó által definiált helyen keres a fejlécfájlhoz, és csak akkor tér vissza a standard include routs-hoz, ha nem található meg ezeken a további helyeken. Gyakran előfordul, hogy ez az űrlap tartalmazza a keresést ugyanabban a könyvtárban, mint az #include irányelvet tartalmazó fájl.
Megjegyzés: A #include direktíva szállítóspecifikus implementációihoz ellenőrizze a használt fejlesztői környezet dokumentációját.
HeadersEdit
The C90 standard headers list:
|
|
|
Headers added since C90:
|
|
|
#pragmaEdit
a Pragma (pragmatikus információ) irányelv része a szabványnak, de a pragma jelentése attól függ, hogy a Pragma milyen a használt szabvány szoftveres megvalósítása. A # pragma irányelv lehetőséget nyújt arra, hogy különleges viselkedést kérjen a fordítótól. Ez az irányelv a leghasznosabb olyan programok esetében, amelyek szokatlanul nagyok, vagy amelyeknek ki kell használniuk egy adott fordító képességeit.
a pragmákat a forrásprogramban használják.
#pragma token(s)
- a pragmát általában egyetlen token követi, amely egy parancsot jelent a fordító számára, hogy engedelmeskedjen. A támogatott tokenek listájához ellenőrizze a C szabvány szoftver implementációját, amelyet használni kíván. Nem meglepő, hogy a #pragma direktívákban megjelenő parancsok halmaza minden fordítónál eltérő; meg kell néznie a fordító dokumentációját, hogy megnézze, mely parancsokat engedélyezi, és mit tesznek ezek a parancsok.
például az egyik leginkább megvalósított előfeldolgozó direktíva, #pragma once
amikor egy fejlécfájl elejére kerül, azt jelzi, hogy a fájl, ahol lakik, átugrásra kerül, ha az előfeldolgozó többször is tartalmazza.
Megjegyzés: más módszerek léteznek erre a műveletre, amelyet általában az include guards használatának neveznek.
#defineEdit
figyelem: az előfeldolgozó makrók, bár csábítóak, meglehetősen váratlan eredményeket hozhatnak, ha nem jól csinálják. Mindig tartsa szem előtt, hogy a makrók szöveges helyettesítések tenni a forráskódot, mielőtt bármi össze. A fordító nem tud semmit a makrókról, és soha nem látja őket. Ez okozhat homályos hibák, többek között a negatív hatásokat. Inkább a nyelvi funkciókat használja, ha vannak ekvivalensek (például használja const int
vagy enum
a #define
d állandók helyett).
ennek ellenére vannak olyan esetek, amikor a makrók nagyon hasznosak (lásd a debug
makró alább egy példát).
a # define direktíva olyan értékek vagy makrók meghatározására szolgál, amelyeket az előfeldolgozó a program forráskódjának manipulálására használ a fordítás előtt. Mivel az előfeldolgozó definíciókat helyettesítik, mielőtt a fordító a forráskódra hatna, a #define által bevezetett hibákat nehéz nyomon követni.
konvenció szerint a #define használatával definiált értékek nagybetűkkel vannak megnevezve. Bár ez nem követelmény, nagyon rossz gyakorlatnak tekintik az ellenkezőjét. Ez lehetővé teszi az értékek könnyű azonosítását a forráskód olvasásakor.
ma a #define-t elsősorban a fordítói és platformbeli különbségek kezelésére használják. Például egy define tartalmazhat egy állandót, amely a megfelelő hibakód egy rendszerhíváshoz. A # define használatát tehát korlátozni kell, hacsak nem feltétlenül szükséges; a typedef utasítások és az állandó változók gyakran biztonságosabban végezhetik ugyanazokat a funkciókat.
A #define parancs másik jellemzője, hogy argumentumokat vehet fel, így pszeudo-függvény létrehozójaként meglehetősen hasznos. Vegye figyelembe a következő kódot:
#define ABSOLUTE_VALUE( x ) ( ((x) < 0) ? -(x) : (x) )...int x = -1;while( ABSOLUTE_VALUE( x ) ) {...}
összetett makrók használata esetén általában érdemes extra zárójeleket használni. Figyeljük meg, hogy a fenti példában az “x” változó mindig a saját zárójelkészletén belül van. Ily módon teljes egészében értékelni fogják, mielőtt összehasonlítanák 0-val vagy megszorozzák -1-gyel. Ezenkívül az egész makrót zárójelek veszik körül, hogy megakadályozzák, hogy más kódokkal szennyeződjön. Ha nem vigyázol, fennáll annak a veszélye, hogy a fordító félreértelmezi a kódot.
a mellékhatások miatt nagyon rossz ötletnek tekinthető a fent leírt makrófunkciók használata.
int x = -10;int y = ABSOLUTE_VALUE( x++ );
Ha az ABSOLUTE_VALUE() valós függvény lenne, az ‘x’ értéke most ‘-9’ lenne, de mivel ez egy argumentum volt egy makróban, kétszer kibővítették, így értéke -8.
példa:
a makrók veszélyeinek szemléltetéséhez fontolja meg ezt a naiv makrót
#define MAX(a,b) a>b?a:b
és a kódot
i = MAX(2,3)+5;j = MAX(3,2)+5;
vessen egy pillantást erre, és fontolja meg, hogy mi lehet a végrehajtás utáni érték. Az utasítások
int i = 2>3?2:3+5;int j = 3>2?3:2+5;
így a végrehajtás után i=8 és j=3 az I=j=8 várt eredménye helyett! Ezért figyelmeztették, hogy használjon egy extra zárójelkészletet fent, de még ezekkel is, az út tele van veszélyekkel. A figyelmeztető olvasó gyorsan rájöhet, hogy ha a a
vagy b
kifejezéseket tartalmaz, akkor a definíciónak zárójelben kell tartalmaznia az A,b Minden használatát a makródefinícióban, így:
#define MAX(a,b) ((a)>(b)?(a):(b))
Ez működik, feltéve,hogy a, b-nek nincs mellékhatása. Valójában a
i = 2;j = 3;k = MAX(i++, j++);
k=4, i=3 és j=5 eredményt ad. Ez nagyon meglepő lenne bárki számára, aki elvárja, hogy MAX () függvényként viselkedjen.
tehát mi a helyes megoldás? A megoldás az, hogy egyáltalán nem használjuk a makrót. Egy globális, inline függvény, mint ez
inline int max(int a, int b) { return a>b?a:b }
nem rendelkezik a fenti buktatókkal, de nem fog működni minden típusnál.
megjegyzés: az explicit inline
deklaráció nem igazán szükséges, kivéve, ha a definíció egy fejlécfájlban van, mivel a fordító képes inline funkciókat az Ön számára (a gcc-vel ez megtehető a -finline-functions
vagy -O3
). A fordító gyakran jobb, mint a programozó, hogy megjósolja, mely funkciókat érdemes beilleszteni. Ezenkívül a funkcióhívások nem igazán drágák (régen voltak).
a fordító valójában szabadon figyelmen kívül hagyhatja a inline
kulcsszót. Ez csak egy tipp (kivéve, hogy inline
szükséges ahhoz, hogy egy függvény definiálható legyen egy fejlécfájlban anélkül, hogy hibaüzenetet generálna, mivel a függvény több fordítóegységben van meghatározva).
(#,##)
A # és ## operátorokat a #define makróval használjuk. A # használatával a # után az első argumentum idézőjelekben karakterláncként kerül visszaadásra. Például a
#define as_string( s ) # s
paranccsal a fordító ezt a parancsot
puts( as_string( Hello World! ) ) ;
a
puts( "Hello World!" );
a ## használatával összefűzi a ## előtti részt azzal, ami utána van. Például a
#define concatenate( x, y ) x ## y...int xy = 10;...
parancs a fordítót
printf( "%d", concatenate( x, y ));
printf( "%d", xy);
– re fordítja, amely természetesen a 10-es normál kimenetet jeleníti meg.
lehetséges összefűzni egy makró argumentumot állandó előtaggal vagy utótaggal, hogy érvényes azonosítót kapjunk, mint a
#define make_function( name ) int my_ ## name (int foo) {}make_function( bar )
amely meghatározza a my_bar () nevű függvényt. De nem lehet integrálni egy makró argumentumot egy állandó karakterláncba a összefűzési operátor segítségével. Annak érdekében, hogy ilyen hatást érjünk el, használhatjuk az ANSI C tulajdonságot, hogy két vagy több egymást követő karakterlánc-állandót egyenértékűnek tekintünk egyetlen karakterlánc-állandóval, ha találkozunk. Ezzel a tulajdonsággal
#define eat( what ) puts( "I'm eating " #what " today." )eat( fruit )
írható, amelyből a makroprocesszor
puts( "I'm eating " "fruit" " today." )
lesz, amelyet viszont a C elemző egyetlen karakterlánc-állandóként értelmez.
a következő trükk használható a numerikus állandók karakterlánc-literálokká alakításához
#define num2str(x) str(x)#define str(x) #x#define CONST 23puts(num2str(CONST));
Ez egy kicsit trükkös, mivel 2 lépésben bővül. Először num2str(CONST)
helyébe a str(23)
lép, amelyet viszont a "23"
vált. Ez hasznos lehet A következő példában:
#ifdef DEBUG#define debug(msg) fputs(__FILE__ ":" num2str(__LINE__) " - " msg, stderr)#else#define debug(msg)#endif
Ez egy szép hibakeresési üzenetet ad, beleértve a fájlt és az üzenet kiadásának sorát. Ha a hibakeresés nincs meghatározva, a hibakeresési üzenet teljesen eltűnik a kódból. Legyen óvatos, hogy ne használja ezt a fajta konstrukció semmit, hogy van mellékhatása, mivel ez ahhoz vezethet, hogy a hibákat, hogy jelennek meg, majd eltűnnek attól függően, hogy a fordítási paraméterek.
macrosEdit
a makrók nincsenek típusellenőrzve, így nem értékelik az argumentumokat. Ezenkívül nem engedelmeskednek megfelelően a hatókörnek, hanem egyszerűen átveszik a nekik átadott karakterláncot, és a makró szövegében a makró argumentum minden előfordulását kicserélik az adott paraméter tényleges karakterláncára (a kódot szó szerint átmásolják arra a helyre, ahonnan hívták).
példa a makró használatára:
#include <stdio.h> #define SLICES 8 #define ADD(x) ( (x) / SLICES ) int main(void) { int a = 0, b = 10, c = 6; a = ADD(b + c); printf("%d\n", a); return 0; }
— az “a” eredményének “2”- nek kell lennie (b + c = 16- > add-hoz kerül – > 16 / SLICES – > az eredmény “2”)
megjegyzés:
általában rossz gyakorlat a makrók meghatározása a fejlécekben.
a makrót csak akkor kell meghatározni, ha nem lehet ugyanazt az eredményt elérni egy funkcióval vagy más mechanizmussal. Egyes fordítók képesek optimalizálni a kódot, ahol a kis funkciókra irányuló hívásokat inline kóddal helyettesítik, elutasítva az esetleges sebességelőnyt.A typedefs, enums és inline (C99-ben) használata gyakran jobb megoldás.
azon kevés helyzetek egyike, ahol az inline függvények nem fognak működni-tehát inkább függvényszerű makrókat kell használni-a fordítási idő állandóinak inicializálása (a struktúrák statikus inicializálása).Ez akkor történik, amikor a makró argumentumai literálok, amelyeket a fordító optimalizálhat egy másik literálra.
# errorEdit
a # error direktíva leállítja a fordítást. Amikor találkozik a szabvány meghatározza, hogy a fordítónak ki kell bocsátania egy diagnosztikát, amely tartalmazza az irányelvben fennmaradó tokeneket. Ezt leginkább hibakeresési célokra használják.
a programozók a” #error “-t használják egy feltételes blokkban, hogy azonnal leállítsák a fordítót, amikor a” #IF “vagy a” # ifdef ” – a blokk elején-fordítási időt észlel.Általában a fordító kihagyja a blokkot (és a benne lévő “#error” direktívát), és a fordítás folytatódik.
#error message
#warningEdit
sok fordító támogatja a #figyelmeztető irányelvet. Amikor találkozik, a fordító diagnosztikát bocsát ki, amely tartalmazza az irányelvben szereplő fennmaradó tokeneket.
#warning message
#undefEdit
a #undef irányelv nem határoz meg makrót. Az azonosítót korábban nem kellett meghatározni.
#if, # Elif, #endif (conditionals)Szerkesztés
A # if parancs ellenőrzi, hogy egy kontrolling feltételes kifejezés nullára vagy nem nullára értékel-e, és kizárja vagy tartalmazza a kódblokkot. Például:
#if 1 /* This block will be included */ #endif #if 0 /* This block will not be included */ #endif
a feltételes kifejezés bármilyen C operátort tartalmazhat, kivéve a hozzárendelési operátorokat, a növekmény és csökkentés operátorokat, az operátor címét és az operátor méretét.
egy egyedi operátor, amelyet az előfeldolgozásban használnak, és sehol máshol nem a meghatározott operátor. 1-et ad vissza, ha a makrónév, opcionálisan zárójelbe zárva, jelenleg definiálva van; 0, ha nem.
a # endif parancs egy #if
#ifdef
vagy #ifndef
által indított blokkot fejez be.
a # elif parancs hasonló a #if
parancshoz, azzal a különbséggel, hogy a kódblokkok sorozatából nyer ki egyet. Pl.:
#if /* some expression */ : : : #elif /* another expression */ : /* imagine many more #elifs here ... */ : #else /* The optional #else block is selected if none of the previous #if or #elif blocks are selected */ : : #endif /* The end of the #if block */
#ifdef,#ifndefEdit
a #ifdef parancs hasonló a #if
parancshoz, azzal a különbséggel, hogy az azt követő kódblokk ki van választva, ha egy makrónév meg van adva. Ebben a tekintetben
#ifdef NAME
egyenértékű
#if defined NAME
a #ifndef parancs hasonló az #ifdef-hez, azzal a különbséggel, hogy a teszt megfordul:
#ifndef NAME
A
egyenértékű a
#if !defined NAME
#LINEEDIT
ezzel az előfeldolgozó irányelvvel állíthatjuk be az irányelvet követő sor nevét és sorszámát új értékekre. Ez a __FILE__ és __LINE__ makrók beállítására szolgál.