Wie verwende ich extern, um Variablen zwischen Quelldateien freizugeben?

987

Ich weiß, dass globale Variablen in C manchmal das externSchlüsselwort haben. Was ist eine externVariable? Wie ist die Erklärung? Was ist ihr Umfang?

Dies hängt mit dem Teilen von Variablen zwischen Quelldateien zusammen, aber wie funktioniert das genau? Wo verwende ich extern?

Lundin
quelle

Antworten:

1751

Die Verwendung externist nur relevant, wenn das von Ihnen erstellte Programm aus mehreren miteinander verknüpften Quelldateien besteht, wobei einige der beispielsweise in der Quelldatei definierten Variablen file1.cin anderen Quelldateien referenziert werden müssen, z file2.c.

Es ist wichtig, den Unterschied zwischen dem Definieren einer Variablen und dem Deklarieren einer Variablen zu verstehen :

  • Eine Variable wird deklariert, wenn der Compiler darüber informiert wird, dass eine Variable vorhanden ist (und dies ist ihr Typ). Zu diesem Zeitpunkt wird der Speicher für die Variable nicht zugewiesen.

  • Eine Variable wird definiert, wenn der Compiler den Speicher für die Variable zuweist.

Sie können eine Variable mehrmals deklarieren (obwohl einmal ausreichend ist). Sie können es innerhalb eines bestimmten Bereichs nur einmal definieren. Eine Variablendefinition ist auch eine Deklaration, aber nicht alle Variablendeklarationen sind Definitionen.

Beste Möglichkeit, globale Variablen zu deklarieren und zu definieren

Die saubere und zuverlässige Methode zum extern Deklarieren und Definieren globaler Variablen besteht darin, eine Header-Datei zu verwenden, die eine Deklaration der Variablen enthält.

Der Header ist in der einen Quelldatei enthalten, die die Variable definiert, sowie in allen Quelldateien, die auf die Variable verweisen. Für jedes Programm definiert eine Quelldatei (und nur eine Quelldatei) die Variable. Ebenso sollte eine Header-Datei (und nur eine Header-Datei) die Variable deklarieren. Die Header-Datei ist entscheidend; Es ermöglicht die Gegenprüfung zwischen unabhängigen TUs (Übersetzungseinheiten - Think Source-Dateien) und stellt die Konsistenz sicher.

Obwohl es andere Möglichkeiten gibt, ist diese Methode einfach und zuverlässig. Es wird gezeigt , durch file3.h, file1.cund file2.c:

file3.h

extern int global_variable;  /* Declaration of the variable */

file1.c

#include "file3.h"  /* Declaration made available here */
#include "prog1.h"  /* Function declarations */

/* Variable defined here */
int global_variable = 37;    /* Definition checked against declaration */

int increment(void) { return global_variable++; }

file2.c

#include "file3.h"
#include "prog1.h"
#include <stdio.h>

void use_it(void)
{
    printf("Global variable: %d\n", global_variable++);
}

Dies ist der beste Weg, um globale Variablen zu deklarieren und zu definieren.


Die nächsten beiden Dateien vervollständigen die Quelle für prog1:

Die gezeigten vollständigen Programme verwenden Funktionen, sodass sich Funktionsdeklarationen eingeschlichen haben. Sowohl C99 als auch C11 erfordern, dass Funktionen deklariert oder definiert werden, bevor sie verwendet werden (während C90 dies aus guten Gründen nicht tat). Ich verwende das Schlüsselwort externvor Funktionsdeklarationen in Headern, um die Konsistenz zu gewährleisten - damit es mit dem externvor Variablendeklarationen in Headern übereinstimmt. Viele Leute bevorzugen es, nicht externvor Funktionsdeklarationen zu verwenden; Dem Compiler ist das egal - und letztendlich auch nicht, solange Sie konsistent sind, zumindest innerhalb einer Quelldatei.

prog1.h

extern void use_it(void);
extern int increment(void);

prog1.c

#include "file3.h"
#include "prog1.h"
#include <stdio.h>

int main(void)
{
    use_it();
    global_variable += 19;
    use_it();
    printf("Increment: %d\n", increment());
    return 0;
}
  • prog1Anwendungen prog1.c, file1.c, file2.c, file3.hund prog1.h.

Die Datei prog1.mkist nur ein Makefile für prog1. Es wird mit den meisten Versionen von makeseit etwa der Jahrtausendwende produziert funktionieren . Es ist nicht speziell an GNU Make gebunden.

prog1.mk

# Minimal makefile for prog1

PROGRAM = prog1
FILES.c = prog1.c file1.c file2.c
FILES.h = prog1.h file3.h
FILES.o = ${FILES.c:.c=.o}

CC      = gcc
SFLAGS  = -std=c11
GFLAGS  = -g
OFLAGS  = -O3
WFLAG1  = -Wall
WFLAG2  = -Wextra
WFLAG3  = -Werror
WFLAG4  = -Wstrict-prototypes
WFLAG5  = -Wmissing-prototypes
WFLAGS  = ${WFLAG1} ${WFLAG2} ${WFLAG3} ${WFLAG4} ${WFLAG5}
UFLAGS  = # Set on command line only

CFLAGS  = ${SFLAGS} ${GFLAGS} ${OFLAGS} ${WFLAGS} ${UFLAGS}
LDFLAGS =
LDLIBS  =

all:    ${PROGRAM}

${PROGRAM}: ${FILES.o}
    ${CC} -o $@ ${CFLAGS} ${FILES.o} ${LDFLAGS} ${LDLIBS}

prog1.o: ${FILES.h}
file1.o: ${FILES.h}
file2.o: ${FILES.h}

# If it exists, prog1.dSYM is a directory on macOS DEBRIS = a.out core *~ *.dSYM RM_FR = rm -fr 

clean:
    ${RM_FR} ${FILES.o} ${PROGRAM} ${DEBRIS}

Richtlinien

Regeln, die nur von Experten und nur aus gutem Grund verletzt werden dürfen:

  • Eine Header-Datei enthält nur externDeklarationen von Variablen - niemals staticoder nicht qualifizierte Variablendefinitionen.

  • Für eine bestimmte Variable wird sie nur von einer Header-Datei deklariert (SPOT - Single Point of Truth).

  • Eine Quelldatei enthält niemals externDeklarationen von Variablen - Quelldateien enthalten immer den (einzigen) Header, der sie deklariert.

  • Für eine bestimmte Variable definiert genau eine Quelldatei die Variable und initialisiert sie vorzugsweise auch. (Obwohl es nicht erforderlich ist, explizit auf Null zu initialisieren, schadet dies nicht und kann etwas Gutes bewirken, da es in einem Programm nur eine initialisierte Definition einer bestimmten globalen Variablen geben kann.)

  • Die Quelldatei, die die Variable definiert, enthält auch den Header, um sicherzustellen, dass die Definition und die Deklaration konsistent sind.

  • Eine Funktion sollte niemals eine Variable mit deklarieren müssen extern.

  • Vermeiden Sie nach Möglichkeit globale Variablen - verwenden Sie stattdessen Funktionen.

