Warum sollten wir eine Struktur so oft in C eingeben?

406

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

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

elem user;

Warum wird es so oft gebraucht? Gibt es einen bestimmten Grund oder einen bestimmten Bereich?

Manoj Zweifel
quelle
14
Gründlichere und präzisere Antwort: stackoverflow.com/questions/612328/…
AbiusX
Es hat Nachteile Ich denke, Sie können keine Link-Liste mit anonymer Struktur erstellen, weil die Zeile struct * ptrinnerhalb der Struktur Fehler verursachen wird
Raghib Ahsan
9
Die 'gründlichere und präzisere Antwort' ist der Unterschied zwischen struct und typedef struct in C ++ , und es gibt signifikante Unterschiede zwischen C und C ++ in diesem Bereich, die diese Antwort für eine Frage zu C nicht ganz angemessen machen.
Jonathan Leffler
Diese Frage hat eine doppelte typedef struct vs struct-Definition , die auch hervorragende Antworten enthält.
Jonathan Leffler
2
OTOH, kernel.org/doc/html/v4.10/process/coding-style.html sagt uns, dass wir solche typedefs nicht machen sollten.
glglgl

Antworten:

454

Wie Greg Hewgill sagte, bedeutet das typedef, dass Sie nicht mehr structüberall schreiben müssen . Das spart nicht nur Tastenanschläge, sondern kann auch den Code sauberer machen, da es eine smidgen mehr Abstraktion bietet.

Zeug 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 Sie das Schlüsselwort "struct" nicht überall sehen müssen. Es sieht eher so aus, als ob es in Ihrer Sprache wirklich einen Typ namens "Point" gibt. Was nach dem wohl typedefder Fall ist.

Beachten Sie auch, dass Ihr Beispiel (und mein Beispiel) das Benennen des struct selbst selbst weggelassen hat, das tatsächliche Benennen jedoch auch nützlich ist, wenn Sie einen undurchsichtigen Typ angeben möchten. Dann hätten Sie Code wie diesen in der Kopfzeile, zum Beispiel:

typedef struct Point Point;

Point * point_new(int x, int y);

und geben Sie dann die structDefinition in der Implementierungsdatei an:

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;
}

In diesem letzteren Fall können Sie den Punkt nicht nach Wert zurückgeben, da seine Definition für Benutzer der Header-Datei nicht sichtbar ist. Dies ist beispielsweise eine in GTK + weit verbreitete Technik .

UPDATE Beachten Sie, dass es auch hoch angesehene C-Projekte gibt, bei denen diese Verwendung typedefzum Verbergen structals schlechte Idee angesehen wird. Der Linux-Kernel ist wahrscheinlich das bekannteste Projekt dieser Art. In Kapitel 5 des Linux Kernel CodingStyle-Dokuments finden Sie Linus 'verärgerte Worte. :) Mein Punkt ist, dass das "sollte" in der Frage vielleicht doch nicht in Stein gemeißelt ist.

entspannen
quelle
58
Sie sollten keine Bezeichner mit einem Unterstrich gefolgt von einem Großbuchstaben verwenden, sie sind reserviert (siehe Abschnitt 7.1.3, Absatz 1). Obwohl es unwahrscheinlich ist, dass es sich um ein großes Problem handelt, handelt es sich bei der Verwendung um ein technisch undefiniertes Verhalten (7.1.3, Absatz 2).
Dreamlax
16
@dreamlax: Falls es anderen nicht klar war, beginnt ein Bezeichner nur mit einem Unterstrich und einem Großbuchstaben, was Sie nicht tun sollten. Sie können dies in der Mitte eines Bezeichners verwenden.
Brianmearns
12
Es ist interessant, dass das hier gegebene Beispiel (in dem das typedef die Verwendung von "struct" "überall" verhindert) tatsächlich länger ist als derselbe Code ohne das typedef, da es genau eine Verwendung des Wortes "struct" speichert. Das gewonnene Maß an Abstraktion ist selten vergleichbar mit der zusätzlichen Verschleierung.
William Pursell
7
@Rerito fyi, Seite 166 des C99-Entwurfs , Alle Bezeichner, die mit einem Unterstrich und entweder einem Großbuchstaben oder einem anderen Unterstrich beginnen, sind immer für jede Verwendung reserviert. Und alle Kennungen , die mit einem Unterstrich beginnen immer reserviert für den Einsatz als Kennungen mit fi le Umfang sowohl in den normalen und Tag - Namensräumen.
e2-e4
10
Interessanterweise sagen die Linux-Kernel-Codierungsrichtlinien, dass wir bei der Verwendung von typedefs (Abschnitt 5) viel konservativer sein sollten: kernel.org/doc/Documentation/CodingStyle
gsingh2011
206

Es ist erstaunlich, wie viele Leute das falsch verstehen. BITTE geben Sie keine Strukturen in C ein, sondern verschmutzen unnötigerweise den globalen Namespace, der normalerweise bereits in großen C-Programmen stark verschmutzt ist.

Typedef'd-Strukturen ohne Tag-Namen sind auch eine Hauptursache für das unnötige Auferlegen von Ordnungsbeziehungen zwischen Header-Dateien.

Erwägen:

#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, bei der keine typedefs verwendet werden, kann eine Compiland-Einheit foo.h einschließen, um zur FOO_DEFDefinition zu gelangen . Wenn nicht versucht wird, das ' foobar' -Mitglied der Struktur zu dereferenzieren, muss die Datei "bar.h" nicht eingefügt werden.

Da sich die Namespaces zwischen den Tag-Namen und den Mitgliedsnamen unterscheiden, ist es auch möglich, gut lesbaren Code zu schreiben, wie z.

struct foo *foo;

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

Da die Namespaces getrennt sind, besteht kein Konflikt bei der Benennung von Variablen, die mit ihrem Struktur-Tag-Namen übereinstimmen.

Wenn ich Ihren Code pflegen muss, werde ich Ihre typedef'd Strukturen entfernen.

Jerry Hicks
quelle
37
Was noch erstaunlicher ist, ist, dass ich 13 Monate nach dieser Antwort der Erste bin, der sie positiv bewertet! Das Schreiben von Strukturen ist einer der größten Missbräuche von C und hat keinen Platz in gut geschriebenem Code. typedef ist nützlich, um verschlungene Funktionszeigertypen zu verschleiern, und dient wirklich keinem anderen nützlichen Zweck.
William Pursell
28
Peter van der Linden spricht sich in seinem aufschlussreichen Buch "Expert C Programming - Deep C Secrets" auch gegen Typedefing-Strukturen aus. Das Wesentliche ist: Sie möchten wissen, dass etwas eine Struktur oder Vereinigung ist, nicht es verbergen.
Jens
34
Der Linux-Kernel-Codierungsstil verbietet ausdrücklich das Definieren von Strukturen. Kapitel 5: Typedefs: "Es ist ein Fehler , typedef für Strukturen und Zeiger zu verwenden." kernel.org/doc/Documentation/CodingStyle
jasso
63
Welche Vorteile bietet die wiederholte Eingabe von "struct"? Apropos Umweltverschmutzung: Warum sollten Sie eine Struktur und eine Funktion / Variable / Typedef mit demselben Namen in einem globalen Namespace haben (es sei denn, es handelt sich um eine Typedef für dieselbe Funktion)? Das sichere Muster ist zu verwenden typedef struct X { ... } X. Auf diese Weise können Sie die Kurzform verwenden X, um die Struktur überall dort zu adressieren, wo die Definition verfügbar ist. Sie können sie jedoch weiterhin deklarieren und bei Bedarf verwenden struct X.
Pavel Minaev
7
Ich persönlich benutze sehr selten, wenn überhaupt, typedef, ich würde nicht sagen, dass andere Leute es nicht benutzen sollten, es ist einfach nicht mein Stil. Ich möchte eine Struktur vor einem Variablentyp sehen, damit ich sofort weiß, dass es sich um eine Struktur handelt. Das Argument, das einfacher einzugeben ist, ist etwas lahm. Eine Variable, die aus einem einzelnen Buchstaben besteht, ist auch einfacher einzugeben, auch bei automatischen Vervollständigungen, wie schwierig es heutzutage ist, struct irgendwo einzugeben.
Nathan Day
138

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


Die C-Sprachregeln für die Benennung von Strukturen sind etwas exzentrisch, aber ziemlich harmlos. Wenn sie jedoch auf Klassen in C ++ erweitert werden, öffnen dieselben Regeln kleine Risse, durch die Fehler kriechen können.

In C erscheint der Name in

struct s
    {
    ...
    };

ist ein Tag. Ein Tag-Name ist kein Typname. In Anbetracht der obigen Definition können Erklärungen 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 Gewerkschaften und Aufzählungen sind ebenfalls Tags und keine Typen.

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

struct s s;

ist eine gültige Deklaration, die Variable s vom Typ struct s deklariert. Es ist möglicherweise keine gute Praxis, aber C-Compiler müssen dies akzeptieren. Ich habe nie eine Begründung dafür gesehen, warum C so entworfen wurde. Ich habe immer gedacht, dass es ein Fehler war, aber da ist es.

