wake-up-neo.com

Was ist "Vorwärtsdeklaration" und der Unterschied zwischen "typedef struct X" und "struct X"?

Ich bin ein Anfänger in der C-Programmierung und kenne den Unterschied zwischen struct type-Deklaration und typedef struct-Deklaration. Ich stieß auf eine Antwort, die besagte, dass, wenn wir eine Struktur definieren, wie:

typedef struct
{ 
some members;
}struct_name;

Dann wird es wie ein Alias ​​für eine anonyme Struktur sein (da sie keinen Tag-Namen hat). Es kann also nicht für die Vorwärtsdeklaration verwendet werden. Ich weiß nicht, was die "Vorwärtserklärung" bedeutet.

Ich wollte das auch für den folgenden Code wissen:

typedef struct NAME
{ 
some members;
}struct_alias;

Gibt es einen Unterschied zwischen NAME und struct_alias? Oder sind beide gleich, denn struct_alias ist ein Alias ​​von struct NAME? 

Außerdem können wir eine Variable vom Typ struct NAME wie folgt deklarieren:

struct_alias variable1;

und/oder wie:

struct NAME variable2;

oder wie:

NAME variable3; 
37
r_goyal

struct forward-Deklarationen können nützlich sein, wenn Sie Schleifenstrukturdeklarationen benötigen. Beispiel:

struct a {
    struct b * b_pointer;
    int c;
};

struct b {
    struct a * a_pointer;
    void * d;
};

Wenn struct a deklariert ist, kennt es die Spezifikationen von struct b noch nicht, aber Sie können es weiterleiten.

Wenn Sie eine anonyme Struktur eingeben, lässt der Compiler nicht zu, dass der Name vor der Typedef steht.

Das ist illegal:

struct a {
    b * b_pointer;
    int c;
};

typedef struct {
    struct a * a_pointer;
    void * d;
} b;

// struct b was never declared or defined

Dies ist jedoch legal:

struct a {
    struct b * b_pointer;
    int c;
};

typedef struct b {
    struct a * a_pointer;
    void * d;
} b;

// struct b is defined and has an alias type called b

Also das ist:

typedef struct b b;
// the type b referes to a yet undefined type struct b

struct a {
    b * struct_b_pointer;
    int c;
};

struct b {
    struct a * a_pointer;
    void * d;
};

Und das:

typedef int b;

struct a {
    struct b * struct_b_pointer;
    b b_integer_type;
    int c;
};

struct b {
    struct a * a_pointer;
    void * d;
};

// struct b and b are two different types all together. Note: this is not allowed in C++
44
Sergey L.

Vorwärtsdeklaration ist ein Versprechen, etwas zu definieren, das Sie einem Compiler an dem Punkt machen, an dem die Definition nicht vorgenommen werden kann. Der Compiler kann Ihr Word verwenden, um andere Deklarationen zu interpretieren, die er sonst nicht interpretieren könnte.

Ein allgemeines Beispiel ist eine struct, die als Knoten in einer verknüpften Liste konzipiert wurde: Sie müssen einen Zeiger auf einen Knoten in die struct setzen, der Compiler würde es jedoch nicht ohne Forward-Deklaration oder ein Tag zulassen:

// Forward declaration
struct element;
typedef struct {
    int value;
    // Use of the forward declaration
    struct element *next;
} element; // Complete definition

und so kann es nicht für Vorwärtsdeklaration verwendet werden

Ich denke, der Standpunkt des Autors bestand darin, dass die Angabe Ihres Tags struct einer Vorwärtsdeklaration gleichwertig wäre:

typedef struct element {
    int value;
    // No need for a forward declaration here
    struct element *next;
} element;
20
dasblinkenlight

Vorwärtsdeklaration ist eine Deklaration, die einer eigentlichen Definition vorangeht, normalerweise, um auf den deklarierten Typ verweisen zu können, wenn die Definition nicht verfügbar ist. Natürlich kann nicht alles mit der deklarierten nicht definierten Struktur gemacht werden, aber in bestimmten Zusammenhängen ist es möglich, sie zu verwenden. Ein solcher Typ wird als unvollständig bezeichnet, und seine Verwendung unterliegt einigen Einschränkungen. Zum Beispiel:

