inclusief bewaker - include guard

In de programmeertalen C en C++ is een #include guard , ook wel een macro guard , header guard of file guard genoemd , een specifieke constructie die wordt gebruikt om het probleem van dubbele inclusie te voorkomen bij het omgaan met de include-richtlijn .

De C preprocessor werkwijzen richtlijnen van de vorm #include <file>in een bronbestand door het plaatsen van de bijbehorende fileop schijf en van sjablonen ( "bevattende") de inhoud in een kopie van het bronbestand zogenaamde vertaaleenheid , ter vervanging van de include in het proces. De bestanden in dit verband zijn over het algemeen header-bestanden , die doorgaans declaraties van functies en klassen of structs bevatten . Als bepaalde C- of C++-taalconstructies twee keer worden gedefinieerd , is de resulterende vertaaleenheid ongeldig . #include bewakers voorkomen dat deze foutieve constructie ontstaat door het dubbele insluitingsmechanisme.

De toevoeging van #include guards aan een headerbestand is een manier om dat bestand idempotent te maken . Een andere constructie om dubbele inclusie tegen te gaan is #pragma once , dat niet-standaard is maar bijna universeel wordt ondersteund door C- en C++- compilers .

Dubbele opname

Voorbeeld

De volgende C-code laat een reëel probleem zien dat kan ontstaan ​​als #include-bewakers ontbreken:

Bestand "grootouder.h"

struct foo {
    int member;
};

Bestand "ouder.h"

#include "grandparent.h"

Bestand "kind.c"

#include "grandparent.h"
#include "parent.h"

Resultaat

struct foo {
    int member;
};
struct foo {
    int member;
};

Hier heeft het bestand "child.c" indirect opgenomen twee exemplaren van de tekst in de header file "grandparent.h". Dit veroorzaakt een compilatiefout , omdat het structuurtype foodus twee keer wordt gedefinieerd. In C++ zou dit een schending van de regel met één definitie worden genoemd .

Gebruik van #include guards

Voorbeeld

In deze sectie wordt dezelfde code gebruikt met de toevoeging van #include guards. De C-preprocessor verwerkt de header-bestanden voor, inclusief en verder recursief voorbewerkt . Dit zal resulteren in een correct bronbestand, zoals we zullen zien.

Bestand "grootouder.h"

#ifndef GRANDPARENT_H
#define GRANDPARENT_H

struct foo {
    int member;
};

#endif /* GRANDPARENT_H */

Bestand "ouder.h"

#include "grandparent.h"

Bestand "kind.c"

#include "grandparent.h"
#include "parent.h"

Resultaat

struct foo {
    int member;
};

Hier heeft de eerste opname van "grandparent.h" de macro GRANDPARENT_Hgedefinieerd. Wanneer "child.c" voor de tweede keer "grandparent.h" bevat en de #ifndeftest false retourneert, springt de preprocessor naar de #endif, waardoor de tweede definitie van wordt vermeden struct foo. Het programma compileert correct.

Discussie

Verschillende naamgeving voor de bewaker macro kan worden gebruikt door verschillende programmeurs . Andere veelvoorkomende vormen van het bovenstaande voorbeeld zijn GRANDPARENT_INCLUDED, CREATORSNAME_YYYYMMDD_HHMMSS(waarbij de juiste tijdsinformatie is vervangen), en namen die zijn gegenereerd op basis van een UUID . ( Namen die beginnen met één onderstrepingsteken en een hoofdletter of namen met een dubbel onderstrepingsteken, zoals _GRANDPARENT__Hen __GRANDPARENT_H, zijn echter voorbehouden aan de taalimplementatie en mogen niet door de gebruiker worden gebruikt.)

Het is natuurlijk belangrijk om te voorkomen dat dezelfde include-guard-macronaam in verschillende headerbestanden wordt gedupliceerd, omdat het opnemen van de 1e zal voorkomen dat de 2e wordt opgenomen, wat leidt tot het verlies van alle declaraties, inline-definities of andere #includes in de 2e kop.

Moeilijkheden

Om ervoor te zorgen dat #include-bewakers correct werken, moet elke bewaker een andere preprocessor-macro testen en voorwaardelijk instellen. Daarom moet een project dat #include guards gebruikt een coherent naamgevingsschema uitwerken voor zijn include guards, en ervoor zorgen dat het schema niet conflicteert met dat van de headers van derden die het gebruikt, of met de namen van globaal zichtbare macro's.

Om deze reden bieden de meeste C- en C++-implementaties een niet-standaardrichtlijn #pragma once. Deze instructie, die bovenaan een headerbestand wordt ingevoegd, zorgt ervoor dat het bestand slechts één keer wordt opgenomen. De Objective-C- taal (die een superset van C is) introduceerde een #importrichtlijn, die precies werkt als #include, behalve dat het elk bestand slechts één keer bevat, waardoor de noodzaak voor #include-wachters overbodig wordt.

Zie ook

Referenties

  1. ^ C++ standaard (ISO/IEC 14882) sectie 17.4.3.1.2/1
  2. ^ C-norm (ISO/IEC 9899) paragraaf 7.1.3/1.
  3. ^ "Doelstelling C: Klassen definiëren" . ontwikkelaar.apple.com . 2014-09-17 . Ontvangen 2018-10-03 .

Externe links