Viele Programmierer (einschließlich Ihrer wirklich) ziehen es vor, Strukturnamen als Typnamen zu betrachten, daher definieren sie einen Alias ​​für das Tag mithilfe eines typedef. Zum Beispiel definieren

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

Ermöglicht die Verwendung von S anstelle von Strukturen wie in

S x;
S *p;

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

S S;    // error

Das ist gut.

Der Tag-Name in einer Struktur-, Vereinigungs- oder Aufzählungsdefinition ist optional. Viele Programmierer falten die Strukturdefinition in das typedef und verzichten ganz auf das Tag, wie in:

typedef struct
    {
    ...
    } S;

Der verlinkte Artikel enthält auch eine Diskussion darüber, wie das C ++ - Verhalten, kein zu benötigen, typedefsubtile Probleme beim Ausblenden von Namen verursachen kann. Um diese Probleme zu vermeiden, ist es auch für typedefIhre Klassen und Strukturen in C ++ eine gute Idee , auch wenn dies auf den ersten Blick unnötig erscheint. In C ++ wird das typedefAusblenden des Namens zu einem Fehler, über den der Compiler informiert, und nicht zu einer versteckten Quelle potenzieller Probleme.

Michael Burr
quelle
2
Ein Beispiel dafür, wo der Tag-Name mit einem Nicht-Tag-Namen identisch ist, ist das Programm (POSIX oder Unix) mit der int stat(const char *restrict path, struct stat *restrict buf)Funktion. Dort haben Sie eine Funktion statim normalen Namensraum und struct statim Tag-Namensraum.
Jonathan Leffler
2
Ihre Aussage, SS; // Fehler .... IST FALSCH Es funktioniert gut. Ich meine Ihre Aussage, dass "wir können nicht den gleichen Namen für typedef Tag und var haben" ist falsch ... pls check
eRaisedToX
63

Wenn Sie a verwenden, typedefmüssen structSie nicht jedes Mal schreiben, wenn Sie eine Variable dieses Typs deklarieren:

struct elem
{
 int i;
 char k;
};
elem user; // compile error!
struct elem user; // this is correct
Greg Hewgill
quelle
2
ok, wir haben dieses Problem nicht in C ++. Warum also sollte niemand diesen Fehler aus dem C-Compiler entfernen und ihn wie in C ++ machen? Ok C ++ hat einige unterschiedliche Anwendungsbereiche und daher die erweiterten Funktionen. Aber können wir einige davon nicht in C erben, ohne das zu ändern Original C?
Manoj Zweifel
4
Manoj, der Tag-Name ("struct foo") ist erforderlich, wenn Sie eine Struktur definieren müssen, die auf sich selbst verweist. zB der "nächste" Zeiger in einer verknüpften Liste. Genauer gesagt implementiert der Compiler den Standard, und das sagt der Standard.
Michael Carman
41
Es ist kein Fehler im C-Compiler, es ist Teil des Designs. Sie haben das für C ++ geändert, was meiner Meinung nach die Dinge einfacher macht, aber das bedeutet nicht, dass Cs Verhalten falsch ist.
Herms
5
Leider definieren viele 'Programmierer' eine Struktur und tippen sie dann mit einem 'nicht verwandten' Namen ein (wie struct myStruct ...; typedef struct myStruct susan *; In fast allen Fällen führt ein typedef dazu, dass der Code nur überladen wird und die eigentliche Definition von verborgen bleibt eine Variable / Parameter, und führt alle falsch, einschließlich des ursprünglichen Schreibers des Codes.
user3629249
1
@ user3629249 Ich stimme Ihnen zu, dass der erwähnte Programmierstil schrecklich ist, aber dies ist kein Grund, typedefStrukturen im Allgemeinen zu verunglimpfen . Sie könnten auch tun typedef struct foo foo;. Natürlich wird das structSchlüsselwort nicht mehr benötigt, was ein nützlicher Hinweis darauf sein kann, dass der Typalias, den wir betrachten, ein Alias ​​für eine Struktur ist, aber im Allgemeinen nicht schlecht. Stellen Sie sich auch einen Fall vor, in dem der Bezeichner des resultierenden Typalias von typedefangibt, dass es sich um einen Alias ​​für eine Struktur handelt, z typedef struct foo foo_struct;.
RobertS unterstützt Monica Cellio
38

Ein weiterer guter Grund, immer Aufzählungen und Strukturen einzugeben, 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 wird ohne Fehler (oder Warnung) kompiliert und ist (abhängig von der wörtlichen Interpretation des C-Standards) korrekt. Das Problem ist, dass ich gerade eine neue (leere) Aufzählungsdefinition in meiner Struktur erstellt habe. Ich verwende nicht (wie beabsichtigt) die vorherige Definition EnumDef.

Bei einem typdef hätte eine ähnliche Art von Tippfehlern zu Compilerfehlern bei 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 IMMER befürworten, Strukturen und Aufzählungen zu tippen.

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

cschol
quelle
1
Schlimmer noch, Ihr Tippfehler könnte mit einem anderen Tag zusammenfallen. Im Fall einer Struktur kann dies dazu führen, dass das gesamte Programm korrekt kompiliert wird und zur Laufzeit ein undefiniertes Verhalten aufweist.
MM
3
diese Definition: 'typedef {FIRST_ITEM, SECOND_ITEM} EnumDef;' definiert keine Aufzählung. Ich habe Hunderte von großen Programmen geschrieben und hatte das Unglück, Wartungsarbeiten an Programmen durchzuführen, die andere geschrieben haben. Aus harter Erfahrung führt die Verwendung von typedef für eine Struktur nur zu Problemen. Hoffentlich ist der Programmierer nicht so behindert, dass er Probleme beim Eingeben einer vollständigen Definition hat, wenn er eine Strukturinstanz deklariert. C ist nicht einfach, daher ist das Eingeben weiterer Zeichen nicht nachteilig für den Betrieb des Programms.
user3629249
2
Für diejenigen, die es etwas ablehnen, mehr als die absolute Mindestanzahl von Zeichen einzugeben, kann ich vorschlagen, einer Gruppe beizutreten, die versucht, Anwendungen mit der Mindestanzahl von Tastenanschlägen zu schreiben. Verwenden Sie ihre neuen Fähigkeiten nur nicht in einer Arbeitsumgebung, insbesondere nicht in einer Arbeitsumgebung, in der Peer Reviews rigoros durchgeführt werden
user3629249
Beachten Sie, dass die Verwendung enumals Typ normalerweise nicht empfohlen wird , da viele Compiler seltsame Warnungen ausgeben, wenn sie "falsch" verwendet werden. Wenn Sie beispielsweise eine enumbis 0 initialisieren , wird möglicherweise eine Warnung "Ganzzahlkonstante nicht in Aufzählung" ausgegeben. Das Vorwärtsdeklarieren von enumist ebenfalls nicht zulässig. Man sollte stattdessen int(oder unsigned int) verwenden.
Yyny
5
Dieses Beispiel wird nicht kompiliert, und ich würde es auch nicht erwarten. Kompilieren von Debug / test.o test.c: 10: 17: Fehler: Feld hat unvollständigen Typ 'enum EnuumDef' enum EnuumDef MyEnum; ^ test.c: 10: 8: note: Vorwärtsdeklaration von 'enum EnuumDef' enum EnuumDef MyEnum; ^ 1 Fehler generiert. gnuc, mit std = c99.
Natersoz
30

Linux-Kernel-Codierungsstil Kapitel 5 bietet große Vor- und Nachteile (meistens Nachteile) der Verwendung typedef.

Bitte verwenden Sie keine Dinge wie "vps_t".

Es ist ein Fehler , typedef für Strukturen und Zeiger zu verwenden. Wenn Sie eine sehen

vps_t a;

Was bedeutet es in der Quelle?

Im Gegensatz dazu, wenn es heißt

struct virtual_container *a;

Sie können tatsächlich sagen, was "a" ist.

Viele Leute denken, dass Typedefs "die Lesbarkeit verbessern". Nicht so. Sie sind nur nützlich für:

(a) völlig undurchsichtige Objekte (wobei das typedef aktiv verwendet wird, um zu verbergen, was das Objekt ist).

Beispiel: "pte_t" usw. undurchsichtige Objekte, auf die Sie nur mit den richtigen Zugriffsfunktionen zugreifen können.

HINWEIS! Undurchsichtigkeit und "Zugriffsfunktionen" sind an sich nicht gut. Der Grund, warum wir sie für Dinge wie pte_t usw. haben, ist, dass es dort wirklich absolut keine tragbaren Informationen gibt.

(b) Löschen Sie ganzzahlige Typen, wobei die Abstraktion dazu beiträgt , Verwirrung zu vermeiden, ob sie "int" oder "long" ist.

u8 / u16 / u32 sind vollkommen feine Typedefs, obwohl sie besser in Kategorie (d) passen als hier.

HINWEIS! Wieder - es muss einen Grund dafür geben. Wenn etwas "lange nicht signiert" ist, gibt es keinen Grund dazu

typedef unsigned long myflags_t;

Wenn es jedoch einen klaren Grund dafür gibt, warum es unter bestimmten Umständen ein "unsigned int" und unter anderen Konfigurationen ein "unsigned long" sein kann, dann verwenden Sie auf jeden Fall ein typedef.

(c) Wenn Sie mit sparse buchstäblich einen neuen Typ für die Typprüfung erstellen .

(d) Neue Typen, die unter bestimmten außergewöhnlichen Umständen mit Standard-C99-Typen identisch sind.

Obwohl es nur eine kurze Zeit dauern würde, bis sich Augen und Gehirn an die Standardtypen wie 'uint32_t' gewöhnt haben, lehnen manche Leute ihre Verwendung trotzdem ab.

Daher sind die Linux-spezifischen Typen 'u8 / u16 / u32 / u64' und ihre signierten Entsprechungen zulässig, die mit den Standardtypen identisch sind - obwohl sie in Ihrem eigenen neuen Code nicht obligatorisch sind.

Wenn Sie vorhandenen Code bearbeiten, der bereits den einen oder anderen Satz von Typen verwendet, sollten Sie die vorhandenen Auswahlmöglichkeiten in diesem Code einhalten.

(e) Typen, die für die Verwendung im Benutzerbereich sicher sind.

In bestimmten Strukturen, die für den Benutzerbereich sichtbar sind, können wir keine C99-Typen benötigen und das obige Formular 'u32' nicht verwenden. Daher verwenden wir __u32 und ähnliche Typen in allen Strukturen, die für den Benutzerbereich freigegeben sind.

Vielleicht gibt es auch andere Fälle, aber die Regel sollte grundsätzlich sein, NIEMALS ein typedef zu verwenden, es sei denn, Sie können einer dieser Regeln eindeutig entsprechen.

Im Allgemeinen sollte ein Zeiger oder eine Struktur mit Elementen, auf die vernünftigerweise direkt zugegriffen werden kann, niemals ein typedef sein.

Yu Hao
quelle
4
"Undurchsichtigkeit und" Zugriffsfunktionen "sind an sich nicht gut". Kann jemand erklären warum? Ich würde denken, dass das Verstecken und Einkapseln von Informationen eine sehr gute Idee wäre.
Yawar
5
@ Yawar Ich habe gerade dieses Dokument gelesen und hatte genau den gleichen Gedanken. Sicher, C ist nicht objektorientiert, aber Abstraktion ist immer noch eine Sache.
Baldrickk
12

Es stellt sich heraus, dass es Vor- und Nachteile gibt. Eine nützliche Informationsquelle ist das wegweisende Buch "Expert C Programming" ( Kapitel 3 ). Kurz gesagt, in C gibt es mehrere Namespaces: Tags, Typen, Mitgliedsnamen und Bezeichner . typedefFührt einen Alias ​​für einen Typ ein und sucht ihn im Tag-Namespace. Nämlich,

typedef struct Tag{
...members...
}Type;

definiert zwei Dinge. Ein Tag im Tag-Namespace und ein Typ im Typ-Namespace. Sie können also beides tun Type myTypeund struct Tag myTagType. Erklärungen wie struct Type myTypeoder Tag myTagTypesind illegal. In einer Erklärung wie dieser:

typedef Type *Type_ptr;

Wir definieren einen Zeiger auf unseren Typ. Also, wenn wir erklären:

Type_ptr var1, var2;
struct Tag *myTagType1, myTagType2;

dann var1, var2und myTagType1sind Zeiger aber auf Typ myTagType2nicht.

In dem oben erwähnten Buch wird erwähnt, dass Typedefing-Strukturen nicht sehr nützlich sind, da sie den Programmierer nur vor dem Schreiben der Wortstruktur bewahren. Ich habe jedoch einen Einwand, wie viele andere C-Programmierer. Obwohl es manchmal dazu führt, dass einige Namen verschleiert werden (deshalb ist es in großen Codebasen wie dem Kernel nicht ratsam), wenn Sie Polymorphismus in C implementieren möchten, hilft es hier sehr, nach Details zu suchen . Beispiel:

typedef struct MyWriter_t{
    MyPipe super;
    MyQueue relative;
    uint32_t flags;
...
}MyWriter;

du kannst tun:

void my_writer_func(MyPipe *s)
{
    MyWriter *self = (MyWriter *) s;
    uint32_t myFlags = self->flags;
...
}

Sie können also durch Casting auf ein äußeres flagsElement ( MyPipe) über die innere Struktur ( ) zugreifen . Für mich ist es weniger verwirrend, den gesamten Typ zu besetzen, als (struct MyWriter_ *) s;jedes Mal, wenn Sie solche Funktionen ausführen möchten. In diesen Fällen ist eine kurze Referenzierung eine große Sache, insbesondere wenn Sie die Technik in Ihrem Code stark einsetzen.

Zum Schluss noch der letzte Aspekt mit typedef ed-Typen die Unfähigkeit, sie im Gegensatz zu Makros zu erweitern. Wenn Sie zum Beispiel haben:

#define X char[10] or
typedef char Y[10]

Sie können dann deklarieren

unsigned X x; but not
unsigned Y y;

Dies ist für Strukturen nicht wirklich wichtig, da es nicht für Speicherspezifizierer ( volatileund const) gilt.

user1533288
quelle
1
MyPipe *s; MyWriter *self = (MyWriter *) s;und Sie haben gerade striktes Aliasing gebrochen.
Jonathon Reinhart
@JonathonReinhart Es wäre anschaulich zu erwähnen, wie dies vermieden werden kann, zum Beispiel, wie das äußerst besetzungsfreudige GTK + darum herum
underscore_d
"typedef führt einen Alias ​​für einen Typ ein und findet ihn im Tag-Namespace." typedef struct Tag{ ...members... }Type; "Definiert zwei Dinge" ist nicht ganz sinnvoll. Wenn typedef Tags definiert, sollte 'Type' hier auch ein Tag sein. Die Wahrheit ist , die Definition definiert 2 - Tags und 1 - Typen (oder 2 Typen und 1 Tag nicht sicher.): struct Tag, TagUnd Type. struct Tagist definitiv ein Typ. Tagist ein Tag. aber die Verwirrung ist, ob Typees sich um ein Tag oder einen Typ handelt
Qwertyzw
12

Ich denke nicht, dass Vorwärtsdeklarationen mit typedef überhaupt möglich sind. Die Verwendung von struct, enum und union ermöglicht das Weiterleiten von Deklarationen, wenn Abhängigkeiten (bekannt) bidirektional sind.

Stil: Die Verwendung von typedef in C ++ ist sehr sinnvoll. Dies kann fast erforderlich sein, wenn Vorlagen verwendet werden, für die mehrere und / oder variable Parameter erforderlich sind. Das typedef hilft dabei, die Benennung gerade zu halten.

Nicht so in der Programmiersprache C. Die Verwendung von typedef dient meist keinem anderen Zweck, als die Verwendung der Datenstruktur zu verschleiern. Da nur {struct (6), enum (4), union (5)} Anzahl von Tastenanschlägen verwendet wird, um einen Datentyp zu deklarieren, wird das Aliasing der Struktur fast nicht verwendet. Ist dieser Datentyp eine Union oder eine Struktur? Wenn Sie die einfache, nicht typdefinierte Deklaration verwenden, wissen Sie sofort, um welchen Typ es sich handelt.

Beachten Sie, wie Linux unter strikter Vermeidung dieses Aliasing-Unsinns geschrieben wird, den typedef mit sich bringt. Das Ergebnis ist ein minimalistischer und klarer Stil.

natersoz
quelle
10
Sauber würde sich nicht structüberall wiederholen ... Typedefs machen neue Typen. Was benutzt du? Typen. Es ist uns egal, ob es sich um eine Struktur, eine Vereinigung oder eine Aufzählung handelt, deshalb tippen wir sie ein.
GManNickG
13
Nein, es ist uns egal, ob es sich um eine Struktur oder Vereinigung handelt, im Gegensatz zu einer Aufzählung oder einem atomaren Typ. Sie können eine Struktur nicht zu einer Ganzzahl oder einem Zeiger (oder zu einem anderen Typ) zwingen, was manchmal alles ist, was Sie zum Speichern eines Kontexts benötigen. Wenn Sie die Schlüsselwörter "struct" oder "union" verwenden, wird die Lokalität des Denkens verbessert. Niemand sagt, dass Sie wissen müssen, was sich in der Struktur befindet.
Bernd Jendrissek
1
@BerndJendrissek: Strukturen und Gewerkschaften unterscheiden sich von anderen Typen, aber sollte sich der Client-Code darum kümmern, welches dieser beiden Dinge (Struktur oder Vereinigung) so etwas wie a FILEist?
Supercat
4
@supercat FILE ist eine gute Verwendung von typedef. Ich denke, dass typedef überbeansprucht wird , nicht dass es eine Fehlfunktion der Sprache ist. IMHO mit typedef für alles ist der Code-Geruch "spekulative Übergeneralität". Beachten Sie, dass Sie Variablen als FILE * foo deklarieren, niemals als FILE foo. Für mich ist das wichtig.
Bernd Jendrissek
2
@supercat: "Wenn dateikennende Variablen vom Typ DATEI statt vom Typ DATEI * wären ..." Aber genau das ist die Mehrdeutigkeit, die typedefs ermöglichen! Wir sind es nur gewohnt, eine DATEI * zu öffnen, damit wir uns nicht darüber ärgern, aber jedes Mal, wenn Sie typedef hinzufügen, führen Sie einen weiteren kognitiven Overhead ein: Will diese API foo_t args oder foo_t *? Das explizite Mitführen der 'Struktur' verbessert die Lokalität des Denkens, wenn dies auf Kosten einiger weiterer Zeichen pro Funktionsdefinition geht.
Bernd Jendrissek
4

Beginnen wir mit den Grundlagen und arbeiten uns nach oben.

Hier ist ein Beispiel für die Strukturdefinition:

struct point
  {
    int x, y;
  };

Hier ist der Name pointoptional.

Eine Struktur kann während oder nach ihrer Definition deklariert werden.

Deklarieren während der Definition

struct point
  {
    int x, y;
  } first_point, second_point;

Nach Definition deklarieren

struct point
  {
    int x, y;
  };
struct point first_point, second_point;

Beachten Sie nun sorgfältig den letzten Fall oben; Sie müssen schreiben struct point, um Strukturen dieses Typs zu deklarieren, wenn Sie diesen Typ zu einem späteren Zeitpunkt in Ihrem Code erstellen möchten.

Geben Sie ein typedef. Wenn Sie beabsichtigen, zu einem späteren Zeitpunkt in Ihrem Programm eine neue Struktur (Struktur ist ein benutzerdefinierter Datentyp) mit demselben Entwurf zu erstellen, ist die Verwendung typedefwährend der Definition möglicherweise eine gute Idee, da Sie einige Eingaben in Zukunft sparen können.

typedef struct point
  {
    int x, y;
  } Points;

Points first_point, second_point;

Ein Wort der Vorsicht bei der Benennung Ihres benutzerdefinierten Typs

Nichts hindert Sie daran, das Suffix _t am Ende Ihres benutzerdefinierten Typnamens zu verwenden, aber der POSIX-Standard behält sich die Verwendung des Suffixes _t zur Bezeichnung von Standardbibliothekstypnamen vor.

Als ob
quelle
3

Der Name, den Sie (optional) der Struktur geben, wird als Tag-Name bezeichnet und ist, wie bereits erwähnt, kein Typ für sich. Um zum Typ zu gelangen, ist das Strukturpräfix erforderlich.

Abgesehen von GTK + bin ich mir nicht sicher, ob der Tagname so häufig wie ein Typedef für den Strukturtyp verwendet wird. In C ++ wird dies erkannt, und Sie können das Schlüsselwort struct weglassen und den Tagnamen auch als Typnamen verwenden:


    struct MyStruct
    {
      int i;
    };

    // The following is legal in C++:
    MyStruct obj;
    obj.i = 7;

philsquared
quelle
1

typedef stellt keinen co-abhängigen Satz von Datenstrukturen bereit. Dies kann man mit typdef nicht machen:

struct bar;
struct foo;

struct foo {
    struct bar *b;
};

struct bar {
    struct foo *f;
};

Natürlich können Sie immer hinzufügen:

typedef struct foo foo_t;
typedef struct bar bar_t;

Was genau ist der Sinn davon?

natersoz
quelle
1

A> a typdef hilft bei der Bedeutung und Dokumentation eines Programms, indem aussagekräftigere Synonyme für Datentypen erstellt werden . Darüber hinaus helfen sie bei der Parametrisierung eines Programms gegen Portabilitätsprobleme (K & R, S. 147, C prog lang).

B> Eine Struktur definiert einen Typ . Structs ermöglicht die bequeme Gruppierung einer Sammlung von Variablen zur Vereinfachung der Handhabung (K & R, S. 127, C prog lang.) Als eine Einheit

Das Schreiben einer Struktur wird in A oben erläutert.

D> Für mich sind Strukturen benutzerdefinierte Typen oder Container oder Sammlungen oder Namespaces oder komplexe Typen, während ein typdef nur ein Mittel ist, um mehr Spitznamen zu erstellen.

JamesAD-0
quelle
0

Es stellt sich heraus, dass in C99 typedef erforderlich ist. Es ist veraltet, aber viele Tools (ala HackRank) verwenden c99 als reine C-Implementierung. Und dort ist typedef erforderlich.

Ich sage nicht, dass sie sich ändern sollten (vielleicht zwei C-Optionen), wenn sich die Anforderung ändert. Diejenigen von uns, die für Interviews auf der Website studieren, wären SOL.

Matthew Corey Brown
quelle
2
"Es stellt sich heraus, dass C99 typedeferforderlich ist." Was meinst du?
Julien Lopez
Die Frage geht um C, nicht C++. In Ctypedefs sind 'erforderlich' (und werden es höchstwahrscheinlich immer sein). 'Erforderlich' wie in, Sie können keine Variable deklarieren Point varName;und der Typ muss auch struct Point;ohne sein typedef struct Point Point;.
Yyny
0

In der Programmiersprache 'C' wird das Schlüsselwort 'typedef' verwendet, um einen neuen Namen für ein Objekt (Struktur, Array, Funktion .. Aufzählungstyp) zu deklarieren. Zum Beispiel werde ich ein 'struct-s' verwenden. In 'C' deklarieren wir oft eine 'Struktur' außerhalb der 'Haupt'-Funktion. Zum Beispiel:

struct complex{ int real_part, img_part }COMPLEX;

main(){

 struct KOMPLEKS number; // number type is now a struct type
 number.real_part = 3;
 number.img_part = -1;
 printf("Number: %d.%d i \n",number.real_part, number.img_part);

}

Jedes Mal, wenn ich mich für die Verwendung eines Strukturtyps entscheide, benötige ich dieses Schlüsselwort 'struct' Something '' name '.' Typedef 'benennt diesen Typ einfach um und ich kann diesen neuen Namen jedes Mal in meinem Programm verwenden, wenn ich möchte. Unser Code lautet also:

typedef struct complex{int real_part, img_part; }COMPLEX;
//now COMPLEX is the new name for this structure and if I want to use it without
// a keyword like in the first example 'struct complex number'.

main(){

COMPLEX number; // number is now the same type as in the first example
number.real_part = 1;
number.img)part = 5;
printf("%d %d \n", number.real_part, number.img_part);

}