struct X; // forward declaration

void f(struct X*) { }  // usage of the declared, undefined structure

// void f(struct X) { }         // ILLEGAL
// struct X x;                  // ILLEGAL
// int n =sizeof(struct X);     // ILLEGAL

// later, or somewhere else altogether
struct X { /* ... */ };

Dies kann nützlich sein, z. Um zirkulare Abhängigkeiten zu brechen oder die Kompilierungszeit zu verkürzen, da die Definitionen in der Regel erheblich größer sind und mehr Ressourcen zum Analysieren erforderlich sind.

In Ihrem Beispiel sind struct NAME und struct_alias tatsächlich gleichwertig.

struct_alias variable1;
struct NAME variable2;

sind richtig;

NAME variable3;

ist nicht, da in C das struct-Schlüsselwort erforderlich ist.

12
Marcin Łoś

struct_alias und struct NAME sind gleich, struct_alias ist ein Alias ​​für struct NAME 

Beide sind gleich und zulässig

struct_alias variable1;  

struct NAME variable1; 

das ist illegal 

NAME variable3;   

Siehe diesen Artikel auf Weitergabe der Erklärung

7
Gangadhar

Wie bereits erwähnt, ist eine Vorwärtsdeklaration in C/C++ die Deklaration von etwas, deren tatsächliche Definition nicht verfügbar ist. Es ist eine Deklaration, die dem Compiler sagt, "es gibt einen Datentyp ABC".

Nehmen wir an, dies ist ein Header für einen Schlüssel-/Wertspeicher my_dict.h:

...
struct my_dict_t;
struct my_dict_t* create();

char* get_value(const struct my_dict_t* dict, const char* name);
char* insert(struct my_dict_t* dict, const char* name, char* value);
void destroy(struct my_dict_t* dict);
...

Sie wissen nichts über my_dict_t, aber für die Verwendung des Shops Müssen Sie nicht wissen:

#include "my_dict.h"
...
struct my_dict_t* dict = create();
if(0 != insert(dict, "AnEntry", strdup("AValue"))) {
    ...
}
...

Der Grund dafür ist: Sie verwenden nur POINTERS für die Datenstruktur.

Zeiger sind nur Zahlen, und um damit umgehen zu können, müssen Sie nicht wissen, worauf sie zeigen.

Dies ist nur wichtig, wenn Sie versuchen, auf sie zuzugreifen, wie z

struct my_dict_t* dict = create();
printf("%s\n", dict->value);  /* Impossible if only a forward decl is available */

Für die Implementierung der Funktionen benötigen Sie also eine tatsächliche Definition von my_struct_t..__, die Sie in der Quelldatei my_dict.c wie folgt tun könnten:

#include "my_dict.h"

struct my_dict_t {
    char* value;
    const char* name;
    struct my_dict_t* next;
}

struct my_dict_t* create() {
    return calloc(1, sizeof(struct my_dict_t));
}

Dies ist praktisch für verschiedene Situationen, z

  • Zur Auflösung zirkulärer Abhängigkeiten, wie Sergei L. erklärt hat.
  • Zur Verkapselung wie im obigen Beispiel.

Die Frage bleibt also: Warum können wir die Forward-Deklaration überhaupt nicht weglassen, wenn Sie die obigen Funktionen verwenden? Am Ende würde es ausreichen, wenn der Compiler weiß, dass alle dict Zeiger sind.

Der Compiler führt jedoch Typüberprüfungen durch: Er muss überprüfen, ob Sie so etwas nicht tun 

...
int i = 12;
char* value = get_value(&i, "MyName");
...

Es muss nicht wissen, wie my_dict_t aussieht, aber es muss wissen, dass &i nicht der Typ des Zeigers ist, der get_value() erwartet.

0
Michael Beer