Der Quellcode und der Text dieser Antwort sind in meinem SOQ- Repository (Stack Overflow Questions) auf GitHub im Unterverzeichnis src / so-0143-3204 verfügbar .

Wenn Sie kein erfahrener C-Programmierer sind, können (und sollten) Sie hier aufhören zu lesen.

Nicht so gute Möglichkeit, globale Variablen zu definieren

Mit einigen (in der Tat vielen) C-Compilern können Sie auch mit einer sogenannten "allgemeinen" Definition einer Variablen davonkommen. 'Common' bezieht sich hier auf eine Technik, die in Fortran zum Teilen von Variablen zwischen Quelldateien unter Verwendung eines (möglicherweise benannten) COMMON-Blocks verwendet wird. Was hier passiert, ist, dass jede einer Reihe von Dateien eine vorläufige Definition der Variablen liefert. Solange nicht mehr als eine Datei eine initialisierte Definition bereitstellt, teilen sich die verschiedenen Dateien eine gemeinsame Definition der Variablen:

file10.c

#include "prog2.h"

long l; /* Do not do this in portable code */ 

void inc(void) { l++; }

file11.c

#include "prog2.h"

long l; /* Do not do this in portable code */ 

void dec(void) { l--; }

file12.c

#include "prog2.h"
#include <stdio.h>

long l = 9; /* Do not do this in portable code */ 

void put(void) { printf("l = %ld\n", l); }

Diese Technik entspricht nicht dem Buchstaben des C-Standards und der 'One Definition Rule' - es ist offiziell undefiniertes Verhalten:

J.2 Undefiniertes Verhalten

Ein Bezeichner mit externer Verknüpfung wird verwendet, aber im Programm existiert nicht genau eine externe Definition für den Bezeichner, oder der Bezeichner wird nicht verwendet, und es existieren mehrere externe Definitionen für den Bezeichner (6.9).

§6.9 Externe Definitionen ¶5

Eine externe Definition ist eine externe Deklaration, die auch eine Definition einer Funktion (außer einer Inline-Definition) oder eines Objekts ist. Wenn ein mit externer Verknüpfung deklarierter Bezeichner in einem Ausdruck verwendet wird (außer als Teil des Operanden eines sizeofoder eines _AlignofOperators, dessen Ergebnis eine Ganzzahlkonstante ist), muss irgendwo im gesamten Programm genau eine externe Definition für den Bezeichner vorhanden sein. Andernfalls darf es nicht mehr als eine geben. 161)

161) Wenn also ein mit externer Verknüpfung deklarierter Bezeichner nicht in einem Ausdruck verwendet wird, muss keine externe Definition dafür vorhanden sein.

Der C-Standard listet ihn jedoch auch im informativen Anhang J als eine der gemeinsamen Erweiterungen auf .

J.5.11 Mehrere externe Definitionen

Es kann mehr als eine externe Definition für die Kennung eines Objekts geben, mit oder ohne die explizite Verwendung des Schlüsselworts extern. Wenn die Definitionen nicht übereinstimmen oder mehr als eine initialisiert wird, ist das Verhalten undefiniert (6.9.2).

Da diese Technik nicht immer unterstützt wird, sollten Sie sie am besten vermeiden, insbesondere wenn Ihr Code portabel sein muss . Mit dieser Technik kann es auch zu unbeabsichtigten Punns kommen.

Wenn eine der oben genannten Dateien lals doublestatt statt als deklariert longwürde, würden die typunsicheren Linker von C die Nichtübereinstimmung wahrscheinlich nicht erkennen. Wenn Sie sich auf einem 64-Bit-Computer befinden longund doublenicht einmal eine Warnung erhalten; Auf einem Computer mit 32-Bit longund 64-Bit erhalten doubleSie wahrscheinlich eine Warnung zu den verschiedenen Größen - der Linker würde die größte Größe verwenden, genau wie ein Fortran-Programm die größte Größe aller gängigen Blöcke annehmen würde.

Beachten Sie, dass GCC 10.1.0, das am 07.05.2020 veröffentlicht wurde, die zu verwendenden Standardkompilierungsoptionen ändert. Dies -fno-commonbedeutet, dass der obige Code standardmäßig nicht mehr verknüpft wird, es sei denn, Sie überschreiben den Standard mit -fcommon(oder verwenden Attribute usw.). siehe den Link).


Die nächsten beiden Dateien vervollständigen die Quelle für prog2:

prog2.h

extern void dec(void);
extern void put(void);
extern void inc(void);

prog2.c

#include "prog2.h"
#include <stdio.h>

int main(void)
{
    inc();
    put();
    dec();
    put();
    dec();
    put();
}
  • prog2Anwendungen prog2.c, file10.c, file11.c, file12.c, prog2.h.

Warnung

Wie in den Kommentaren hier erwähnt und in meiner Antwort auf eine ähnliche Frage angegeben , führt die Verwendung mehrerer Definitionen für eine globale Variable zu undefiniertem Verhalten (J.2; §6.9). Dies ist die Art und Weise, wie der Standard sagt, dass "alles passieren kann". Eines der Dinge, die passieren können, ist, dass sich das Programm so verhält, wie Sie es erwarten. und J.5.11 sagt ungefähr: "Sie könnten öfter Glück haben, als Sie verdienen". Ein Programm, das sich auf mehrere Definitionen einer externen Variablen stützt - mit oder ohne das explizite Schlüsselwort 'extern' - ist jedoch kein streng konformes Programm und funktioniert garantiert nicht überall. Gleichwertig: Es enthält einen Fehler, der sich möglicherweise zeigt oder nicht.

Verstöße gegen die Richtlinien

Es gibt natürlich viele Möglichkeiten, wie diese Richtlinien gebrochen werden können. Gelegentlich kann es einen guten Grund geben, gegen die Richtlinien zu verstoßen, aber solche Anlässe sind äußerst ungewöhnlich.

fehlerhafter_header.h

c int some_var; /* Do not do this in a header!!! */