Wenn Sie ein lokales Objekt (Struktur, Array, wertvoll) haben, das in Ihrem gesamten Programm verwendet wird, können Sie ihm einfach mit einem 'typedef' einen Namen geben.

RichardGeerify
quelle
-2

In der C-Sprache sind struct / union / enum Makrobefehle, die vom C-Sprach-Präprozessor verarbeitet werden (verwechseln Sie nicht den Präprozessor, der "#include" und andere behandelt).

damit :

struct a
{
   int i;
};

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

Struktur b wird wie folgt ausgegeben:

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

und so entwickelt es sich zur Kompilierungszeit auf dem Stapel wie folgt: b: int ai int i int j

das ist auch der Grund, warum es schwierig ist, selbstreferierende Strukturen zu haben, C-Präprozessor-Runde in einer Deklarationsschleife, die nicht beendet werden kann.

typedef sind Typspezifizierer, das heißt, nur der C-Compiler verarbeitet sie und kann sie so ausführen, wie er es für die Optimierung der Assembler-Code-Implementierung wünscht. Es verbraucht auch kein Mitglied vom Typ par dumm wie ein Vorprozessor mit Strukturen, sondern verwendet einen komplexeren Referenzkonstruktionsalgorithmus, also Konstruktion wie:

typedef struct a A; //anticipated declaration for member declaration

