Globale Variablen in Header-Datei

Lesezeit: 6 Minuten

Benutzeravatar von Bruce
Bruce

Ich habe 2 Module (.c-Dateien) und eine .h-Header-Datei:

Datei1.c:

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

int main()
{
    i = 100;
    printf("%d\n",i);
    foo();
    return 0;
}

Datei2.c

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

void foo()
{
    i = 10;
    printf("%d\n",i);
}

global.h

int i;
extern void foo()

Wenn ich gcc file1.c file2.c mache, funktioniert alles gut und ich bekomme die erwartete Ausgabe. Wenn ich jetzt die Variable ‘i’ in der Header-Datei initialisiere, um 0 zu sagen und erneut zu kompilieren, erhalte ich einen Linker-Fehler:

/tmp/cc0oj7yA.o:(.bss+0x0): multiple definition of `i'
/tmp/cckd7TTI.o:(.bss+0x0): first defined here

Wenn ich nur file1.c (Aufruf von foo() entfernen) mit der Initialisierung in der Header-Datei, dh gcc file1.c, kompiliere, funktioniert alles einwandfrei. Was ist los?

Es gibt 3 Szenarien, die Sie beschreiben:

  1. mit 2 .c Dateien und mit int i; in der Kopfzeile.
  2. Mit 2 .c Dateien und mit int i=100; im Header (oder irgendein anderer Wert; das spielt keine Rolle).
  3. Mit 1 .c Datei und mit int i=100; in der Kopfzeile.

Stellen Sie sich in jedem Szenario den Inhalt der Header-Datei vor, die in die eingefügt wird .c Datei und diese .c Datei kompiliert in eine .o Datei und diese dann miteinander verknüpft.

Dann passiert folgendes:

  1. funktioniert gut wegen der bereits erwähnten “vorläufigen Definitionen”: every .o Datei enthält eine davon, also sagt der Linker “ok”.

  2. geht nicht, weil beides .o Dateien enthalten eine Definition mit einem Wert, die kollidieren (auch wenn sie den gleichen Wert haben) – es darf insgesamt nur eine mit einem bestimmten Namen geben .o Dateien, die zu einem bestimmten Zeitpunkt miteinander verknüpft sind.

  3. funktioniert natürlich, weil man nur einen hat .o Datei und somit keine Kollisionsgefahr.

Wäre IMHO eine saubere Sache

  • entweder zu setzen extern int i; oder nur int i; in die Header-Datei,
  • und dann die “echte” Definition von i (nämlich int i = 100;) hinein file1.c. In diesem Fall wird diese Initialisierung beim Start des Programms verwendet und die entsprechende Zeile eingefügt main() kann ausgelassen werden. (Außerdem hoffe ich, dass die Benennung nur ein Beispiel ist; bitte nennen Sie keine globalen Variablen als i in echten Programmen.)

  • Was ich über “entweder setzen extern int i; oder nur int i; in die Header-Datei”: extern int i ist besser, weil es Ihnen sofort sagt, ob die “echte” Definition versehentlich fehlt. Mit einem bloßen int igäbe es eine stille Definition zu 0.

    – glglgl

    13. November 2011 um 12:06 Uhr

  • Ich habe die erste Erklärung nicht bekommen, können Sie näher darauf eingehen, wann Speicher der Variablen i zugewiesen wird. bis jetzt verstehe ich int i in global.h ist äquivalent zu extern int i; was bedeutet, dass beide Objektdateien den Verweis auf den Speicher auf i haben, der woanders zugewiesen ist.

    – Benutzer

    11. Februar 2015 um 7:12 Uhr


  • @ user2383973 Der Speicher wird vom Linker zugewiesen und reserviert. Wenn es eine Anfrage mit Zuweisung und eine “vorläufige” Definition sieht, ist alles in Ordnung. Wenn es mehrere Zuweisungen sieht, selbst wenn sie den gleichen Wert haben, ist es nicht in Ordnung.

    – glglgl

    11. Februar 2015 um 12:16 Uhr

  • @FoadRezek Ich nehme an, Sie haben die Dateistruktur in der Frage ausprobiert. Das liegt daran, dass Zuweisungen nicht auf Dateiebene gültig sind, sondern nur innerhalb von Funktionen.

    – glglgl

    30. Mai 2018 um 8:40 Uhr

  • Fall 1 ist undefiniertes Verhalten, die vorläufige Definition bewirkt, dass eine externe Definition für jede Übersetzungseinheit generiert wird, in der sie erscheint. (C11 6.9.2/2). Ihre Empfehlung ohne extern hat das gleiche Problem

    – MM

    7. Februar 2020 um 5:13 Uhr


Initialisieren Sie keine Variablen in Headern. Setzen Sie die Deklaration in den Header und die Initialisierung in eine der c Dateien.

In der Kopfzeile:

extern int i;

In Datei2.c:

int i=1;

  • Beachten Sie, dass das OP die Variable nicht initialisiert, sondern eine vorläufige Definition.

    – ninjalj

    13. November 2011 um 1:01 Uhr

  • @Banthar: Warum funktioniert es dann im zweiten Fall (wenn ich nur file1.c kompiliere)?

    – Bruce

    13. November 2011 um 1:29 Uhr

  • @Bruce: Weil es in diesem Fall nur einmal initialisiert wird.

    – glglgl

    13. November 2011 um 2:12 Uhr

Benutzeravatar von ninjalj
ninjalj

In Header-Dateien sollten Sie keine globalen Variablen definieren. Sie können sie als deklarieren extern in Header-Datei und definieren Sie sie in a .c Quelldatei.

(Hinweis: In C, int i; eine vorläufige Definition ist, weist sie Speicherplatz für die Variable zu (= ist eine Definition), wenn für diese Variable keine andere Definition in der Übersetzungseinheit gefunden wird.)

  • Was, wenn ich #ifndef in den Header setze und die Variable deklariere int a in der kopfzeile? Es kommt immer noch ein Fehler mit mehreren Definitionen, obwohl ich jetzt nur noch eine Kopie der Header-Datei habe.

    – CKM

    14. Oktober 2016 um 9:54 Uhr


Die derzeit akzeptierte Antwort auf diese Frage ist falsch. C11 6.9.2/2:

Wenn eine Übersetzungseinheit eine oder mehrere vorläufige Definitionen für einen Bezeichner enthält und die Übersetzungseinheit keine externe Definition für diesen Bezeichner enthält, dann ist das Verhalten genau so, als ob die Übersetzungseinheit eine Dateibereichsdeklaration dieses Bezeichners enthält, mit dem zusammengesetzten Typ als des Endes der Übersetzungseinheit, mit einem Initialisierer gleich 0.

Der ursprüngliche Code in der Frage verhält sich also so, als ob file1.c und file2.c jeder enthielt die Zeile int i = 0; am Ende, was aufgrund mehrerer externer Definitionen zu undefiniertem Verhalten führt (6.9/5).

Da dies eine semantische Regel und keine Einschränkung ist, ist keine Diagnose erforderlich.

Hier sind zwei weitere Fragen zum gleichen Code mit richtigen Antworten:

  • Vorläufige Definitionen in C und Verlinkung
  • Wie verwende ich extern, um Variablen zwischen Quelldateien zu teilen?

@glglgl hat bereits erklärt, warum das, was Sie versucht haben, nicht funktioniert hat. Eigentlich, wenn Sie wirklich darauf abzielen definieren eine Variable in einem Header, können Sie mit einigen Präprozessordirektiven austricksen:

Datei1.c:

#include <stdio.h>

#define DEFINE_I
#include "global.h"

int main()
{
    printf("%d\n",i);
    foo();
    return 0;
}

Datei2.c:

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

void foo()
{
    i = 54;
    printf("%d\n",i);
}

global.h:

#ifdef DEFINE_I
int i = 42;
#else
extern int i;
#endif

void foo();

In dieser Situation, i ist nur definiert in der Übersetzungseinheit, in der Sie DEFINE_I definiert haben und ist erklärt überall sonst. Der Linker beschwert sich nicht.

Ich habe das schon ein paar Mal gesehen, wo eine Aufzählung in einem Header deklariert wurde, und direkt darunter war eine Definition eines Zeichens ** mit den entsprechenden Bezeichnungen. Ich verstehe, warum der Autor es vorgezogen hat, diese Definition im Header zu haben, anstatt sie in eine bestimmte Quelldatei zu stecken, aber ich bin mir nicht sicher, ob die Implementierung so elegant ist.

Benutzeravatar von Anand Nekkunti
Anand Nekkunti

Definieren Sie keine Variablen in der Header-Datei, deklarieren Sie sie in der Header-Datei (gute Praxis). In Ihrem Fall funktioniert es, weil mehrere schwache Symbole vorhanden sind. Lesen Sie mehr über schwache und starke Symbole … Link:http://csapp.cs.cmu.edu/public/ch7-preview.pdf

Diese Art von Code verursacht Probleme beim Portieren.

1416070cookie-checkGlobale Variablen in Header-Datei

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy