553 Stimmen

Warum sollten wir in C so oft eine struct typisieren?

Ich habe viele Programme gesehen, die aus Strukturen wie der folgenden bestehen

typedef struct 
{
    int i;
    char k;
} elem;

elem user;

Warum wird sie so oft benötigt? Gibt es einen bestimmten Grund oder ein bestimmtes Gebiet?

578voto

unwind Punkte 377331

Wie Greg Hewgill sagte, bedeutet der Typedef, dass Sie nicht mehr schreiben müssen struct überall auf der Welt. Das spart nicht nur Tastenanschläge, sondern kann den Code auch sauberer machen, da es ein klein wenig mehr Abstraktion bietet.

Dinge wie

typedef struct {
  int x, y;
} Point;

Point point_new(int x, int y)
{
  Point a;
  a.x = x;
  a.y = y;
  return a;
}

wird sauberer, wenn man nicht überall das Schlüsselwort "struct" sehen muss, es sieht eher so aus, als gäbe es wirklich einen Typ namens "Point" in Ihrer Sprache. Was, nach dem typedef ist der Fall, denke ich.

Beachten Sie auch, dass in Ihrem Beispiel (und auch in meinem) die Nennung der struct selbst, die Benennung ist auch dann nützlich, wenn Sie einen undurchsichtigen Typ bereitstellen möchten. Dann hätten Sie zum Beispiel Code wie diesen in der Kopfzeile:

typedef struct Point Point;

Point * point_new(int x, int y);

und stellen dann die struct Definition in der Implementierungsdatei:

struct Point
{
  int x, y;
};

Point * point_new(int x, int y)
{
  Point *p;
  if((p = malloc(sizeof *p)) != NULL)
  {
    p->x = x;
    p->y = y;
  }
  return p;
}

Im letzteren Fall können Sie den Punkt nicht als Wert zurückgeben, da seine Definition für Benutzer der Header-Datei verborgen ist. Dies ist eine Technik, die häufig in GTK+ zum Beispiel.

UPDATE Beachten Sie, dass es auch hoch angesehene C-Projekte gibt, bei denen diese Verwendung von typedef zu verstecken struct als eine schlechte Idee angesehen wird, ist der Linux-Kernel wahrscheinlich das bekannteste Projekt dieser Art. Siehe Kapitel 5 von Das Dokument Linux Kernel CodingStyle für Linus' wütende Worte :) Ich will damit sagen, dass das "sollte" in der Frage vielleicht doch nicht in Stein gemeißelt ist.

259voto

Jerry Hicks Punkte 2694

Es ist erstaunlich, wie viele Leute das falsch verstehen. BITTE keine typedef structs in C, es verschmutzt unnötig den globalen Namespace, der in großen C-Programmen typischerweise schon sehr verschmutzt ist.

Außerdem sind typisierte Strukturen ohne Tag-Namen eine Hauptursache für die unnötige Auferlegung von Ordnungsbeziehungen zwischen Header-Dateien.

Bedenken Sie:

#ifndef FOO_H
#define FOO_H 1

#define FOO_DEF (0xDEADBABE)

struct bar; /* forward declaration, defined in bar.h*/

struct foo {
  struct bar *bar;
};

#endif

Mit einer solchen Definition, die keine Typedefs verwendet, ist es für eine Compiland-Unit möglich, foo.h einzubinden, um an die FOO_DEF Definition. Wenn es nicht versucht, das 'bar'-Mitglied der foo struct, dann ist es nicht notwendig, die Datei "bar.h" einzubinden.

Da die Namespaces zwischen den Tag-Namen und den Member-Namen unterschiedlich sind, ist es auch möglich, sehr lesbaren Code zu schreiben, wie z.B.:

struct foo *foo;

printf("foo->bar = %p", foo->bar);

Da die Namensräume getrennt sind, gibt es keinen Konflikt bei der Benennung von Variablen, die mit dem Namen ihres Struktur-Tags übereinstimmen.

Wenn ich Ihren Code pflegen muss, werde ich Ihre typisierten Strukturen entfernen.

158voto

Michael Burr Punkte 320591

Aus einem alten Artikel von Dan Saks ( http://www.ddj.com/cpp/184403396?pgno=3 ):


Die Regeln der Sprache C für die Benennung von structs sind ein wenig exzentrisch, aber sie sind ziemlich harmlos. Wenn jedoch auf Klassen in C++ ausgedehnt werden, öffnen dieselben Regeln kleine Risse, durch die Bugs krabbeln.

In C wird der Name s, der in

struct s
    {
    ...
    };

ist eine Markierung. Ein Tag-Name ist kein Typ name. In Anbetracht der obigen Definition, Deklarationen wie

s x;    /* error in C */
s *p;   /* error in C */

sind Fehler in C. Sie müssen sie schreiben als

struct s x;     /* OK */
struct s *p;    /* OK */

Die Namen von Unions und Aufzählungen sind ebenfalls Tags und keine Typen.

In C unterscheiden sich die Tags von allen anderen Namen (für Funktionen, Typen, Variablen und Aufzählungskonstanten). C-Compiler verwalten Tags in einer Symbol Tabelle, die konzeptionell, wenn auch nicht physisch getrennt von der Tabelle die alle anderen Namen enthält. Daher ist es ist es möglich, dass ein C-Programm einen Tag und einen anderen Namen mit der gleichen Schreibweise im gleichen Bereich haben. Zum Beispiel,

struct s s;

ist eine gültige Erklärung, die Variable s vom Typ struct s deklariert. Es mag keine gute Praxis sein, aber C-Compiler müssen es akzeptieren. Ich habe noch nie eine Begründung gesehen, warum C so entworfen wurde Weise entworfen wurde. Ich habe immer gedacht, es sei ein Fehler, aber so ist es.

Viele Programmierer (einschließlich Ihrer wirklich) ziehen es vor, Strukturnamen als als Typnamen zu betrachten, daher definieren sie einen Alias für das Tag unter Verwendung eines Typedefs. Für Beispiel: Definition von

struct s
    {
    ...
    };
typedef struct s S;

können Sie S anstelle von struct s verwenden, wie in

S x;
S *p;

Ein Programm kann S nicht als Name von eines Typs und einer Variablen (oder Funktion oder Aufzählungskonstante) verwenden:

S S;    // error

Das ist gut.

Der Tag-Name in einer Struktur, Union oder enum-Definition ist optional. Viele Programmierer falten die struct-Definition in das Typedef ein und verzichten auf das Tag gänzlich verzichten, wie in:

typedef struct
    {
    ...
    } S;

Der verlinkte Artikel hat auch eine Diskussion darüber, wie die C++-Verhalten von nicht erfordern eine typedef kann zu subtilen Problemen beim Verbergen von Namen führen. Um diese Probleme zu vermeiden, ist es eine gute Idee typedef Ihre Klassen und Strukturen auch in C++, auch wenn es auf den ersten Blick unnötig erscheint. In C++, mit der typedef wird das Verstecken des Namens zu einem Fehler, auf den Sie der Compiler hinweist, und nicht zu einer versteckten Quelle potenzieller Probleme.

73voto

Greg Hewgill Punkte 882617

Mit einer typedef vermeidet, dass man schreiben muss struct jedes Mal, wenn Sie eine Variable dieses Typs deklarieren:

struct elem
{
 int i;
 char k;
};
elem user; // compile error!
struct elem user; // this is correct

48voto

cschol Punkte 12385

Ein weiterer guter Grund, enums und structs immer zu typisieren, ergibt sich aus diesem Problem:

enum EnumDef
{
  FIRST_ITEM,
  SECOND_ITEM
};

struct StructDef
{
  enum EnuumDef MyEnum;
  unsigned int MyVar;
} MyStruct;

Beachten Sie den Tippfehler in EnumDef in der Struktur (Enu u mDef)? Dies kompiliert ohne Fehler (oder Warnung) und ist (abhängig von der wörtlichen Interpretation des C-Standards) korrekt. Das Problem ist, dass ich gerade eine neue (leere) Aufzählungsdefinition innerhalb meiner Struktur erstellt habe. Ich verwende nicht (wie beabsichtigt) die vorherige Definition EnumDef.

Mit einem typdef hätten ähnliche Tippfehler zu einem Compilerfehler wegen der Verwendung eines unbekannten Typs geführt:

typedef 
{
  FIRST_ITEM,
  SECOND_ITEM
} EnumDef;

typedef struct
{
  EnuumDef MyEnum; /* compiler error (unknown type) */
  unsigned int MyVar;
} StructDef;
StrructDef MyStruct; /* compiler error (unknown type) */

Ich würde dafür plädieren, Strukturen und Aufzählungen IMMER zu typisieren.

Nicht nur, um Tipparbeit zu sparen (kein Wortspiel beabsichtigt ;)), sondern weil es sicherer ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X