Hinweis 1: Wenn der Header die Variable ohne das externSchlüsselwort definiert, erstellt jede Datei, die den Header enthält, eine vorläufige Definition der Variablen. Wie bereits erwähnt, funktioniert dies häufig, aber der C-Standard garantiert nicht, dass er funktioniert.

gebrochener_header.h

c int some_var = 13; /* Only one source file in a program can use this */

Hinweis 2: Wenn der Header die Variable definiert und initialisiert, kann nur eine Quelldatei in einem bestimmten Programm den Header verwenden. Da Header hauptsächlich zum Teilen von Informationen dienen, ist es etwas albern, solche zu erstellen, die nur einmal verwendet werden können.

selten_korrekt.h

c static int hidden_global = 3; /* Each source file gets its own copy */

Hinweis 3: Wenn der Header eine statische Variable definiert (mit oder ohne Initialisierung), erhält jede Quelldatei eine eigene private Version der 'globalen' Variablen.

Wenn die Variable beispielsweise tatsächlich ein komplexes Array ist, kann dies zu einer extremen Duplizierung des Codes führen. Es kann gelegentlich ein vernünftiger Weg sein, einen Effekt zu erzielen, aber das ist sehr ungewöhnlich.


Zusammenfassung

Verwenden Sie die Header-Technik, die ich zuerst gezeigt habe. Es funktioniert zuverlässig und überall. Beachten Sie insbesondere, dass der Header, der das deklariert, global_variablein jeder Datei enthalten ist, die es verwendet - einschließlich derjenigen, die es definiert. Dies stellt sicher, dass alles selbstkonsistent ist.

Ähnliche Bedenken ergeben sich bei der Deklaration und Definition von Funktionen - es gelten analoge Regeln. Da es sich jedoch speziell um Variablen handelte, habe ich die Antwort nur auf Variablen beibehalten.

Ende der ursprünglichen Antwort

Wenn Sie kein erfahrener C-Programmierer sind, sollten Sie hier wahrscheinlich aufhören zu lesen.


Späte Hauptaddition

Vermeiden von Code-Duplikationen

Ein Problem, das manchmal (und zu Recht) in Bezug auf den hier beschriebenen Mechanismus "Deklarationen in Headern, Definitionen in der Quelle" geäußert wird, besteht darin, dass zwei Dateien synchronisiert werden müssen - der Header und die Quelle. Darauf folgt normalerweise die Beobachtung, dass ein Makro verwendet werden kann, damit der Header die doppelte Aufgabe erfüllt - normalerweise werden die Variablen deklariert. Wenn jedoch ein bestimmtes Makro festgelegt wird, bevor der Header enthalten ist, werden stattdessen die Variablen definiert.

Ein weiteres Problem kann sein, dass die Variablen in einem von mehreren Hauptprogrammen definiert werden müssen. Dies ist normalerweise ein falsches Anliegen. Sie können einfach eine C-Quelldatei einführen, um die Variablen zu definieren und die mit jedem der Programme erstellte Objektdatei zu verknüpfen.

Ein typisches Schema funktioniert folgendermaßen unter Verwendung der ursprünglichen globalen Variablen, dargestellt in file3.h:

file3a.h

#ifdef DEFINE_VARIABLES
#define EXTERN /* nothing */
#else
#define EXTERN extern
#endif /* DEFINE_VARIABLES */

EXTERN int global_variable;

file1a.c

#define DEFINE_VARIABLES
#include "file3a.h"  /* Variable defined - but not initialized */
#include "prog3.h"

int increment(void) { return global_variable++; }

file2a.c

#include "file3a.h"
#include "prog3.h"
#include <stdio.h>

void use_it(void)
{
    printf("Global variable: %d\n", global_variable++);
}

Die nächsten beiden Dateien vervollständigen die Quelle für prog3:

prog3.h

extern void use_it(void);
extern int increment(void);

prog3.c

#include "file3a.h"
#include "prog3.h"
#include <stdio.h>

int main(void)
{
    use_it();
    global_variable += 19;
    use_it();
    printf("Increment: %d\n", increment());
    return 0;
}
  • prog3Anwendungen prog3.c, file1a.c, file2a.c, file3a.h, prog3.h.

Variable Initialisierung

Das gezeigte Problem bei diesem Schema besteht darin, dass es keine Initialisierung der globalen Variablen vorsieht. Mit C99 oder C11 und variablen Argumentlisten für Makros können Sie ein Makro definieren, das auch die Initialisierung unterstützt. (Mit C89 und ohne Unterstützung für Listen mit variablen Argumenten in Makros gibt es keine einfache Möglichkeit, mit beliebig langen Initialisierern umzugehen.)

file3b.h

#ifdef DEFINE_VARIABLES
#define EXTERN                  /* nothing */
#define INITIALIZER(...)        = __VA_ARGS__
#else
#define EXTERN                  extern
#define INITIALIZER(...)        /* nothing */
#endif /* DEFINE_VARIABLES */

EXTERN int global_variable INITIALIZER(37);
EXTERN struct { int a; int b; } oddball_struct INITIALIZER({ 41, 43 });

Inhalt #ifund #elseBlöcke umkehren , Fehler beheben, der von Denis Kniazhev identifiziert wurde

file1b.c

#define DEFINE_VARIABLES
#include "file3b.h"  /* Variables now defined and initialized */
#include "prog4.h"

int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }

file2b.c

#include "file3b.h"
#include "prog4.h"
#include <stdio.h>

void use_them(void)
{
    printf("Global variable: %d\n", global_variable++);
    oddball_struct.a += global_variable;
    oddball_struct.b -= global_variable / 2;
}

Natürlich ist der Code für die Oddball-Struktur nicht das, was Sie normalerweise schreiben würden, aber er veranschaulicht den Punkt. Das erste Argument für den zweiten Aufruf von INITIALIZERis { 41und das verbleibende Argument (in diesem Beispiel Singular) ist 43 }. Ohne C99 oder ähnliche Unterstützung für variable Argumentlisten für Makros sind Initialisierer, die Kommas enthalten müssen, sehr problematisch.

Richtiger Header file3b.henthalten (anstelle von fileba.h) pro Denis Kniazhev


Die nächsten beiden Dateien vervollständigen die Quelle für prog4:

prog4.h

extern int increment(void);
extern int oddball_value(void);
extern void use_them(void);

prog4.c

#include "file3b.h"
#include "prog4.h"
#include <stdio.h>

