Wie deklariere ich eine Struktur in einem Header, die von mehreren Dateien in c verwendet werden soll?

115

Wenn ich eine source.c-Datei mit einer Struktur habe:

struct a { 
    int i;
    struct b {
        int j;
    }
};

Wie kann diese Struktur in einer anderen Datei (dh func.c) verwendet werden?

Soll ich eine neue Header-Datei erstellen, die Struktur dort deklarieren und diesen Header einfügen func.c?

Oder sollte ich die gesamte Struktur in einer Header-Datei definieren und diese in beide source.cund aufnehmen func.c? Wie kann die Struktur externin beiden Dateien deklariert werden?

Soll ich typedefes? Wenn das so ist, wie?

Softwareentwickler
quelle
Beachten Sie, dass die Strukturdefinition nicht gültig ist. C. Nach der geschlossenen Klammer sollte mindestens ein Semikolon stehen struct b, aber dann adeklariert Ihre Struktur einen Typ, der nicht verwendet wird (Sie sollten wahrscheinlich einen Mitgliedsnamen definieren, möglicherweise knach der inneren engen Klammer und vor dem Semikolon.
Jonathan Leffler

Antworten:

139

Wenn diese Struktur von einer anderen Datei func.c verwendet werden soll, wie geht das?

Wenn ein Typ in einer Datei verwendet wird (dh eine func.c-Datei), muss er sichtbar sein. Der schlechteste Weg, dies zu tun, ist das Kopieren und Einfügen in jede benötigte Quelldatei.

Der richtige Weg ist, es in eine Header-Datei einzufügen und diese Header-Datei bei Bedarf einzuschließen.

Sollen wir eine neue Header-Datei öffnen und die Struktur dort deklarieren und diesen Header in die func.c aufnehmen?

Dies ist die Lösung, die mir besser gefällt, weil sie den Code sehr modular macht. Ich würde Ihre Struktur wie folgt codieren:

#ifndef SOME_HEADER_GUARD_WITH_UNIQUE_NAME
#define SOME_HEADER_GUARD_WITH_UNIQUE_NAME

struct a
{ 
    int i;
    struct b
    {
        int j;
    }
};

#endif

Ich würde Funktionen, die diese Struktur verwenden, in denselben Header einfügen (die Funktion, die "semantisch" Teil ihrer "Schnittstelle" ist).

Und normalerweise könnte ich die Datei nach dem Strukturnamen benennen und diesen Namen erneut verwenden, um die definierten Header-Guards auszuwählen.

Wenn Sie eine Funktion mit einem Zeiger auf die Struktur deklarieren müssen, benötigen Sie nicht die vollständige Strukturdefinition. Eine einfache Vorwärtserklärung wie:

struct a ;

Wird ausreichen, und es verringert die Kopplung.

oder können wir die Gesamtstruktur in der Header-Datei definieren und diese sowohl in source.c als auch in func.c aufnehmen?

Dies ist ein anderer Weg, der etwas einfacher, aber weniger modular ist: Einige Codes, für deren Funktion nur Ihre Struktur erforderlich ist, müssen noch alle Typen enthalten.

In C ++ könnte dies zu interessanten Komplikationen führen, aber dies ist nicht zum Thema (kein C ++ - Tag), daher werde ich nicht näher darauf eingehen.

dann, wie diese Struktur in beiden Dateien als extern deklariert wird. ?

Ich verstehe den Punkt vielleicht nicht, aber Greg Hewgill hat eine sehr gute Antwort in seinem Beitrag. Wie deklariere ich eine Struktur in einem Header, die von mehreren Dateien in c verwendet werden soll? .

sollen wir es dann wie tippen?

  • Wenn Sie C ++ verwenden, tun Sie dies nicht.
  • Wenn Sie C verwenden, sollten Sie.

Der Grund dafür ist, dass die Verwaltung von C-Strukturen schmerzhaft sein kann: Sie müssen das Schlüsselwort struct überall dort deklarieren, wo es verwendet wird:

struct MyStruct ; /* Forward declaration */

struct MyStruct
{
   /* etc. */
} ;

void doSomething(struct MyStruct * p) /* parameter */
{
   struct MyStruct a ; /* variable */
   /* etc */
}

Mit einem typedef können Sie es ohne das Schlüsselwort struct schreiben.

struct MyStructTag ; /* Forward declaration */

typedef struct MyStructTag
{
   /* etc. */
} MyStruct ;

void doSomething(MyStruct * p) /* parameter */
{
   MyStruct a ; /* variable */
   /* etc */
}

Es ist wichtig, dass Sie immer noch einen Namen für die Struktur behalten. Schreiben:

typedef struct
{
   /* etc. */
} MyStruct ;

erstellt nur eine anonyme Struktur mit einem typisierten Namen, und Sie können sie nicht weiter deklarieren. Halten Sie sich also an das folgende Format:

typedef struct MyStructTag
{
   /* etc. */
} MyStruct ;

Auf diese Weise können Sie MyStruct überall dort verwenden, wo Sie das Hinzufügen des Schlüsselworts struct vermeiden möchten, und MyStructTag weiterhin verwenden, wenn ein typedef nicht funktioniert (dh eine Vorwärtsdeklaration).

Bearbeiten:

Die falsche Annahme bezüglich der C99-Strukturdeklaration wurde korrigiert, wie Jonathan Leffler zu Recht bemerkte .

Bearbeiten 2018-06-01:

Craig Barnes erinnert uns in seinem Kommentar daran, dass Sie aus Gründen der Klarheit keine separaten Namen für den Strukturnamen "tag" und den Namen "typedef" verwenden müssen, wie ich es oben getan habe.

In der Tat könnte der obige Code gut geschrieben werden als:

typedef struct MyStruct
{
   /* etc. */
} MyStruct ;

IIRC, genau das macht C ++ mit seiner einfacheren Strukturdeklaration hinter den Kulissen, um die Kompatibilität mit C zu gewährleisten:

// C++ explicit declaration by the user
struct MyStruct
{
   /* etc. */
} ;
// C++ standard then implicitly adds the following line
typedef MyStruct MyStruct;

Zurück zu C habe ich beide Verwendungen gesehen (separate Namen und gleiche Namen), und keine hat mir bekannte Nachteile. Die Verwendung desselben Namens vereinfacht das Lesen, wenn Sie keine separaten C-Namespaces für Strukturen und andere Symbole verwenden .

paercebal
quelle
2
Können Sie den Abschnitt des C99-Standards kommentieren oder darauf hinweisen, der Ihren Kommentar "Wenn Sie C99 verwenden, nicht" garantiert?
Jonathan Leffler
Du hast recht. Ich habe kürzlich einen C99 getestet und war überrascht zu sehen, dass meine nicht typisierte Struktur bei Verwendung in C ++ nicht erkannt wurde. Ich suchte nach Compiler-Optionen und fand dann in allen Standarddokumenten meine Hände, fand aber nichts, was erklären könnte, dass ich es für möglich hielt ...
paercebal
2
... Trotzdem danke für die Bemerkung. Ich habe es jetzt korrigiert.
Paercebal
Es ist nicht erforderlich, unterschiedliche Namen für das structTag und den typedefNamen zu verwenden. C verwendet einen anderen Namespace für structTags, sodass Sie MyStructbeide verwenden können.
Craig Barnes
1
@CraigBarnes Du hast recht, aber ich wollte, dass das klar wird, indem du nur den Code liest. Hätte ich den gleichen Namen angegeben, hätte dies C-Neulinge über die Notwendigkeit verwirren können, den Namen * zweimal "in dieselbe" Erklärung "zu schreiben. Ich werde eine Notiz hinzufügen, in der Ihr Kommentar erwähnt wird. Danke!
paercebal
34

Für eine Strukturdefinition, die für mehr als eine Quelldatei verwendet werden soll, sollten Sie sie definitiv in eine Header-Datei einfügen. Fügen Sie diese Header-Datei dann in jede Quelldatei ein, die die Struktur benötigt.

Die externDeklaration wird nicht für Strukturdefinitionen verwendet, sondern für Variablendeklarationen (dh einige Datenwerte mit einem von Ihnen definierten Strukturtyp). Wenn Sie dieselbe Variable für mehr als eine Quelldatei verwenden möchten, deklarieren Sie sie wie externin einer Header-Datei wie:

extern struct a myAValue;

Definieren Sie dann in einer Quelldatei die tatsächliche Variable:

struct a myAValue;

Wenn Sie dies vergessen oder versehentlich in zwei Quelldateien definieren, informiert Sie der Linker darüber.

Greg Hewgill
quelle
Möglicherweise wird ein Linkerfehler angezeigt oder nicht. In C ermöglicht das Verknüpfungsmodell "allgemeine" Definitionen, sodass möglicherweise mehrere Definitionen ohne Initialisierer (und möglicherweise mit demselben Initialisierer) funktionieren. Es ist eine "gemeinsame Erweiterung". Ich glaube, einige Compiler unterstützen auch vorläufige (fehlende) Definitionen.
Jonathan Leffler
Definieren Sie dann in einer Quelldatei die tatsächliche Variable :; ... oder versehentlich in zwei Quelldateien definieren ... :)
Johannes Schaub - litb
Eine Technik, die ich für das Deklarations- / Definitionsproblem verwendet habe, besteht darin, bedingt GLOBALals externoder nichts oben im Header zu definieren und dann Variablen als zu deklarieren GLOBAL struct a myAValue;. In den meisten Quelldateien legen Sie fest, dass die #define GLOBAL externVersion verwendet wird ( Deklarieren der Variablen), und in genau einer Quelldatei wird die leere Definition verwendet, sodass die Variablen definiert werden .
TripeHound
Sie können den Strukturnamen mit dem Typedef-Namen in C identisch haben, jedoch nicht in C ++.
Xuhdev
6

Ah:

#ifndef A_H
#define A_H

struct a { 
    int i;
    struct b {
        int j;
    }
};

#endif

Jetzt müssen Sie nur noch ah in die Dateien aufnehmen, in denen Sie diese Struktur verwenden möchten.

fmsf
quelle