Wir alle haben definitiv das eine oder andere Mal typedef
s und #define
s benutzt. Während ich heute mit ihnen zusammenarbeite, habe ich angefangen, über etwas nachzudenken.
Betrachten Sie die folgenden 2 Situationen, um einen int
Datentyp mit einem anderen Namen zu verwenden:
typedef int MYINTEGER
und
#define MYINTEGER int
Wie in der obigen Situation können wir in vielen Situationen mit #define sehr gut etwas erreichen und auch mit typedef das Gleiche tun, obwohl die Art und Weise, wie wir dasselbe tun, sehr unterschiedlich sein kann. #define kann auch MACRO-Aktionen ausführen, die ein typedef nicht ausführen kann.
Obwohl der Hauptgrund für ihre Verwendung der unterschiedliche ist, wie unterschiedlich ist ihre Arbeitsweise? Wann sollte einer dem anderen vorgezogen werden, wenn beide verwendet werden können? Auch ist einer in welchen Situationen garantiert schneller als der andere? (zB #define ist eine Präprozessor-Direktive, so dass alles viel früher als beim Kompilieren oder zur Laufzeit ausgeführt wird).
quelle
Antworten:
A
typedef
wird im Allgemeinen bevorzugt, es sei denn, es gibt einen merkwürdigen Grund, warum Sie speziell ein Makro benötigen.Makros ersetzen den Text, was der Semantik des Codes erheblichen Schaden zufügen kann. Zum Beispiel gegeben:
Sie könnten legal schreiben:
weil
short MYINTEGER
erweitert sich umshort int
.Auf der anderen Seite mit einem typedef:
Der Name
MYINTEGER
ist ein anderer Name für den Typint
und keine textuelle Ersetzung für das Schlüsselwort "int".Bei komplizierteren Typen wird es noch schlimmer. Zum Beispiel vorausgesetzt, dass:
a
,b
Undc
sind alle Zeiger, sondernd
einchar
, weil die letzte Zeile erweitert:das ist äquivalent zu
(Typedefs für Zeigertypen sind normalerweise keine gute Idee, aber dies veranschaulicht den Punkt.)
Ein weiterer merkwürdiger Fall:
quelle
typedef
, aber der richtige Weg , ein Makro zu erstellen , die etwas tut , mit dem, was folgt , ist Argumente zu verwenden:#define CHAR_PTR(x) char *x
. Dies führt zumindest dazu, dass der Compiler bei falscher Verwendung kvetch ausführt.const CHAR_PTR mutable_pointer_to_const_char; const char_ptr const_pointer_to_mutable_char;
Das mit Abstand größte Problem bei Makros ist, dass sie nicht über einen bestimmten Bereich verfügen. Dies allein rechtfertigt die Verwendung von typedef. Auch ist es semantisch klarer. Wenn jemand, der Ihren Code liest, eine Definition sieht, weiß er erst, worum es geht, wenn er sie liest und das gesamte Makro versteht. Ein typedef teilt dem Leser mit, dass ein Typenname definiert wird. (Ich sollte erwähnen, dass ich über C ++ spreche. Ich bin nicht sicher, ob Typedef für C geeignet ist, aber ich denke, es ist ähnlich).
quelle
Der Quellcode ist hauptsächlich für Mitentwickler geschrieben. Computer verwenden eine kompilierte Version davon.
In dieser Perspektive
typedef
trägt eine Bedeutung,#define
die nicht.quelle
Die Antwort von Keith Thompson ist sehr gut und sollte zusammen mit den zusätzlichen Punkten von Tamás Szelei zum Scoping den gesamten Hintergrund liefern, den Sie benötigen.
Sie sollten Makros immer als das allerletzte Mittel der Verzweiflung betrachten. Es gibt bestimmte Dinge, die Sie nur mit Makros tun können. Selbst dann sollten Sie lange und gründlich nachdenken, wenn Sie es wirklich wollen. Die Menge an Problemen beim Debuggen, die durch subtil defekte Makros verursacht werden können, ist beträchtlich und lässt Sie vorverarbeitete Dateien durchsehen. Es lohnt sich, dies nur einmal zu tun, um ein Gefühl für den Umfang des Problems in C ++ zu bekommen - allein die Größe der vorverarbeiteten Datei kann ein Augenöffner sein.
quelle
Zusätzlich zu allen gültigen Hinweisen zu Umfang und Textersetzung, die bereits gegeben wurden, ist #define auch nicht vollständig kompatibel mit typedef! Nämlich, wenn es um Funktionszeiger geht.
Dies deklariert einen Typ,
fptr_t
der ein Zeiger auf eine Funktion des Typs istvoid func(void)
.Sie können diesen Typ nicht mit einem Makro deklarieren.
#define fptr_t void(*)(void)
wird natürlich nicht funktionieren. Man müsste so etwas Obskures schreiben#define fptr_t(name) void(*name)(void)
, was in der C-Sprache, in der wir keine Konstruktoren haben, wirklich keinen Sinn ergibt.Array-Zeiger können auch nicht mit #define deklariert werden:
typedef int(*arr_ptr)[10];
Und während C keine nennenswerte Sprachunterstützung für die Typensicherheit bietet, ist ein anderer Fall, in dem typedef und #define nicht kompatibel sind, der, wenn Sie verdächtige Typenkonvertierungen durchführen. Der Compiler und / oder das Astatic Analyzer-Tool können möglicherweise Warnungen für solche Konvertierungen ausgeben, wenn Sie typedef verwenden.
quelle
char *(*(*foo())[])();
(foo
ist eine Funktion, die einen Zeiger auf ein Array von Zeigern auf Funktionen zurückgibt, auf die der Zeiger zurückgegeben wirdchar
).Verwenden Sie das Werkzeug mit der geringsten Leistung und den meisten Warnungen. #define wird im Präprozessor ausgewertet, Sie sind dort weitgehend alleine. typedef wird vom Compiler ausgewertet. Es werden Überprüfungen durchgeführt und typedef kann nur Typen definieren, wie der Name sagt. Entscheide dich also in deinem Beispiel definitiv für typedef.
quelle
Wie würden Sie, abgesehen von den normalen Argumenten gegen define, diese Funktion mit Makros schreiben?
Dies ist offensichtlich ein triviales Beispiel, aber diese Funktion kann über jede vorwärts iterierbare Sequenz eines Typs summiert werden, der die Addition * implementiert. Solche Typedefs sind ein wichtiges Element der generischen Programmierung .
* Ich habe das gerade hier geschrieben, ich lasse es als Übung für den Leser zusammenstellen :-)
BEARBEITEN:
Diese Antwort scheint viel Verwirrung zu stiften. Wenn Sie in die Definition eines STL-Vektors schauen, sehen Sie etwas Ähnliches wie das Folgende:
Die Verwendung von typedefs in den Standardcontainern ermöglicht es einer generischen Funktion (wie der oben erstellten), auf diese Typen zu verweisen. Die Funktion "Summe" richtet sich nach dem Typ des Containers (
std::vector<int>
) und nicht nach dem Typ, der sich im Container befindet (int
). Ohne typedef wäre es nicht möglich, auf diesen internen Typ zu verweisen.Daher sind typedefs von zentraler Bedeutung für Modern C ++, und dies ist mit Makros nicht möglich.
quelle
typedef
, nicht zu Makros vs Inline-Funktionen.typedef
passt zur C ++ Philosophie: alle möglichen Checks / Asserts in der Kompilierungszeit.#define
ist nur ein Präprozessor-Trick, der dem Compiler viel Semantik verbirgt. Sie sollten sich nicht mehr um die Kompilierungsleistung als um die Code-Korrektheit kümmern.Wenn Sie einen neuen Typ erstellen, definieren Sie ein neues "Ding", das die Domäne Ihres Programms manipuliert. Sie können dieses "Ding" also verwenden, um Funktionen und Klassen zu komponieren, und Sie können den Compiler zu Ihrem Vorteil verwenden, um statische Überprüfungen durchzuführen. Wie auch immer, da C ++ mit C kompatibel ist, gibt es eine Menge impliziter Konvertierungen zwischen
int
und int-basierten Typen, die keine Warnungen generieren. In diesem Fall erhalten Sie nicht die volle Leistung der statischen Überprüfungen. Sie können jedoch Ihretypedef
durch eine ersetzenenum
, um implizite Konvertierungen zu finden. ZB: Wenn Sie habentypedef int Age;
, können Sie durch ersetzen,enum Age { };
und Sie erhalten alle Arten von Fehlern durch implizite Konvertierungen zwischenAge
undint
.Eine andere Sache: die
typedef
kann in einem seinnamespace
.quelle
Die Verwendung von Defines anstelle von typedefs ist unter einem anderen wichtigen Aspekt problematisch, nämlich dem Konzept der Typmerkmale. Betrachten Sie verschiedene Klassen (denken Sie an Standardcontainer), die alle ihre spezifischen Typendefinitionen definieren. Sie können generischen Code schreiben, indem Sie auf die typedefs verweisen. Beispiele hierfür sind die allgemeinen Containeranforderungen (c ++ Standard 23.2.1 [container.requirements.general]) wie
All dies lässt sich mit einem Makro nicht generisch ausdrücken, da es keinen Gültigkeitsbereich hat.
quelle
Vergessen Sie nicht, welche Auswirkungen dies auf Ihren Debugger hat. Einige Debugger behandeln #define nicht sehr gut. Spielen Sie mit beiden in dem von Ihnen verwendeten Debugger. Denken Sie daran, Sie werden mehr Zeit damit verbringen, es zu lesen als zu schreiben.
quelle
"#define" ersetzt das, was Sie geschrieben haben, typedef erstellt den Typ. Wenn Sie also einen benutzerdefinierten Typ haben möchten, verwenden Sie typedef. Wenn Sie Makros benötigen, verwenden Sie define.
quelle