int main(void)
{
    use_them();
    global_variable += 19;
    use_them();
    printf("Increment: %d\n", increment());
    printf("Oddball:   %d\n", oddball_value());
    return 0;
}
  • prog4Anwendungen prog4.c, file1b.c, file2b.c, prog4.h, file3b.h.

Header Guards

Jeder Header sollte vor erneuter Einschließung geschützt werden, damit Typdefinitionen (Enum-, Struktur- oder Unionstypen oder Typedefs im Allgemeinen) keine Probleme verursachen. Die Standardtechnik besteht darin, den Kopf des Headers in einen Header-Schutz zu wickeln, wie z.

#ifndef FILE3B_H_INCLUDED
#define FILE3B_H_INCLUDED

...contents of header...

#endif /* FILE3B_H_INCLUDED */

Der Header kann zweimal indirekt enthalten sein. Wenn beispielsweise file4b.hIncludes file3b.hfür eine Typdefinition enthalten sind, die nicht angezeigt wird und file1b.csowohl den Header file4b.hals auch verwenden muss file3b.h, müssen Sie einige schwierigere Probleme lösen. Natürlich können Sie die Header-Liste so überarbeiten, dass sie nur enthält file4b.h. Möglicherweise sind Sie sich der internen Abhängigkeiten jedoch nicht bewusst - und der Code sollte im Idealfall weiterhin funktionieren.

Außerdem wird es schwierig, weil Sie möglicherweise file4b.hvor dem Einfügen einschließen file3b.h, um die Definitionen zu generieren, aber die normalen Header- file3b.hSchutzfunktionen würden verhindern, dass der Header wieder eingeschlossen wird.

Sie müssen also den Hauptteil von file3b.hhöchstens einmal für Deklarationen und höchstens einmal für Definitionen einschließen, benötigen jedoch möglicherweise beide in einer einzigen Übersetzungseinheit (TU - eine Kombination aus einer Quelldatei und den verwendeten Headern).

Mehrfacheinschluss mit Variablendefinitionen

Dies kann jedoch unter nicht zu unangemessenen Bedingungen erfolgen. Lassen Sie uns einen neuen Satz von Dateinamen einführen:

  • external.h für die EXTERN-Makrodefinitionen usw.

  • file1c.hTypen zu definieren (insbesondere struct oddballden Typ von oddball_struct).

  • file2c.h um die globalen Variablen zu definieren oder zu deklarieren.

  • file3c.c welches die globalen Variablen definiert.

  • file4c.c die einfach die globalen Variablen verwendet.

  • file5c.c Dies zeigt, dass Sie die globalen Variablen deklarieren und dann definieren können.

  • file6c.c Dies zeigt, dass Sie die globalen Variablen definieren und dann (versuchen) können.

In diesen Beispielen file5c.cund file6c.cdirekt den Header file2c.hmehrmals einschließen , aber das ist der einfachste Weg, um zu zeigen, dass der Mechanismus funktioniert. Dies bedeutet, dass der Header auch sicher wäre, wenn er indirekt zweimal enthalten wäre.

Die Einschränkungen dafür sind:

  1. Der Header, der die globalen Variablen definiert oder deklariert, definiert möglicherweise selbst keine Typen.

  2. Unmittelbar bevor Sie einen Header einfügen, der Variablen definieren soll, definieren Sie das Makro DEFINE_VARIABLES.

  3. Der Header, der die Variablen definiert oder deklariert, hat den Inhalt stilisiert.

external.h


#ifdef DEFINE_VARIABLES
#define EXTERN              /* nothing */
#define INITIALIZE(...)     = __VA_ARGS__
#else
#define EXTERN              extern
#define INITIALIZE(...)     /* nothing */
#endif /* DEFINE_VARIABLES */

file1c.h

#ifndef FILE1C_H_INCLUDED
#define FILE1C_H_INCLUDED

struct oddball
{
    int a;
    int b;
};

extern void use_them(void);
extern int increment(void);
extern int oddball_value(void);

#endif /* FILE1C_H_INCLUDED */

file2c.h


/* Standard prologue */
#if defined(DEFINE_VARIABLES) && !defined(FILE2C_H_DEFINITIONS)
#undef FILE2C_H_INCLUDED
#endif

#ifndef FILE2C_H_INCLUDED
#define FILE2C_H_INCLUDED

#include "external.h"   /* Support macros EXTERN, INITIALIZE */
#include "file1c.h"     /* Type definition for struct oddball */

#if !defined(DEFINE_VARIABLES) || !defined(FILE2C_H_DEFINITIONS)

/* Global variable declarations / definitions */
EXTERN int global_variable INITIALIZE(37);
EXTERN struct oddball oddball_struct INITIALIZE({ 41, 43 });

#endif /* !DEFINE_VARIABLES || !FILE2C_H_DEFINITIONS */

/* Standard epilogue */
#ifdef DEFINE_VARIABLES
#define FILE2C_H_DEFINITIONS
#endif /* DEFINE_VARIABLES */

#endif /* FILE2C_H_INCLUDED */

file3c.c

#define DEFINE_VARIABLES
#include "file2c.h"  /* Variables now defined and initialized */

int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }

file4c.c

#include "file2c.h"
#include <stdio.h>

void use_them(void)
{
    printf("Global variable: %d\n", global_variable++);
    oddball_struct.a += global_variable;
    oddball_struct.b -= global_variable / 2;
}

file5c.c


#include "file2c.h"     /* Declare variables */

#define DEFINE_VARIABLES
#include "file2c.h"  /* Variables now defined and initialized */

int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }

file6c.c


#define DEFINE_VARIABLES
#include "file2c.h"     /* Variables now defined and initialized */

#include "file2c.h"     /* Declare variables */

int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }

Die nächste Quelldatei vervollständigt die Quelle (stellt ein Hauptprogramm bereit) für prog5 , prog6und prog7:

prog5.c

#include "file2c.h"
#include <stdio.h>

int main(void)
{
    use_them();
    global_variable += 19;
    use_them();
    printf("Increment: %d\n", increment());
    printf("Oddball:   %d\n", oddball_value());
    return 0;
}
  • prog5Anwendungen prog5.c, file3c.c, file4c.c, file1c.h, file2c.h,external.h.

  • prog6Anwendungen prog5.c, file5c.c, file4c.c, file1c.h, file2c.h,external.h.

  • prog7Anwendungen prog5.c, file6c.c, file4c.c, file1c.h, file2c.h, external.h.


Dieses Schema vermeidet die meisten Probleme. Sie stoßen nur dann auf ein Problem, wenn ein Header Variablen definiert (zfile2c.h ), anderen Header ( ) enthalten ist file7c.h, der Variablen definiert. Es gibt keinen einfachen Weg, um das zu umgehen, außer "mach es nicht".

