C programozási / előfeldolgozó irányelvek és makrók

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:

  • <assert.h>
  • <ctype.h>
  • <errno.h>
  • <float.h>
  • <limits.h>
  • <locale.h>
  • <math.h>
  • <setjmp.h>
  • <signal.h>
  • <stdarg.h>
  • <stddef.h>
  • <stdio.h>
  • <stdlib.h>
  • <string.h>
  • <time.h>

Headers added since C90:

  • <complex.h>
  • <fenv.h>
  • <inttypes.h>
  • <iso646.h>
  • <stdbool.h>
  • <stdint.h>
  • <tgmath.h>
  • <wchar.h>
  • <wctype.h>

#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)
  1. 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 #defined á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 avagy 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 inlinedeklará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-functionsvagy -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.

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.