C Programmerings – / preprocessordirektiv och makron

direktiven är speciella instruktioner riktade till preprocessorn (preprocessordirektivet) eller till kompilatorn (kompilatordirektivet) om hur det ska behandla en del eller hela din källkod eller ställa in några flaggor på det slutliga objektet och används för att göra det lättare att skriva källkod (mer bärbar till exempel) och för att göra källkoden mer förståelig. Direktiv hanteras av förprocessorn, som antingen är ett separat program som åberopas av kompilatorn eller en del av kompilatorn själv.

#includeEdit

C har vissa funktioner som en del av språket och några andra som en del av ett standardbibliotek, vilket är ett arkiv med kod som är tillgängligt tillsammans med varje standardkonformant C-kompilator. När C-kompilatorn sammanställer ditt program Länkar det vanligtvis också det med standard C-biblioteket. Till exempel, när man stöter på ett #include <stdio.h> – direktiv, ersätter det direktivet med innehållet i stdio.h header-fil.

När du använder funktioner från biblioteket kräver C att du deklarerar vad du skulle använda. Den första raden i programmet är ett förbehandlingsdirektiv som ska se ut så här:

#include <stdio.h>

ovanstående rad orsakar C-deklarationerna som finns i stdio.h header som ska ingå för användning i ditt program. Vanligtvis implementeras detta genom att bara infoga innehållet i en rubrikfil som heter stdio i ditt program.h, belägen i en systemberoende plats. Platsen för sådana filer kan beskrivas i din kompilatörs dokumentation. En lista med standard C-huvudfiler listas nedan i tabellen rubriker.

stdio.h header innehåller olika deklarationer för input/output (I/O) med hjälp av en abstraktion av I/O-mekanismer som kallas strömmar. Till exempel finns det ett utgångsströmobjekt som heter stdout som används för att mata ut text till standardutgången, som vanligtvis visar texten på datorskärmen.

om du använder vinkelfästen som exemplet ovan instrueras förprocessorn att söka efter include-filen längs utvecklingsmiljövägen för standarden includes.

#include "other.h"

om du använder citattecken ( ”” ) förväntas förprocessorn söka på några ytterligare, vanligtvis användardefinierade platser för rubrikfilen och falla tillbaka till standarden inkludera sökvägar endast om den inte finns på dessa ytterligare platser. Det är vanligt att det här formuläret inkluderar sökning i samma katalog som filen som innehåller #include-direktivet.

OBS: Du bör kontrollera dokumentationen för den utvecklingsmiljö du använder för alla leverantörsspecifika implementeringar av #include-direktivet.

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

pragma-direktivet (pragmatisk information) är en del av standarden, men betydelsen av någon pragma beror på programvaruimplementering av den standard som används. # Pragma-direktivet ger ett sätt att begära speciellt beteende från kompilatorn. Detta direktiv är mest användbart för program som är ovanligt stora eller som behöver utnyttja kapaciteten hos en viss kompilator.

Pragmas används i källprogrammet.

#pragma token(s)
  1. pragma följs vanligtvis av en enda token, vilket representerar ett kommando för kompilatorn att lyda. Du bör kontrollera programvaruimplementeringen av C-standarden du tänker använda för en lista över de stödda tokens. Inte överraskande är uppsättningen kommandon som kan visas i #pragma-direktiv olika för varje kompilator; du måste konsultera dokumentationen för din kompilator för att se vilka kommandon den tillåter och vad dessa kommandon gör.

till exempel ett av de mest implementerade preprocessordirektiven, #pragma once när det placeras i början av en header-fil, indikerar att filen där den finns kommer att hoppas över om den ingår flera gånger av preprocessorn.

Obs: andra metoder finns för att göra denna åtgärd som vanligen kallas att använda inkluderar vakter.

#defineEdit