Sie können das Problem teilweise umgehen, indem Sie es überarbeiten file2c.h in file2d.h:

file2d.h

/* Standard prologue */
#if defined(DEFINE_VARIABLES) && !defined(FILE2D_H_DEFINITIONS)
#undef FILE2D_H_INCLUDED
#endif

#ifndef FILE2D_H_INCLUDED
#define FILE2D_H_INCLUDED

#include "external.h"   /* Support macros EXTERN, INITIALIZE */
#include "file1c.h"     /* Type definition for struct oddball */

#if !defined(DEFINE_VARIABLES) || !defined(FILE2D_H_DEFINITIONS)

/* Global variable declarations / definitions */
EXTERN int global_variable INITIALIZE(37);
EXTERN struct oddball oddball_struct INITIALIZE({ 41, 43 });

#endif /* !DEFINE_VARIABLES || !FILE2D_H_DEFINITIONS */

/* Standard epilogue */
#ifdef DEFINE_VARIABLES
#define FILE2D_H_DEFINITIONS
#undef DEFINE_VARIABLES
#endif /* DEFINE_VARIABLES */

#endif /* FILE2D_H_INCLUDED */

Das Problem wird "sollte der Header enthalten #undef DEFINE_VARIABLES?" Wenn Sie dies in der Kopfzeile weglassen und einen definierenden Aufruf mit #defineund umschließen#undef :

#define DEFINE_VARIABLES
#include "file2c.h"
#undef DEFINE_VARIABLES

im Quellcode (daher ändern die Header niemals den Wert von DEFINE_VARIABLES ), sollten Sie sauber sein. Es ist nur ein Ärgernis, sich daran erinnern zu müssen, die zusätzliche Zeile zu schreiben. Eine Alternative könnte sein:

#define HEADER_DEFINING_VARIABLES "file2c.h"
#include "externdef.h"

externdef.h


#if defined(HEADER_DEFINING_VARIABLES)
#define DEFINE_VARIABLES
#include HEADER_DEFINING_VARIABLES
#undef DEFINE_VARIABLES
#undef HEADER_DEFINING_VARIABLES
#endif /* HEADER_DEFINING_VARIABLES */

Dies wird ein bisschen verworren, scheint aber sicher zu sein (mit dem file2d.h, mit nein#undef DEFINE_VARIABLES in file2d.h).

file7c.c

/* Declare variables */
#include "file2d.h"

/* Define variables */
#define HEADER_DEFINING_VARIABLES "file2d.h"
#include "externdef.h"

/* Declare variables - again */
#include "file2d.h"

/* Define variables - again */
#define HEADER_DEFINING_VARIABLES "file2d.h"
#include "externdef.h"

int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }

file8c.h

/* Standard prologue */
#if defined(DEFINE_VARIABLES) && !defined(FILE8C_H_DEFINITIONS)
#undef FILE8C_H_INCLUDED
#endif

#ifndef FILE8C_H_INCLUDED
#define FILE8C_H_INCLUDED

#include "external.h"   /* Support macros EXTERN, INITIALIZE */
#include "file2d.h"     /* struct oddball */

#if !defined(DEFINE_VARIABLES) || !defined(FILE8C_H_DEFINITIONS)

/* Global variable declarations / definitions */
EXTERN struct oddball another INITIALIZE({ 14, 34 });

#endif /* !DEFINE_VARIABLES || !FILE8C_H_DEFINITIONS */

/* Standard epilogue */
#ifdef DEFINE_VARIABLES
#define FILE8C_H_DEFINITIONS
#endif /* DEFINE_VARIABLES */

#endif /* FILE8C_H_INCLUDED */

file8c.c

/* Define variables */
#define HEADER_DEFINING_VARIABLES "file2d.h"
#include "externdef.h"

/* Define variables */
#define HEADER_DEFINING_VARIABLES "file8c.h"
#include "externdef.h"

int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }

Die nächsten beiden Dateien vervollständigen die Quelle für prog8und prog9:

prog8.c

#include "file2d.h"
#include <stdio.h>

int main(void)
{
    use_them();
    global_variable += 19;
    use_them();
    printf("Increment: %d\n", increment());
    printf("Oddball:   %d\n", oddball_value());
    return 0;
}

file9c.c

#include "file2d.h"
#include <stdio.h>

void use_them(void)
{
    printf("Global variable: %d\n", global_variable++);
    oddball_struct.a += global_variable;
    oddball_struct.b -= global_variable / 2;
}
  • prog8Anwendungen prog8.c, file7c.c, file9c.c.

  • prog9Anwendungen prog8.c, file8c.c, file9c.c.


Es ist jedoch relativ unwahrscheinlich, dass die Probleme in der Praxis auftreten, insbesondere wenn Sie die Standardempfehlung befolgen

Vermeiden Sie globale Variablen


Vermisst diese Ausstellung etwas?

Geständnis : Das hier beschriebene Schema "Vermeiden von doppeltem Code" wurde entwickelt, da das Problem einen Code betrifft, an dem ich arbeite (den ich aber nicht besitze), und das mit dem im ersten Teil der Antwort beschriebenen Schema ein Problem darstellt. Das ursprüngliche Schema lässt Ihnen jedoch nur zwei Stellen zum Ändern übrig, um Variablendefinitionen und Deklarationen synchron zu halten. Dies ist ein großer Fortschritt gegenüber der Verteilung externer Variablendeklarationen über die gesamte Codebasis (was wirklich wichtig ist, wenn insgesamt Tausende von Dateien vorhanden sind). . Der Code in den Dateien mit den Namen fileNc.[ch](plus external.hund externdef.h) zeigt jedoch, dass er zum Laufen gebracht werden kann. Es ist natürlich nicht schwer, ein Header-Generator-Skript zu erstellen, mit dem Sie die standardisierte Vorlage für eine Variable erhalten, die die Header-Datei definiert und deklariert.

NB Dies sind Spielzeugprogramme mit kaum genug Code, um sie geringfügig interessant zu machen. Es gibt Wiederholungen in den Beispielen, die entfernt werden könnten, aber nicht, um die pädagogische Erklärung zu vereinfachen. (Zum Beispiel: Der Unterschied zwischen prog5.cund prog8.cist der Name eines der enthaltenen Header. Es wäre möglich, den Code so zu reorganisieren, dass die main()Funktion nicht wiederholt wird, aber mehr verbirgt, als sich herausstellt.)

