Warum verwenden C-Programmierer Typedefs, um grundlegende Typen umzubenennen?

Lesezeit: 8 Minuten

Ich bin also weit davon entfernt, ein C-Experte zu sein, aber etwas stört mich an Code, den ich schon lange lese: Kann mir jemand erklären, warum C(++)-Programmierer Typedefs verwenden, um einfache Typen umzubenennen? Ich verstehe, warum Sie sie für Strukturen verwenden würden, aber was genau ist der Grund für Deklarationen, die ich sehe

typedef unsigned char uch;
typedef uch UBYTE;
typedef unsigned long ulg;
typedef unsigned int u32;
typedef signed short s16;

Gibt es einen Vorteil, der mir nicht klar ist (ein Programmierer, dessen Erfahrung mit Java beginnt und der sich nicht weit über streng typsichere Sprachen hinaus gewagt hat)? Weil ich mir keinen Grund dafür vorstellen kann – es sieht so aus, als würde es den Code für Leute, die mit dem Projekt nicht vertraut sind, weniger lesbar machen.

Fühlen Sie sich frei, mich wie einen C-Neuling zu behandeln, ich weiß ehrlich gesagt sehr wenig darüber und es ist wahrscheinlich, dass es Dinge gibt, die ich von Anfang an falsch verstanden habe. 😉

  • Sollte man sich anschauen stdint.h . Sie werden auch verwendet, wenn sich der Typ von etwas vom System unterscheiden kann, der Code jedoch kompatibel bleiben muss.

    – Nategans

    27. Juli 2010 um 20:39 Uhr

Das Umbenennen von Typen, ohne ihre exponierte Semantik/Eigenschaften zu ändern, macht nicht viel Sinn. In deinem Beispiel

typedef unsigned char uch;
typedef unsigned long ulg;

gehören zu dieser Kategorie. Ich sehe keinen Sinn, abgesehen davon, einen kürzeren Namen zu machen.

Aber diese

typedef uch UBYTE;
typedef unsigned int u32;
typedef signed short s16;

sind eine ganz andere Geschichte. Zum Beispiel, s16 steht für “signed 16 bit type”. Diese Art ist nicht unbedingt signed short. Welcher spezifische Typ wird sich dahinter verstecken s16 ist plattformabhängig. Programmierer führen diese zusätzliche Ebene der Namensumleitung ein, um die Unterstützung für mehrere Plattformen zu vereinfachen. Wenn auf einer anderen Plattform ein signierter 16-Bit-Typ vorliegt signed intmuss der Programmierer nur eine typedef-Definition ändern. UBYTE steht anscheinend für einen vorzeichenlosen Maschinenbytetyp, was nicht unbedingt der Fall ist unsigned char.

Es ist erwähnenswert, dass die C99-Spezifikation bereits eine Standardnomenklatur für integrale Typen mit bestimmter Breite bereitstellt, wie z int16_t, uint32_t usw. Auf Plattformen, die C99 nicht unterstützen, ist es wahrscheinlich sinnvoller, sich an diese Standard-Namenskonvention zu halten.

  • +1 für die Empfehlung, die Standardnamen zu verwenden, aber diplomatischer zu sein, als ich es tun würde. 🙂

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    27. Juli 2010 um 6:07 Uhr

  • Haha, @R.., du hast mir ein Lächeln ins Gesicht gezaubert. 🙂

    – Prof. Falken

    5. April 2011 um 5:47 Uhr

Dies ermöglicht eine Portabilität. Beispielsweise benötigen Sie einen vorzeichenlosen 32-Bit-Ganzzahltyp. Welcher Standardtyp ist das? Sie wissen es nicht – es ist die Implementierung definiert. Deshalb du typedef einen separaten Typ als 32-Bit-Ganzzahl ohne Vorzeichen und verwenden Sie den neuen Typ in Ihrem Code. Wenn Sie auf einer anderen C-Implementierung kompilieren müssen, ändern Sie einfach die typedefs.

  • Diese Verwendung ist veraltet, da C dies getan hat uint32_t usw.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    27. Juli 2010 um 5:23 Uhr

  • @R: Nur wenn Ihr Compiler mit der Implementierung von C99 begonnen hat. Zuletzt habe ich nachgesehen, Microsoft zum Beispiel nicht.

    – Nikolaus Ritter

    27. Juli 2010 um 5:43 Uhr

  • Ein Compiler muss kaum C99 implementieren müssen inttypes.h und stdint.h. Wenn sie Ihrem Compiler fehlen, fügen Sie einfach Ersatz mit den richtigen Definitionen für Ihre Zielarchitektur ein. Es gibt keine Entschuldigung für die Verwendung von nicht standardmäßigen Namen.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    27. Juli 2010 um 6:07 Uhr

  • @R: In diesem Fall stellen Sie Typedefs sowieso selbst bereit. Ihr Ansatz ist ratsam, unterscheidet sich jedoch nicht wesentlich vom eigenständigen Hinzufügen von Typedefs zu Ihrem Projekt.

    – Nikolaus Ritter

    27. Juli 2010 um 6:10 Uhr

  • @R: uint32_t ist eine Typedef von stdint.h, daher ist die Verwendung kaum veraltet. Es ist lediglich standardisiert und für Sie erledigt.

    – JeremyP

    27. Juli 2010 um 12:42 Uhr