varning: Preprocessormakron, även om de är frestande, kan ge ganska oväntade resultat om de inte görs rätt. Tänk alltid på att makron är textutbyten som görs till din källkod innan något sammanställs. Kompilatorn vet ingenting om makron och får aldrig se dem. Detta kan ge obskyra fel, bland andra negativa effekter. Föredrar att använda språkfunktioner, om det finns likvärdiga (i exempel använd const int eller enum istället för #defined konstanter).

som sagt finns det fall där makron är mycket användbara (se debug Makro nedan för ett exempel).

#define-direktivet används för att definiera värden eller makron som används av förprocessorn för att manipulera programkällkoden innan den kompileras. Eftersom preprocessordefinitioner ersätts innan kompilatorn agerar på källkoden är eventuella fel som introduceras av #define svåra att spåra.

enligt konvention namnges värden som definieras med #define i versaler. Även om det inte är ett krav anses det vara mycket dålig praxis att göra något annat. Detta gör att värdena lätt kan identifieras när du läser källkoden.

idag används # define främst för att hantera kompilator-och plattformsskillnader. T. ex. kan en definiera hålla en konstant som är lämplig felkod för ett systemanrop. Användningen av # define bör således begränsas om det inte är absolut nödvändigt; typedef-uttalanden och konstanta variabler kan ofta utföra samma funktioner säkrare.

en annan funktion i kommandot #define är att det kan ta argument, vilket gör det ganska användbart som en pseudofunktion skapare. Tänk på följande kod:

#define ABSOLUTE_VALUE( x ) ( ((x) < 0) ? -(x) : (x) )...int x = -1;while( ABSOLUTE_VALUE( x ) ) {...}

det är i allmänhet bra att använda extra parenteser när man använder komplexa makron. Observera att i ovanstående exempel är variabeln ”x” alltid inom sin egen uppsättning parenteser. På så sätt utvärderas det i sin helhet innan det jämförs med 0 eller multipliceras med -1. Hela makrot omges också av parenteser för att förhindra att det förorenas av annan kod. Om du inte är försiktig riskerar du att kompilatorn misstolkar din kod.

på grund av biverkningar anses det vara mycket dåligt att använda makrofunktioner som beskrivits ovan.

int x = -10;int y = ABSOLUTE_VALUE( x++ );

om ABSOLUTE_VALUE() var en riktig funktion skulle ’x’ nu ha värdet ’-9’, men eftersom det var ett argument i ett makro utvidgades det två gånger och har därmed ett värde på -8.

exempel:

för att illustrera farorna med makron, överväga detta naiva Makro

#define MAX(a,b) a>b?a:b

och koden

i = MAX(2,3)+5;j = MAX(3,2)+5;

ta en titt på detta och överväga vad värdet efter körning kan vara. Uttalandena omvandlas till

int i = 2>3?2:3+5;int j = 3>2?3:2+5;

således, efter körning i=8 och j=3 istället för det förväntade resultatet av i=j=8! Det var därför du varnade för att använda en extra uppsättning parentes ovan, men även med dessa är vägen fylld av faror. Varningsläsaren kan snabbt inse att om a eller b innehåller uttryck, måste definitionen parentesera varje användning av a,b i makrodefinitionen, så här:

#define MAX(a,b) ((a)>(b)?(a):(b))

detta fungerar, förutsatt att a,b inte har några biverkningar. Faktum är att

i = 2;j = 3;k = MAX(i++, j++);

skulle resultera i k=4, i=3 och j=5. Detta skulle vara mycket förvånande för alla som förväntar sig att MAX () ska bete sig som en funktion.

Så vad är rätt lösning? Lösningen är inte att använda makro alls. En global inline-funktion, som denna

inline int max(int a, int b) { return a>b?a:b }

har ingen av fallgroparna ovan, men fungerar inte med alla typer.

Obs: den explicitainline deklarationen är egentligen inte nödvändigt om definitionen är i en header-fil, eftersom din kompilator kan inline funktioner för dig (med gcc detta kan göras med -finline-functions eller -O3). Kompilatorn är ofta bättre än programmeraren för att förutsäga vilka funktioner som är värda inlining. Funktionssamtal är inte riktigt dyra (de brukade vara).

kompilatorn är faktiskt fri att ignorera inline nyckelordet. Det är bara en ledtråd (förutom att inline är nödvändig för att en funktion ska kunna definieras i en rubrikfil utan att generera ett felmeddelande på grund av att funktionen definieras i mer än en översättningsenhet).

(#,##)

operatorerna # OCH ## används med makrot #define. Att använda # gör att det första argumentet efter # returneras som en sträng i citat. Till exempel kommer kommandot

#define as_string( s ) # s

att göra kompilatorn att göra detta kommando

puts( as_string( Hello World! ) ) ;

till

puts( "Hello World!" );

med ## sammanfogar vad som är före ## med vad som är efter det. Till exempel kommer kommandot

#define concatenate( x, y ) x ## y...int xy = 10;...

att göra kompilatorn vänd

printf( "%d", concatenate( x, y ));

till

printf( "%d", xy);

som naturligtvis visar 10 till standardutgång.

det är möjligt att sammanfoga ett makroargument med ett konstant prefix eller suffix för att få en giltig identifierare som i

#define make_function( name ) int my_ ## name (int foo) {}make_function( bar )

som definierar en funktion som heter my_bar(). Men det är inte möjligt att integrera ett makroargument i en konstant sträng med hjälp av sammanfogningsoperatören. För att erhålla en sådan effekt kan man använda ANSI C-egenskapen att två eller flera på varandra följande strängkonstanter anses motsvara en enda strängkonstant när de påträffas. Med den här egenskapen kan man skriva

#define eat( what ) puts( "I'm eating " #what " today." )eat( fruit )

som makroprocessorn kommer att förvandlas till

puts( "I'm eating " "fruit" " today." )

som i sin tur tolkas av C-parsern som en enda strängkonstant.

följande trick kan användas för att förvandla en numerisk konstanter till strängbokstäver

#define num2str(x) str(x)#define str(x) #x#define CONST 23puts(num2str(CONST));

detta är lite knepigt, eftersom det expanderas i 2 steg. Förstnum2str(CONST) ersätts medstr(23), som i sin tur ersätts med"23". Detta kan vara användbart i följande exempel:

#ifdef DEBUG#define debug(msg) fputs(__FILE__ ":" num2str(__LINE__) " - " msg, stderr)#else#define debug(msg)#endif

detta ger dig ett trevligt felsökningsmeddelande inklusive filen och raden där meddelandet utfärdades. Om DEBUG inte är definierat kommer dock felsökningsmeddelandet att försvinna helt från din kod. Var försiktig så att du inte använder den här typen av konstruktion med något som har biverkningar, eftersom det kan leda till buggar som visas och försvinner beroende på kompileringsparametrarna.

macrosEdit

makron är inte typkontrollerade och utvärderar därför inte argument. Dessutom följer de inte omfattningen ordentligt, utan tar helt enkelt strängen som skickas till dem och ersätter varje förekomst av makroargumentet i Makro-texten med den faktiska strängen för den parametern (koden kopieras bokstavligen till den plats den anropades från).

ett exempel på hur man använder ett makro:

 #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; }

– resultatet av ”A” ska vara ”2” (b + c = 16- > skickas för att lägga till- > 16 / skivor – > resultatet är ”2”)

Obs:
det är vanligtvis dålig praxis att definiera makron i rubriker.

ett makro bör definieras endast när det inte går att uppnå samma resultat med en funktion eller någon annan mekanism. Vissa kompilatorer kan Optimera kod till där samtal till små funktioner ersätts med inline-kod, vilket förnekar eventuell hastighetsfördel.Att använda typedefs, enums och inline (i C99) är ofta ett bättre alternativ.

en av de få situationer där inline-funktioner inte fungerar-så du är ganska tvungen att använda funktionsliknande makron istället-är att initiera kompileringskonstanter (statisk initialisering av strukturer).Detta händer när argumenten till makrot är bokstäver som kompilatorn kan optimera till en annan bokstavlig.

#errorredit

#feldirektivet stoppar kompileringen. När man stöter på standarden anger att kompilatorn ska avge en diagnostik som innehåller de återstående tokens i direktivet. Detta används mest för felsökningsändamål.

programmerare använder ”# error ” inuti ett villkorligt block för att omedelbart stoppa kompilatorn när ”#if” eller ”#ifdef”-i början av blocket-upptäcker ett kompileringstidsproblem.Normalt hoppar kompilatorn över blocket (och” # error ” – direktivet inuti det) och sammanställningen fortsätter.

 #error message

#warningEdit

många kompilatorer stöder ett #varningsdirektiv. När man stöter på, avger kompilatorn en diagnostik som innehåller de återstående tokensna i direktivet.

 #warning message

#undefEdit

#undef-direktivet definierar ett makro. Identifieraren behöver inte ha definierats tidigare.

#if,#else,#elif,#endif (conditionals)redigera

kommandot #if kontrollerar om ett kontrollerande villkorligt uttryck utvärderas till noll eller icke-noll, och utesluter eller inkluderar ett kodblock respektive. Till exempel:

 #if 1 /* This block will be included */ #endif #if 0 /* This block will not be included */ #endif

det villkorliga uttrycket kan innehålla vilken C-operator som helst förutom tilldelningsoperatörerna, inkrement-och minskningsoperatörerna, operatörens adress och operatorens storlek.

en unik operator som används vid förbehandling och ingen annanstans är den definierade operatorn. Den returnerar 1 om makronamnet, valfritt inom parentes, för närvarande är definierat; 0 om inte.

kommandot #endif avslutar ett block startat av #if#ifdef, eller #ifndef.

kommandot #elif liknar #if, förutom att det används för att extrahera en från en serie kodblock. T. ex.:

 #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

kommandot #ifdef liknar#if, förutom att kodblocket som följer det väljs om ett Makronamn definieras. I detta avseende

#ifdef NAME

motsvarar

#if defined NAME

kommandot #ifndef liknar #ifdef, förutom att testet är omvänd:

#ifndef NAME

motsvarar

#if !defined NAME

#LINEEDIT

detta preprocessordirektiv används för att ställa in filnamnet och radnumret för raden som följer direktivet till nya värden. Detta används för att ställa in __FILE__ och __LINE__ makron.

Lämna ett svar

Din e-postadress kommer inte publiceras.