Jonathan Leffler
quelle
3
@litb: siehe Anhang J.5.11 für die gemeinsame Definition - es ist eine gemeinsame Erweiterung.
Jonathan Leffler
3
@litb: und ich stimme zu, dass es vermieden werden sollte - deshalb steht es im Abschnitt "Nicht so gute Möglichkeit, globale Variablen zu definieren".
Jonathan Leffler
3
Es ist zwar eine häufige Erweiterung, aber es ist ein undefiniertes Verhalten, auf das sich ein Programm verlassen kann. Mir war nur nicht klar, ob Sie sagten, dass dies nach Cs eigenen Regeln erlaubt ist. Jetzt sehe ich, dass Sie sagen, es ist nur eine übliche Erweiterung und um dies zu vermeiden, wenn Sie Ihren Code portabel benötigen. So kann ich Sie ohne Zweifel positiv bewerten. Wirklich tolle Antwort IMHO :)
Johannes Schaub - litb
19
Wenn Sie oben anhalten, bleiben einfache Dinge einfach. Wie Sie weiter unten lesen, werden mehr Nuancen, Komplikationen und Details behandelt. Ich habe gerade zwei "frühe Haltepunkte" für weniger erfahrene C-Programmierer hinzugefügt - oder C-Programmierer, die das Thema bereits kennen. Sie müssen nicht alles lesen, wenn Sie die Antwort bereits kennen (aber lassen Sie mich wissen, wenn Sie einen technischen Fehler finden).
Jonathan Leffler
4
@supercat: Mir fällt ein, dass Sie C99-Array-Literale verwenden können, um einen Aufzählungswert für die Array-Größe abzurufen, beispielhaft dargestellt durch ( foo.h): #define FOO_INITIALIZER { 1, 2, 3, 4, 5 }Definieren des Initialisierers für das Array, enum { FOO_SIZE = sizeof((int [])FOO_INITIALIZER) / sizeof(((int [])FOO_INITIALIZER)[0]) };Abrufen der Größe des Arrays und extern int foo[];Deklarieren des Arrays . Natürlich sollte die Definition gerecht sein int foo[FOO_SIZE] = FOO_INITIALIZER;, obwohl die Größe nicht unbedingt in die Definition aufgenommen werden muss. Dadurch erhalten Sie eine ganzzahlige Konstante FOO_SIZE.
Jonathan Leffler
125

Eine externVariable ist eine Deklaration (danke an sbi für die Korrektur) einer Variablen, die in einer anderen Übersetzungseinheit definiert ist. Das heißt, der Speicher für die Variable wird in einer anderen Datei zugewiesen.

.cAngenommen, Sie haben zwei Dateien test1.cund test2.c. Wenn Sie definieren eine globale Variable int test1_var;in test1.cund Sie möchten , dass diese Variable für den Zugriff auf den test2.cSie verwenden haben extern int test1_var;in test2.c.

Komplette Probe:

$ cat test1.c 
int test1_var = 5;
$ cat test2.c
#include <stdio.h>

extern int test1_var;

int main(void) {
    printf("test1_var = %d\n", test1_var);
    return 0;
}
$ gcc test1.c test2.c -o test
$ ./test
test1_var = 5
Johannes Weiss
quelle
21
Es gibt keine "Pseudodefinitionen". Es ist eine Erklärung.
sbi
3
Wenn ich im obigen Beispiel das extern int test1_var;in ändere , int test1_var;wird der Linker (gcc 5.4.0) weiterhin übergeben. Also, wird externin diesem Fall wirklich gebraucht?
Radiohead
2
@radiohead: In meiner Antwort finden Sie die Information, dass das Löschen von externeine häufige Erweiterung ist, die häufig funktioniert - und speziell mit GCC funktioniert (aber GCC ist bei weitem nicht der einzige Compiler, der dies unterstützt; es ist auf Unix-Systemen weit verbreitet). Sie können in meiner Antwort nach "J.5.11" oder dem Abschnitt "Nicht so gut" suchen (ich weiß - es ist lang) und in dem Text in der Nähe, der dies erklärt (oder dies versucht).
Jonathan Leffler
Eine externe Deklaration muss sicherlich nicht in einer anderen Übersetzungseinheit definiert werden (und ist es normalerweise nicht). In der Tat können Deklaration und Definition ein und dasselbe sein.
Erinnern Sie sich an Monica
40

Extern ist das Schlüsselwort, mit dem Sie deklarieren, dass sich die Variable selbst in einer anderen Übersetzungseinheit befindet.

Sie können also entscheiden, eine Variable in einer Übersetzungseinheit zu verwenden und dann von einer anderen auf sie zuzugreifen. In der zweiten deklarieren Sie sie als extern und das Symbol wird vom Linker aufgelöst.

Wenn Sie es nicht als extern deklarieren, erhalten Sie zwei gleichnamige, aber überhaupt nicht verwandte Variablen sowie einen Fehler bei mehreren Definitionen der Variablen.

Arkaitz Jimenez
quelle
5
Mit anderen Worten, die Übersetzungseinheit, in der extern verwendet wird, kennt diese Variable, ihren Typ usw. und erlaubt daher dem Quellcode in der zugrunde liegenden Logik, sie zu verwenden, aber sie ordnet die Variable nicht zu, eine andere Übersetzungseinheit wird dies tun. Wenn beide Übersetzungseinheiten die Variable normal deklarieren würden, gäbe es effektiv zwei physische Speicherorte für die Variable mit den zugehörigen "falschen" Referenzen innerhalb des kompilierten Codes und der daraus resultierenden Mehrdeutigkeit für den Linker.
mjv
26

Ich stelle mir eine externe Variable gerne als Versprechen vor, das Sie dem Compiler geben.

Wenn der Compiler auf ein externes Objekt stößt, kann er nur seinen Typ herausfinden, nicht wo er "lebt", sodass er die Referenz nicht auflösen kann.

Sie sagen es: "Vertrauen Sie mir. Zum Zeitpunkt der Verknüpfung ist diese Referenz auflösbar."

Buggieboy
quelle
Im Allgemeinen ist eine Erklärung ein Versprechen, dass der Name zum Zeitpunkt der Verknüpfung in genau eine Definition aufgelöst werden kann. Ein extern deklariert eine Variable ohne Definition.
Lie Ryan
18

extern weist den Compiler an, Ihnen zu vertrauen, dass der Speicher für diese Variable an anderer Stelle deklariert ist, sodass er nicht versucht, Speicher zuzuweisen / zu überprüfen.

Daher können Sie eine Datei kompilieren, die auf eine externe Datei verweist. Sie können jedoch keine Verknüpfung herstellen, wenn dieser Speicher nicht irgendwo deklariert ist.

Nützlich für globale Variablen und Bibliotheken, aber gefährlich, da der Linker keine Typprüfung durchführt.

BenB
quelle
Der Speicher wird nicht deklariert. Weitere Informationen finden Sie in den Antworten auf diese Frage: stackoverflow.com/questions/1410563 .
sbi
15

Hinzufügen eines externWindungen eine variable Definition in eine variable Deklaration . In diesem Thread erfahren Sie, was der Unterschied zwischen einer Deklaration und einer Definition ist.

sbi
quelle
Welcher Unterschied zwischen int foound extern int foo(Dateibereich)? Beides ist eine Erklärung, nicht wahr?
@ user14284: Beide sind nur Deklarationen in dem Sinne, dass jede Definition auch eine Deklaration ist. Aber ich habe eine Erklärung dazu verlinkt. ("In diesem Thread erfahren Sie, was der Unterschied zwischen einer Deklaration und einer Definition ist.") Warum folgen Sie nicht einfach dem Link und lesen?
sbi
14
                 declare | define   | initialize |
                ----------------------------------

extern int a;    yes          no           no
-------------
int a = 2019;    yes          yes          yes
-------------
int a;           yes          yes          no
-------------

Durch die Deklaration wird kein Speicher zugewiesen (die Variable muss für die Speicherzuweisung definiert werden), die Definition jedoch. Dies ist nur eine weitere einfache Ansicht des externen Schlüsselworts, da die anderen Antworten wirklich großartig sind.

Lucian Nut
quelle
11

Die richtige Interpretation von extern ist, dass Sie dem Compiler etwas mitteilen. Sie teilen dem Compiler mit, dass die deklarierte Variable, obwohl sie gerade nicht vorhanden ist, vom Linker irgendwie gefunden wird (normalerweise in einem anderen Objekt (Datei)). Der Linker ist dann der Glückliche, der alles findet und zusammenstellt, unabhängig davon, ob Sie externe Erklärungen hatten oder nicht.

Alex Lockwood
quelle
8

In C erhält eine Variable in einer Datei, beispielsweise example.c, einen lokalen Bereich. Der Compiler erwartet, dass die Definition der Variablen in derselben Datei example.c liegt. Wenn sie diese nicht findet, wird ein Fehler ausgegeben. Eine Funktion hingegen hat standardmäßig einen globalen Gültigkeitsbereich. Daher müssen Sie dem Compiler gegenüber nicht explizit erwähnen, dass "Look Dude ... die Definition dieser Funktion finden Sie hier". Für eine Funktion mit der Datei, die ihre Deklaration enthält, reicht dies aus. (Die Datei, die Sie tatsächlich als Header-Datei bezeichnen). Betrachten Sie beispielsweise die folgenden 2 Dateien:
example.c

#include<stdio.h>
extern int a;
main(){
       printf("The value of a is <%d>\n",a);
}

Beispiel1.c

int a = 5;

Wenn Sie nun die beiden Dateien zusammen kompilieren, verwenden Sie die folgenden Befehle:

Schritt 1) ​​cc -o ex Beispiel.c Beispiel1.c Schritt 2) ./ ex

Sie erhalten folgende Ausgabe: Der Wert von a ist <5>

Phoenix225
quelle
8

Implementierung von GCC ELF Linux

Andere Antworten haben die Seite des Sprachgebrauchs behandelt. Schauen wir uns nun an, wie sie in dieser Implementierung implementiert wird.

Haupt c

#include <stdio.h>

int not_extern_int = 1;
extern int extern_int;

void main() {
    printf("%d\n", not_extern_int);
    printf("%d\n", extern_int);
}

Kompilieren und dekompilieren:

gcc -c main.c
readelf -s main.o

Die Ausgabe enthält:

Num:    Value          Size Type    Bind   Vis      Ndx Name
 9: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 not_extern_int
12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND extern_int

Im Kapitel "Symboltabelle" der System V ABI Update ELF-Spezifikation wird Folgendes erläutert:

SHN_UNDEF Dieser Abschnittstabellenindex bedeutet, dass das Symbol undefiniert ist. Wenn der Verknüpfungseditor diese Objektdatei mit einer anderen kombiniert, die das angegebene Symbol definiert, werden die Verweise dieser Datei auf das Symbol mit der tatsächlichen Definition verknüpft.

Dies ist im Grunde das Verhalten, das der C-Standard externVariablen gibt .

Von nun an ist es Aufgabe des Linkers, das endgültige Programm zu externerstellen , aber die Informationen wurden bereits aus dem Quellcode in die Objektdatei extrahiert.

Getestet auf GCC 4.8.

C ++ 17 Inline-Variablen

In C ++ 17 möchten Sie möglicherweise Inline-Variablen anstelle von externen Variablen verwenden, da diese einfach zu verwenden sind (können nur einmal im Header definiert werden) und leistungsfähiger sind (Unterstützung von constexpr). Siehe: Was bedeutet 'const static' in C und C ++?

Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功
quelle
3
Es ist nicht meine Ablehnung, also weiß ich es nicht. Ich werde jedoch eine Meinung abgeben. Obwohl Sie sich die Ausgabe von ansehen readelfoder nmhilfreich sein können, haben Sie weder die Grundlagen der Verwendung erläutert externnoch das erste Programm mit der tatsächlichen Definition abgeschlossen. Ihr Code wird nicht einmal verwendet notExtern. Es gibt auch ein Nomenklaturproblem: Obwohl notExternhier definiert und nicht mit deklariert extern, handelt es sich um eine externe Variable, auf die andere Quelldateien zugreifen können, wenn diese Übersetzungseinheiten eine geeignete Deklaration enthalten (was erforderlich wäre extern int notExtern;!).
Jonathan Leffler
1
@ JonathanLeffler danke für das Feedback! Die Standardempfehlungen für Verhalten und Verwendung wurden bereits in anderen Antworten angegeben. Daher habe ich beschlossen, die Implementierung ein wenig zu zeigen, da dies mir wirklich geholfen hat, zu verstehen, was vor sich geht. Nicht zu benutzen notExternwar hässlich, hat es behoben. Über die Nomenklatur, lassen Sie mich wissen, wenn Sie einen besseren Namen haben. Natürlich wäre das kein guter Name für ein tatsächliches Programm, aber ich denke, es passt gut zur didaktischen Rolle hier.
Ciro Santilli 法轮功 病毒 审查 六四 事件 2
Was ist mit den Namen, die global_deffür die hier definierte Variable und extern_reffür die in einem anderen Modul definierte Variable gelten? Hätten sie eine angemessen klare Symmetrie? Sie landen immer noch mit int extern_ref = 57;oder so etwas in der Datei, in der es definiert ist, daher ist der Name nicht ganz ideal, aber im Kontext der einzelnen Quelldatei ist es eine vernünftige Wahl. Nachdem extern int global_def;in einem Header ist nicht so sehr ein Problem, so scheint es mir. Natürlich ganz bei Ihnen.
Jonathan Leffler
7

