Warum sollten wir ein typedef struct so oft in C?
Frage
Ich habe gesehen, viele Programme, bestehend aus Strukturen wie unten
typedef struct
{
int i;
char k;
} elem;
elem user;
Warum ist es notwendig, so oft?Alle spezifischen Grund oder anwendbarer Bereich?
Lösung
Wie Greg Hewgill sagte, die typedef bedeutet, dass Sie nicht mehr struct
ganz über den Platz zu schreiben. Das ist nicht nur Tastatureingaben speichert, kann sie auch den Code sauberer machen, da es eine ganz klein wenig mehr Abstraktion bereitstellt.
Stuff wie
typedef struct {
int x, y;
} Point;
Point point_new(int x, int y)
{
Point a;
a.x = x;
a.y = y;
return a;
}
Reiniger wird, wenn Sie brauchen, um die „Struktur“ Schlüsselwort nicht zu sehen, alle über den Ort, sieht es eher, als ob es wirklich eine Art „Point“ in Ihrer Sprache genannt wird. Was nach dem typedef
, ist der Fall, denke ich.
Beachten Sie auch, dass, während Ihr Beispiel (und mir) weggelassen sich die struct
nennen, eigentlich ist es Namensgebung für auch nützlich, wenn Sie eine undurchsichtige Art zur Verfügung stellen möchten. Dann würden Sie Codes wie diese im Header haben, zum Beispiel:
typedef struct Point Point;
Point * point_new(int x, int y);
und geben Sie 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;
}
In diesem letzteren Fall können Sie den Punkt nach Wert nicht zurück, da die Definition von Benutzern der Header-Datei versteckt ist. Dies ist eine Technik weit verbreitet in der GTK + zum Beispiel verwendet wird.
UPDATE Beachten Sie, dass es auch hoch angesehene C Projekte, bei denen diese Verwendung von typedef
struct
zu verstecken einer schlechten Idee betrachtet wird, ist das Linux-Kernel wahrscheinlich das bekannteste solches Projekt. Siehe Kapitel 5 von Der Linux-Kernel Coding Dokument für Linus' böse Worte. :) Mein Punkt ist, dass die „sollte“ in der Frage vielleicht nicht in Stein gemeißelt ist, nachdem alle.
Andere Tipps
Es ist erstaunlich, wie viele Menschen dies falsch. BITTE typedef nicht structs in C, es verpestet unnötig den globalen Namensraum, die in großen C-Programme bereits verschmutzt typischerweise sehr ist.
Auch typedef'd structs ohne Tag-Namen sind eine der Hauptursachen für unnötige Einführung von Beziehungen zwischen den Header-Dateien zu bestellen.
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 nicht typedefs verwenden, ist es möglich, dass ein compiland Einheit foo.h aufzunehmen an der FOO_DEF
Definition zu erhalten. Wenn es nicht versuchen, nicht dereferenzieren das ‚Bar‘ Mitglied der foo
struct dann wird es keine Notwendigkeit, die „bar.h“ Datei aufzunehmen.
Da auch die Namensräume unterschiedlich zwischen den Tag-Namen und den Mitgliedsnamen sind, ist es möglich, sehr lesbaren Code zu schreiben, wie zum Beispiel:
struct foo *foo;
printf("foo->bar = %p", foo->bar);
Da die Namensräume getrennt sind, gibt es keinen Konflikt in Variablen gleichzeitig mit ihrer Struktur Tag-Namen zu nennen.
Wenn ich Ihren Code zu erhalten, werde ich Ihre typedef'd structs entfernen.
Von einem alten Artikel von Dan Saks ( http://www.ddj.com/ ? CPP / 184403396 pgno = 3 ):
Die Sprache C Regeln für die Benennung structs sind ein wenig exzentrisch, aber sie sind ziemlich harmlos. Wenn jedoch erweitert, um Klassen in C ++, die gleichen Regeln öffnen kleinen Riss für Bugs kriechen durch.
In C s erscheint der Name in
struct s { ... };
ist ein Tag. Ein Tag-Name ist kein Typ Name. Gegeben über die Definition, Erklärungen wie
s x; /* error in C */ s *p; /* error in C */
Fehler in C. Sie müssen sie schreiben wie
struct s x; /* OK */ struct s *p; /* OK */
Die Namen der Gewerkschaften und Aufzählungen auch sind die Tags statt Typen.
In C-Tags unterscheiden sich von allen anderen Namen (für Funktionen, Typen, Variablen und Enumerationskonstanten). C-Compiler pflegen Tags in einem Symbol Tabelle, die vom Konzept her, wenn nicht ist physisch getrennt von dem Tisch dass hält alle anderen Namen. Also es Programm ist möglich, dass ein C zu haben sowohl einen Tag und ein anderer Name mit die gleiche Schreibweise im gleichen Umfang. Zum Beispiel:
struct s s;
ist eine gültige Erklärung, die erklärt, Variable s vom Typ struct s. Es kann seine nicht gute Praxis, aber C-Compiler muss es akzeptieren. Ich habe noch nie gesehen ein Gründe dafür, warum C dies wurde entwickelt, Weg. Ich habe immer gedacht, es war ein Fehler, aber da ist es.
Viele Programmierer (einschließlich Ihrer wirklich) lieber von struct Namen denken als Typennamen, so definieren sie einen Alias für den Tag eines typedef. Zum Beispiel definieren
struct s { ... }; typedef struct s S;
können Sie verwenden S anstelle von struct s, wie in
S x; S *p;
Ein Programm kann nicht als Name verwendet S von sowohl ein Typ und eine Variable (oder Funktion oder Enumerationskonstante):
S S; // error
Das ist gut.
Der Tag-Name in einer Struktur, Vereinigung oder Enum-Definition ist optional. Viele Programmierer falten Sie die Struktur Definition in die typedef und verzichten auf die Tag zusammen, wie in:
typedef struct { ... } S;
Der verlinkte Artikel hat auch eine Diskussion darüber, wie die C ++ Verhalten nicht typedef
requireing subtile Namen verstecken Probleme verursachen kann. Um diese Probleme zu vermeiden, ist es eine gute Idee, um Ihre Klassen und Strukturen in C ++ typedef
auch, obwohl auf den ersten Blick zu sein unnötig erscheint. In C ++, mit dem typedef
Versteck der Name ein Fehler geworden, dass der Compiler Sie eher als eine versteckte Quelle von möglichen Problemen erzählt.
ein typedef
Verwendung vermeidet jedes Mal, wenn Sie eine Variable dieses Typs deklarieren struct
schreiben:
struct elem
{
int i;
char k;
};
elem user; // compile error!
struct elem user; // this is correct
Ein weiterer guter Grund, immer typedef Aufzählungen und structs Ergebnisse dieses Problem:
enum EnumDef
{
FIRST_ITEM,
SECOND_ITEM
};
struct StructDef
{
enum EnuumDef MyEnum;
unsigned int MyVar;
} MyStruct;
Beachten Sie die Tippfehler in EnumDef in der Struktur (Enu u mDef)? Dies kompiliert ohne Fehler (oder Warnung) und ist (abhängig von der wörtlichen Auslegung des C Standard) korrekt. Das Problem ist, dass ich gerade eine neue (leere) Aufzählung Definition in meiner Struktur erstellt. Ich bin nicht (wie beabsichtigt) die vorherige Definition EnumDef verwendet wird.
Mit einer typdef ähnlichen Art von Fehlern in einem Compiler-Fehler ergeben hätte eine unbekannte Art für die Verwendung:
typedef
{
FIRST_ITEM,
SECOND_ITEM
} EnumDef;
typedef struct
{
EnuumDef MyEnum; /* compiler error (unknown type) */
unsigned int MyVar;
} StructDef;
StrructDef MyStruct; /* compiler error (unknown type) */
würde ich befürworten IMMER structs und Aufzählungen typedef'ing.
Nicht nur Tipparbeit zu sparen (kein Wortspiel beabsichtigt;).), Sondern weil es sicherer ist
Linux-Kernel Codierstil Kapitel 5 große Vor-und Nachteile gibt (vor allem Nachteile) von mit typedef
.
Bitte verwenden Sie keine Dinge wie "vps_t".
Es ist ein Fehler für Strukturen zu verwenden typedef und Zeiger. Wenn Sie sehen, ein
vps_t a;
in der Quelle, was es bedeutet?
Im Gegensatz dazu, wenn es sagt,
struct virtual_container *a;
Sie können tatsächlich sagen, was "a" ist.
Viele Leute denken, dass typedefs „Hilfe Lesbarkeit“. Nicht so. Sie sind nützlich, nur für:
(a) völlig undurchsichtige Objekte (wo die typedef aktiv verwendet wird verbergen , was das Objekt ist).
. Beispiel: „pte_t“ usw. undurchsichtige Objekte, dass Sie nur den Zugriff, die richtigen Zugriffsfunktionen mit
Hinweis! Undurchsichtigkeit und „Accessorfunktionen“ sind an sich nicht gut. Der Grund, warum wir sie für Dinge wie pte_t usw. ist, dass es wirklich absolut Null portably zugängliche Informationen geben.
(b) Klar Integer-Typen, wobei die Abstraktion hilft Verwirrung vermeiden, ob es "int" oder "lang" ist.
U8 / U16 / U32 sind völlig in Ordnung typedefs, obwohl sie in der Kategorie passen (d) besser als hier.
Hinweis! Auch hier - es muss ein Grund für diese sein. Wenn etwas „unsigned long“ ist, dann gibt es keinen Grund zu tun
typedef unsigned long myflags_t;
aber wenn es ein klarer Grund dafür, warum es unter bestimmten Umständen könnte ein „unsigned int“ und unter anderen Konfigurationen sein könnte „unsigned long“ sein, dann mit allen Mitteln voran gehen und eine typedef verwenden.
(c), wenn Sie spärlich verwenden, um buchstäblich zu erstellen neue Typ für Typ-Überprüfung.
(d) Neue Typen, die auf Standard-Typen C99 identisch sind, in bestimmten Ausnahmefällen.
Auch wenn es nur eine kurze Zeit für die Augen und das Gehirn zu den Standardtypen wie ‚uint32_t‘ zu gewöhnen nehmen würde, widersprechen einige Leute ihre Verwendung sowieso.
Daher ist die Linux-spezifischen ‚U8 / U16 / U32 / U64‘ Typen und deren unterzeichnet Äquivalente, die auf Standardtypen identisch sind, sind zulässig -. Obwohl sie in neuem Code der eigenen ist nicht obligatorisch
Wenn die Code Bearbeitung vorhandener, die bereits verwendet den einen oder anderen Satz von Typen, sollten Sie in diesem Code zu den bestehenden Möglichkeiten entsprechen.
(e) Art sicher für den Einsatz in User-Space.
In bestimmten Strukturen, die auf Anwenderseite sichtbar sind, können wir nicht C99 Typen erfordern und die obige Form ‚u32‘ nicht verwenden können. So verwenden wir __u32 und ähnliche Arten in allen Strukturen, die mit dem Anwender gemeinsam genutzt werden.
Vielleicht gibt es andere Fälle auch, aber die Regel sollte grundsätzlich sein, NIEMALS einen typedef zu verwenden, wenn Sie eindeutig eine dieser Regeln entsprechen können.
Im Allgemeinen wird ein Zeiger oder eine Struktur, die Elemente hat, die vernünftigerweise direkt zugegriffen werden sollte nie ein typedef sein.
Es stellt sich heraus, dass es vor-und Nachteile.Eine nützliche Quelle der Informationen ist die bahnbrechenden Buch "Expert C Programming" (Kapitel 3).Kurz, in C haben Sie mehrere namespaces: tags, Typen -, member-Namen und Identifikatoren. typedef
stellt einen alias für eine Typ-und findet es in der tag-namespace.Nämlich,
typedef struct Tag{
...members...
}Type;
definiert zwei Dinge.Ein Tag in der tag-namespace und ein Typ in den Typ namespace.So können Sie beides tun Type myType
und struct Tag myTagType
.Erklärungen wie struct Type myType
oder Tag myTagType
sind illegal.Darüber hinaus, In einer Erklärung wie diese:
typedef Type *Type_ptr;
wir definieren einen Zeiger auf unsere Art.Also, wenn wir erklären:
Type_ptr var1, var2;
struct Tag *myTagType1, myTagType2;
dann var1
,var2
und myTagType1
Zeiger auf Typ, aber myTagType2
nicht.
In dem oben genannten Buch, es wird erwähnt, dass typedefing Strukturen sind nicht sehr nützlich, es speichert nur die Programmierer zu schreiben die Wort-Struktur.Allerdings habe ich einen Einspruch-wie viele andere C-Programmierer.Obwohl es manchmal dreht sich zu verschleiern einige Namen (, dass ist, warum es ist nicht ratsam, in großen code-Basen, wie den kernel), wenn Sie wollen zur Umsetzung Polymorphismus in C hilft viel schauen Sie hier für details.Beispiel:
typedef struct MyWriter_t{
MyPipe super;
MyQueue relative;
uint32_t flags;
...
}MyWriter;
können Sie tun:
void my_writer_func(MyPipe *s)
{
MyWriter *self = (MyWriter *) s;
uint32_t myFlags = self->flags;
...
}
So können Sie auf einen äußeren Mitglied (flags
), die von der inneren Struktur (MyPipe
) durch Gießen.Für mich ist es weniger verwirrend zu wirken, die ganze Art, als das zu tun (struct MyWriter_ *) s;
jedes mal, wenn Sie ausführen möchten, diese Funktionalität.In diesen Fällen kurze Referenzierung ist eine große Sache, besonders wenn Sie stark beschäftigen die Technik in Ihrem code.
Schließlich der Letzte Aspekt mit typedef
ed-Arten ist die Unfähigkeit, Sie zu verlängern, im Gegensatz zu Makros.Wenn Sie beispielsweise:
#define X char[10] or
typedef char Y[10]
Sie können dann erklären
unsigned X x; but not
unsigned Y y;
Wir nicht wirklich wichtig, für diese Strukturen, weil es gilt nicht für die speicherbezeichner (volatile
und const
).
Ich glaube nicht vorwärts Erklärungen mit typedef auch möglich sind. Die Verwendung von struct, enum, und Vereinigung ermöglicht Forwarding Erklärungen, wenn Abhängigkeiten (kennen) ist bidirektional.
Stil: Die Verwendung von typedef in C ++ macht ziemlich viel Sinn. Es kann fast notwendig sein, wenn mit Vorlagen zu tun, die mehrere und / oder variablen Parameter erfordern. Die typedef hilft die Namensgebung gerade zu halten.
Nicht so in der C-Programmiersprache. Die Verwendung von typedef meisten dient oft keinen Zweck, sondern die Datenstruktur Nutzung zu verschleiern. Da nur {struct (6), ENUM (4), Vereinigung (5)} Anzahl der Tastenanschläge verwendet werden, einen Datentyp zu erklären, gibt es fast keine Verwendung für das Aliasing der Struktur. Ist das Datum, die eine Vereinigung oder eine Struktur geben? die einfache Nicht-typdefed Erklärung verwenden, können Sie sofort wissen, welche Art es ist.
Beachten Sie, wie Linux mit strikter Vermeidung dieses Aliasing Unsinn geschrieben wird typedef bringt. Das Ergebnis ist ein minimalistischer und sauberer Stil.
Lassen Sie uns mit den Grundlagen beginnen und uns auf den Weg arbeiten.
Hier ist ein Beispiel für Strukturdefinition:
struct point
{
int x, y;
};
Hier wird der Name point
optional.
Eine Struktur kann während seiner Definition oder nach deklariert werden.
bei der Definition Deklarieren
struct point
{
int x, y;
} first_point, second_point;
Deklarieren nach Definition
struct point
{
int x, y;
};
struct point first_point, second_point;
Nun, beachten Sie sorgfältig den letzten Fall oben; Sie müssen struct point
schreiben Strukturen dieser Art zu erklären, wenn Sie diese Art zu einem späteren Zeitpunkt in Ihrem Code erstellen entscheiden.
Geben Sie typedef
. Wenn Sie beabsichtigen, neue Struktur zu schaffen (Struktur ist ein benutzerdefinierter Daten-Typ) zu einem späteren Zeitpunkt in Ihrem Programm des gleichen Bauplan verwenden, typedef
während seiner Definition verwenden könnte eine gute Idee sein, da Sie etwas Tipp vorwärts bewegt speichern.
typedef struct point
{
int x, y;
} Points;
Points first_point, second_point;
Ein Wort der Vorsicht, während der benutzerdefinierte Typ
NamensgebungNichts hindert Sie daran, _t Suffix am Ende des benutzerdefinierten Typnamen aber POSIX-Standard behält sich die Verwendung von Suffix _t zu bezeichnen Standardbibliothek Typnamen verwendet wird.
der Name, den Sie (optional), um die Struktur geben die Tag-Namen genannt und, wie erwähnt, ist kein Typ für sich. Um auf die Art zu erhalten erfordert die Struktur Präfix.
GTK + beiseite, ich bin nicht sicher, dass der Tag-Name etwas verwendet wird, wie so häufig als typedef auf den Struktur-Typen, so in C ++, die erkannt werden, und Sie können das Schlüsselwort struct wegzulassen und die tagname als Typname verwenden:
struct MyStruct
{
int i;
};
// The following is legal in C++:
MyStruct obj;
obj.i = 7;
typedef wird kein co-abhängige Satz von Datenstrukturen zur Verfügung stellen. Das kann man nicht mit typdef tun:
struct bar;
struct foo;
struct foo {
struct bar *b;
};
struct bar {
struct foo *f;
};
Natürlich kann man immer hinzufügen:
typedef struct foo foo_t;
typedef struct bar bar_t;
Was genau ist der Punkt, der das?
A> ein typdef Hilfsmittel im Sinne und Dokumentation eines Programms, indem Schaffung sinnvoller Synonyme für Datentypen . Darüber hinaus helfen sie ein Programm gegen Portabilitätsprobleme (K & R, pg147, C prog lang) parametriert werden.
B> definiert eine Struktur mit einer Art . Structs ermöglicht eine bequeme Gruppierung einer Sammlung von Vars zur Vereinfachung der Handhabung (K & R, pg127, C prog lang.) Als eine einzige Einheit
C> typedef'ing eine Struktur ist in einer oben erläutert.
D> Für mich structs sind benutzerdefinierte Typen oder Container oder Sammlungen oder Namespaces oder komplexe Typen, während ein typdef nur ein Mittel ist mehr Spitznamen zu erstellen.
Es stellte sich heraus in C99 typedef erforderlich ist. Es ist veraltet, aber eine Menge von Werkzeugen (ala HackRank) verwendet c99 als reine C-Implementierung. Und typedef ist erforderlich.
Ich sage nicht, sollten sie ändern (vielleicht zwei C-Optionen haben), wenn die Anforderung geändert, würden die von uns beschlagen für Interviews auf der Website SOL sein.
In ‚C‘ Programmiersprache das Schlüsselwort ‚typedef‘ wird verwendet, um einen neuen Namen für ein Objekt (struct, array, function..enum Typ) zu erklären. Zum Beispiel werde ich ein ‚struct-s‘ verwenden. In ‚C‘ erklären wir oft einen ‚struct‘ außerhalb der ‚main‘ 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 entscheiden, einen Strukturtyp verwenden, ich muss dieses Schlüsselwort ‚Struktur‚etwas‘‚name‘.‘ Typedef‘wird einfach diese Art umbenennen und ich den neuen Namen in meinem Programm möchte jedes Mal verwenden kann ich. So ist unser Code wird sein:
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 einige lokale Objekt haben (struct, Array, wertvoll), die Sie einfach in Ihrem gesamten Programm verwendet wird geben Sie einen Namen ein ‚typedef‘ verwenden.
Bei allen, in C-Sprache, Struktur / Union / Enum ist Makrobefehl durch die Sprache C Präprozessor verarbeitet wird (nicht mit dem Präprozessor Fehler, dass „# include“ und andere behandeln)
so:
struct a
{
int i;
};
struct b
{
struct a;
int i;
int j;
};
struct b wird als so etwas wie dieses aufgewandt:
struct b
{
struct a
{
int i;
};
int i;
int j;
}
und so, bei der Kompilierung auf Stapel als etwas entwickeln, wie: b: int ai int i int j
das auch, warum es dificult selfreferent structs, C Präprozessor Runde in einem déclaration Schleife zu haben, die nicht beendet werden kann.
typedef Typspezifizierer ist, bedeutet, dass nur verarbeiten C-Compiler und es tun kann, wie er für optimize Assembler-Code Implementierung möge. Es nicht auch Mitglied des Typ par aufwenden dummerweise wie Preprocessor mit structs zu tun, aber komplexen Referenzkonstruktionsalgorithmus verwenden, so Konstruktion wie:
typedef struct a A; //anticipated declaration for member declaration
typedef struct a //Implemented declaration
{
A* b; // member declaration
}A;
ist permited und voll funktionsfähig. Diese Implementierung geben auch Zugriff auf Typumwandlung und entfernen Sie einige Lausch Effekte, wenn die Ausführung Thread verlassen das Anwendungsfeld der Initialisierung Funktionen compilator.
Dies bedeutet, dass in C typedefs ist in der Nähe wie C ++ Klasse als einsames structs.