Manchmal wird es verwendet, um eine unhandliche Sache wie zu reduzieren volatile unsigned long etwas Kompakteres wie z vuint32_t.

In anderen Fällen soll es bei der Portabilität helfen, da Typen wie int sind nicht immer auf jeder Plattform gleich. Durch die Verwendung einer Typedef können Sie die Speicherklasse, an der Sie interessiert sind, auf die beste Übereinstimmung der Plattform setzen, ohne den gesamten Quellcode zu ändern.

  • Hinzu kommt, dass es in C++ sogar so weit gehen kann, einen möglicherweise nativen Typ unter der Haube (wie Float) auf einer Plattform durch eine ganze Klasse zu ersetzen, die einen Float emuliert (wie für Fixed Point, on eine Plattform ohne Schwimmer)

    – matt

    27. Juli 2010 um 5:06 Uhr

  • Obwohl POSIX Typnamen reserviert, die mit enden _t.

    – Traumlax

    27. Juli 2010 um 6:25 Uhr

Es gibt viele Gründe dafür. Was ich denke ist:

  1. Typename wird kürzer und damit auch Code kleiner und besser lesbar.
  2. Aliasing-Effekt für längere Strukturnamen.
  3. Konvention, die in bestimmten Teams / Unternehmen / Stilen verwendet wird.
  4. Portierung – Haben denselben Namen für alle Betriebssysteme und Maschinen. Die native Datenstruktur kann etwas anders sein.

Es folgt ein Zitat aus The C Programming Language (K&R)

Neben rein ästhetischen Aspekten gibt es zwei Hauptgründe für die Verwendung von Typedefs.

Erstens, um ein Programm zu parametrisieren

Die erste besteht darin, ein Programm gegen Portabilitätsprobleme zu parametrisieren. Wenn Typedefs für Datentypen verwendet werden, die möglicherweise maschinenabhängig sind, müssen nur die Typedefs geändert werden, wenn das Programm verschoben wird.

Eine übliche Situation besteht darin, typedef-Namen für verschiedene ganzzahlige Mengen zu verwenden und dann für jeden Hostcomputer eine geeignete Auswahl von short, int und long zu treffen. Beispiele sind Typen wie size_t und ptrdiff_t aus der Standardbibliothek.

Die kursiv gedruckten Teile sagen uns, dass Programmierer typedef Grundtyp für Portabilität. Wenn ich sicherstellen möchte, dass mein Programm auf verschiedenen Plattformen funktioniert und verschiedene Compiler verwende, werde ich versuchen, sicherzustellen, dass seine Portabilität auf jede erdenkliche Weise und typedef Ist einer von ihnen.

Als ich anfing, mit dem Turbo C-Compiler auf der Windows-Plattform zu programmieren, bekamen wir die Größe von int 2. Als ich zur Linux-Plattform und zum GCC-Compiler wechselte, erhalte ich eine Größe von 4. Wenn ich ein Programm mit Turbo C entwickelt hätte, das sich auf die Behauptung stützte, dass sizeof( int ) ist immer zwei, hätte es nicht richtig auf meine neue Plattform portiert.

Ich hoffe es hilft.

Das folgende Zitat von K&R bezieht sich nicht auf Ihre Anfrage, aber ich habe es der Vollständigkeit halber ebenfalls gepostet.

Zweitens, um eine bessere Dokumentation bereitzustellen