Das Schlüsselwort extern wird mit der Variablen zur Identifizierung als globale Variable verwendet.

Es bedeutet auch, dass Sie die mit dem Schlüsselwort extern deklarierte Variable in jeder Datei verwenden können, obwohl sie in einer anderen Datei deklariert / definiert ist.

Anup
quelle
5

extern Ermöglicht einem Modul Ihres Programms den Zugriff auf eine globale Variable oder Funktion, die in einem anderen Modul Ihres Programms deklariert ist. Normalerweise haben Sie externe Variablen in Header-Dateien deklariert.

Wenn Sie nicht möchten, dass ein Programm auf Ihre Variablen oder Funktionen zugreift, verwenden Sie staticdiese Option, um dem Compiler mitzuteilen, dass diese Variable oder Funktion nicht außerhalb dieses Moduls verwendet werden kann.

loganaayahee
quelle
5

extern bedeutet einfach, dass eine Variable an anderer Stelle definiert ist (z. B. in einer anderen Datei).

Gerämie
quelle
4

Zunächst einmal wird das externSchlüsselwort nicht zum Definieren einer Variablen verwendet. Vielmehr wird es zum Deklarieren einer Variablen verwendet. Ich kann sagen, externist eine Speicherklasse, kein Datentyp.

externwird verwendet, um anderen C-Dateien oder externen Komponenten mitzuteilen, dass diese Variable bereits irgendwo definiert ist. Beispiel: Wenn Sie eine Bibliothek erstellen, müssen Sie die globale Variable nicht zwingend irgendwo in der Bibliothek selbst definieren. Die Bibliothek wird direkt kompiliert, überprüft jedoch beim Verknüpfen der Datei die Definition.

user1270846
quelle
3

externwird verwendet, damit eine first.cDatei vollen Zugriff auf einen globalen Parameter in einer anderen second.cDatei haben kann.

Das externkann in der first.cDatei oder in einer der Header-Dateien deklariert werden first.c.

Shoham
quelle
3
Beachten Sie, dass sich die externDeklaration in einem Header und nicht in befinden first.csollte. Wenn sich der Typ ändert, ändert sich auch die Deklaration. Außerdem sollte der Header, der die Variable deklariert, eingeschlossen werden, second.cum sicherzustellen, dass die Definition mit der Deklaration übereinstimmt. Die Deklaration in der Kopfzeile ist der Klebstoff, der alles zusammenhält. Dadurch können die Dateien separat kompiliert werden, es wird jedoch sichergestellt, dass sie eine konsistente Ansicht des Typs der globalen Variablen haben.
Jonathan Leffler
2

Mit xc8 müssen Sie vorsichtig sein, wenn Sie eine Variable in jeder Datei als denselben Typ deklarieren, da Sie fälschlicherweise etwas intin einer Datei und a deklarieren könnenchar mitreden können. Dies kann zur Beschädigung von Variablen führen.

Dieses Problem wurde vor etwa 15 Jahren in einem Mikrochip-Forum elegant gelöst / * Siehe "http: www.htsoft.com" / / "forum / all / showflat.php / Cat / 0 / Number / 18766 / an / 0 / page / 0 # 18766 "

Aber dieser Link scheint nicht mehr zu funktionieren ...

Also werde ich schnell versuchen, es zu erklären; Erstellen Sie eine Datei mit dem Namen global.h.

Darin erklären Sie Folgendes

#ifdef MAIN_C
#define GLOBAL
 /* #warning COMPILING MAIN.C */
#else
#define GLOBAL extern
#endif
GLOBAL unsigned char testing_mode; // example var used in several C files

Jetzt in der Datei main.c.

#define MAIN_C 1
#include "global.h"
#undef MAIN_C

Dies bedeutet, dass in main.c die Variable als deklariert wird unsigned char .

In anderen Dateien, die einfach global.h enthalten, wird es als extern für diese Datei deklariert .

extern unsigned char testing_mode;

Aber es wird korrekt als deklariert unsigned char .

Der alte Forumsbeitrag hat dies wahrscheinlich etwas klarer erklärt. Dies ist jedoch ein echtes Potenzial, gotchawenn Sie einen Compiler verwenden, mit dem Sie eine Variable in einer Datei deklarieren und in einer anderen Datei extern als einen anderen Typ deklarieren können. Die damit verbundenen Probleme sind, wenn Sie sagen, dass testing_mode in einer anderen Datei als int deklariert ist, würde dies bedeuten, dass es sich um eine 16-Bit-Variable handelt, und einen anderen Teil des RAM überschreiben, wodurch möglicherweise eine andere Variable beschädigt wird. Schwer zu debuggen!

user50619
quelle
0

Eine sehr kurze Lösung, mit der ich zulasse, dass eine Header-Datei die externe Referenz oder die tatsächliche Implementierung eines Objekts enthält. Die Datei, die das Objekt tatsächlich enthält, funktioniert nur #define GLOBAL_FOO_IMPLEMENTATION. Wenn ich dann ein neues Objekt zu dieser Datei hinzufüge, wird es in dieser Datei auch angezeigt, ohne dass ich die Definition kopieren und einfügen muss.

Ich verwende dieses Muster für mehrere Dateien. Um die Dinge so eigenständig wie möglich zu halten, verwende ich einfach das einzelne GLOBAL-Makro in jedem Header. Mein Header sieht so aus:

//file foo_globals.h
#pragma once  
#include "foo.h"  //contains definition of foo

#ifdef GLOBAL  
#undef GLOBAL  
#endif  

#ifdef GLOBAL_FOO_IMPLEMENTATION  
#define GLOBAL  
#else  
#define GLOBAL extern  
#endif  

GLOBAL Foo foo1;  
GLOBAL Foo foo2;


//file main.cpp
#define GLOBAL_FOO_IMPLEMENTATION
#include "foo_globals.h"

//file uses_extern_foo.cpp
#include "foo_globals.h
Muusbolla
quelle