inkludera vakt

I programmeringsspråken C och C++ är en #include guard , ibland kallad macro guard , header guard eller file guard , en speciell konstruktion som används för att undvika problemet med dubbel inkludering när man hanterar include-direktivet .

C -förbehandlaren bearbetar direktiv av formen #include <fil> i en källfil genom att lokalisera den associerade filen disken och transkludera ("inklusive") dess innehåll till en kopia av källfilen känd som översättningsenheten, och ersätter direktivet include i processen. Filerna som ingår i detta avseende är i allmänhet rubrikfiler , som vanligtvis innehåller deklarationer av funktioner och klasser eller strukturer . Om vissa C- eller C++-språkkonstruktioner definieras två gånger är den resulterande översättningsenheten ogiltig . #include-skydd förhindrar att denna felaktiga konstruktion uppstår av den dubbla inkluderingsmekanismen.

Tillägget av #include guards till en rubrikfil är ett sätt att göra den filen idempotent . En annan konstruktion för att bekämpa dubbel inkludering är #pragma once , som är icke-standard men nästan universellt stödd bland C- och C++- kompilatorer .

Dubbel inkludering

Exempel

Följande C-kod visar ett verkligt problem som kan uppstå om #include-skydd saknas:

Filen "grandparent.h"

  
     
 struct  foo  {  int  medlem  ;  }; 

Filen "parent.h"

  #inkludera  "farförälder.h" 

Filen "barn.c"

 
  #inkludera  "morförälder.h"  #inkludera  "förälder.h" 

Resultat

  
     

  
     
 struct  foo  {  int  medlem  ;  };  struct  foo  {  int  medlem  ;  }; 

Här har filen "child.c" indirekt inkluderat två kopior av texten i rubrikfilen " grandparent.h" . Detta orsakar ett kompileringsfel eftersom strukturtypen foo alltså kommer att definieras två gånger. I C++ skulle detta kallas ett brott mot endefinitionsregeln .

Användning av #include-vakter

Exempel

I det här avsnittet används samma kod med tillägg av #include guards. C- förprocessorn förbearbetar rubrikfilerna, inklusive och förbearbetar dem ytterligare rekursivt . Detta kommer att resultera i en korrekt källfil, som vi kommer att se.

Filen "grandparent.h"




  
     


 #ifndef GRANDPARENT_H  #define GRANDPARENT_H  struct  foo  {  int  medlem  ;  };  #endif  /* GRANDPARENT_H */ 

Filen "parent.h"

  #inkludera  "farförälder.h" 

Filen "barn.c"

 
  #inkludera  "morförälder.h"  #inkludera  "förälder.h" 

Resultat

  
     
 struct  foo  {  int  medlem  ;  }; 

Här har den första inkluderingen av "grandparent.h" makrot GRANDPARENT_H definierat. När "child.c" inkluderar "grandparent.h" vid andra gången (medan det inkluderar "parent.h"), eftersom # ifndef- testet returnerar false, hoppar förprocessorn ner till #endif , och undviker därmed den andra definitionen av struct foo . Programmet kompilerar korrekt.

Diskussion

Olika namnkonventioner för vaktmakrot kan användas av olika programmerare . Andra vanliga former av exemplet ovan inkluderar GRANDPARENT_INCLUDED , CREATORSNAME_YYYYMMDD_HHMMSS (med lämplig tidsinformation ersatt) och namn genererade från ett UUID . ( Namn som börjar med ett understreck och en stor bokstav eller namn som innehåller dubbelt understreck, som _GRANDPARENT__H och __GRANDPARENT_H , är dock reserverade för språkimplementeringen och ska inte användas av användaren.)

Naturligtvis är det viktigt att undvika att duplicera samma include-guard-makronamn i olika rubrikfiler, eftersom inkludering av 1:an kommer att förhindra att 2:an inkluderas, vilket leder till förlust av alla deklarationer, inline-definitioner eller andra #includes i 2:a rubriken.

Svårigheter

För att #include-skydd ska fungera korrekt måste varje skydd testa och villkorligt ställa in ett annat förprocessormakro. Därför måste ett projekt som använder #include guards utarbeta ett sammanhängande namnschema för sina include guards, och se till att dess schema inte kommer i konflikt med det för någon tredje parts headers som det använder, eller med namnen på eventuella globalt synliga makron.

Av denna anledning tillhandahåller de flesta C- och C++-implementeringar ett icke-standardiserat #pragma once -direktiv. Detta direktiv, infogat överst i en rubrikfil, säkerställer att filen endast inkluderas en gång. Objective -C- språket (som är en superuppsättning av C) introducerade ett #import- direktiv, som fungerar precis som #include , förutom att det bara inkluderar varje fil en gång, vilket eliminerar behovet av #include-skydd.

Andra språk

PL/I använder %INCLUDE -satsen som motsvarighet till C:s #include -direktiv. IBM Enterprise PL/I stöder också %XINCLUDE -satsen som kommer att "integrera extern text i källprogrammet om den inte tidigare har inkluderats." Detta skiljer sig från C #pragma en gång genom att programmet inklusive den externa texten ansvarar för att specificera att duplicerad text inte ska inkluderas, snarare än själva den inkluderade texten.

Den erbjuder också en XPROCEDURE -sats, liknande en PROCEDURE -sats, som ignorerar den andra och efterföljande förekomsten av en XPROCEDURE med samma namn,

Se även

  1. ^ C++ standard (ISO/IEC 14882) avsnitt 17.4.3.1.2/1
  2. ^ C-standard (ISO/IEC 9899) avsnitt 7.1.3/1.
  3. ^ "Mål C: Att definiera klasser" . developer.apple.com . 2014-09-17 . Hämtad 2018-10-03 .
  4. ^ IBM Corporation (augusti 2017). Enterprise PL/I för z/OS PL/I för AIX Enterprise PL/I för z/OS Språkreferens version 5 version 1 ( PDF) . sid. 257 . Hämtad 7 april 2022 .

externa länkar