typedef struct a //Implemented declaration
{
    A* b; // member declaration
}A;

ist erlaubt und voll funktionsfähig. Diese Implementierung bietet auch Zugriff auf die Konvertierung des Compilatortyps und beseitigt einige Fehlereffekte, wenn der Ausführungsthread das Anwendungsfeld der Initialisierungsfunktionen verlässt.

Dies bedeutet, dass in C typedefs eher als C ++ - Klasse als als einsame Strukturen sind.

doccpu
quelle
3
Es ist überhaupt nicht schwierig, selbstreferenzielle Strukturen zu haben. struct foo {struct foo * next; int Sache; }
Bernd Jendrissek
4
...Was? Es ist schon schlimm genug, Präprozessor zu sagen , um die Auflösung von structsund typedefs zu beschreiben , aber der Rest Ihres Schreibens ist so verwirrend, dass ich es schwierig finde, eine Nachricht davon zu bekommen. Eine Sache, die ich jedoch sagen kann, ist, dass Ihre Vorstellung, dass ein Nicht- typedefD structnicht vorwärts deklariert oder als undurchsichtiges (Zeiger-) Mitglied verwendet werden kann, völlig falsch ist. In Ihrem ersten Beispiel struct bkann trivial ein struct a *, nicht typedeferforderlich enthalten. Die Behauptungen, dass structs nur dumme Teile der Makroexpansion sind und typedefihnen revolutionäre neue Kräfte verleihen, sind schmerzlich falsch
underscore_d