Der zweite Zweck von typedefs besteht darin, eine bessere Dokumentation für ein Programm bereitzustellen – ein Typ namens Treeptr ist möglicherweise leichter zu verstehen als einer, der nur als Zeiger auf eine komplizierte Struktur deklariert ist.

Die meisten dieser Muster sind schlechte Praktiken, die durch das Lesen und Kopieren von vorhandenem fehlerhaftem Code entstehen. Oft spiegeln sie Missverständnisse darüber wider, was C tut oder nicht erfordert.

  1. Ist ähnlich #define BEGIN { außer es spart etwas Tipparbeit, anstatt mehr zu machen.
  2. Ist ähnlich #define FALSE 0. Wenn Ihre Vorstellung von “Byte” die kleinste adressierbare Einheit ist, char ist per Definition ein Byte. Wenn Ihre Vorstellung von “Byte” ein Oktett ist, dann entweder char der Oktetttyp ist, oder Ihre Maschine hat keinen Oktetttyp.
  3. Ist wirklich hässliche Abkürzung für Leute, die nicht tippen können …
  4. Ist ein Fehler. Es sollte sein typedef uint32_t u32; oder noch besser, uint32_t sollte nur direkt verwendet werden.
  5. Ist dasselbe wie 4. Ersetzen uint32_t mit int16_t.

Bitte setzen Sie ein “als schädlich angesehen„Stempel auf sie alle. typedef sollte verwendet werden, wenn Sie wirklich einen neuen Typ erstellen müssen, dessen Definition sich im Laufe des Lebenszyklus Ihres Codes ändern könnte, oder wenn der Code auf andere Hardware portiert wird, nicht weil Sie denken, dass C mit anderen Typnamen “schöner” wäre.

Wir verwenden es, um es projekt-/plattformspezifisch zu machen, alles hat eine gemeinsame Namenskonvention

pname_int32, pname_uint32, pname_uint8 — pname ist Projekt-/Plattform-/Modulname

Und einige #definiert

pname_malloc, pname_strlen

Es ist einfacher zu lesen und verkürzt lange Datentypen wie unsigned char auf pname_uint8 und macht es auch zu einer Konvention für alle Module.

Beim Portieren müssen Sie nur die einzelne Datei ändern, was das Portieren vereinfacht.

  • Warum brauchen Sie Ihren eigenen Ersatz für alles, was die Standardbibliothek bereits bietet?

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    27. Juli 2010 um 5:22 Uhr

  • @ R.. Ist das in Ordnung? #define pname_free(ptr) free(ptr) #define pname_safe_free(ptr) \ if(ptr!=NULL) \ { pname_free((void*) ptr); \ ptr =NULL;\ } Wie sieht es auch mit OEM-Funktionen aus? Es sieht nicht so lesbar aus, wenn Sie das obige Makro ersetzen und andere Standardnamen beibehalten. Darf wieder seine individuelle Meinung sein.

    – Praveen S

    27. Juli 2010 um 5:45 Uhr

  • @R .. Microsoft-spezifische Schlüsselwörter sind __int8, __int16 usw. Wie würden Sie Code für den Microsoft C-Compiler schreiben, der sogar unter Linux mit gcc kompiliert wird? Macht das Sinn? #ifdef(_MSC_VER) typedef unsigned __int8 PNAME_INT #elif definiert(__GNUC) typedef unsigned int8_t PNAME_INT #endif

    – Praveen S

    27. Juli 2010 um 5:55 Uhr

  • @Praveen, auf kaputten Compilern, denen das Recht fehlt Standard -Typen, stellen Sie einfach einen Drop-In-Header bereit, der sie mit den Standardnamen versorgt. Es besteht keine Notwendigkeit für hässlich PNAME_ Umweltverschmutzung. Übrigens deine pname_safe_free Makro ist kaputt. In Betracht ziehen: if (foo) pname_safe_free(foo); else ...

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    27. Juli 2010 um 6:03 Uhr

  • Ändern Sie es in if (bar) wenn es dich glücklicher macht. Der Punkt ist, dass Sie einen Compiler-Fehler bei erhalten else. Funktionsähnliche Makros sollten noch nie Verpackung sein {}. Entweder verwenden ((ptr)?(free((ptr)),(ptr)=0):0) oder fügen Sie den Code ein do { ... } while(0).

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    27. Juli 2010 um 6:11 Uhr

1411960cookie-checkWarum verwenden C-Programmierer Typedefs, um grundlegende Typen umzubenennen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy