C Language
Typedef
Suche…
Einführung
Mit dem typedef
Mechanismus können Aliase für andere Typen erstellt werden. Es werden keine neuen Typen erstellt. Menschen verwenden typedef
häufig, um die Portabilität von Code zu verbessern, um Aliase für Struktur- oder typedef
zu vergeben oder um Aliase für Funktions- (oder Funktionszeiger-) Typen zu erstellen.
In der C-Norm wird typedef
als "Speicherklasse" klassifiziert. es tritt syntaktisch auf, wo Speicherklassen wie static
oder extern
könnten.
Syntax
- typedef Vorhandener_Name Alias_Name;
Bemerkungen
Nachteile von Typedef
typedef
kann in großen C-Programmen zur Verschmutzung des Namensraums führen.
Nachteile von Typedef-Strukturen
Auch typedef
'd-Strukturen ohne Tag-Namen sind eine Hauptursache für unnötige Auferlegung 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 nicht mit typedefs
, ist es möglich , dass eine Übersetzungseinheit umfassen foo.h
am bekommen FOO_DEF
Definition. Wenn nicht versucht wird, den bar
Member der foo
Struktur zu bar.h
Datei bar.h
nicht bar.h
werden.
Typedef vs #define
#define
ist eine C-Vorprozessor-Direktive, die auch zur Definition der Aliase für verschiedene Datentypen verwendet wird, die typedef
ähnlich sind, jedoch mit den folgenden Unterschieden:
typedef
kann nur Typen symbolische Namen geben, bei denen mit#define
auch Alias für Werte definiert werden kann.typedef
Interpretation wird vom Compiler ausgeführt, während die Anweisungen#define
vom Vorprozessor verarbeitet werden.Beachten Sie, dass
#define cptr char *
gefolgt voncptr a, b;
macht nicht dasselbe wietypedef char *cptr;
gefolgt voncptr a, b;
. Mit der#define
,b
ist ein einfacherchar
variable, aber es ist auch ein Zeiger mit demtypedef
.
Typedef für Strukturen und Vereinigungen
Sie können einer struct
Aliasnamen geben:
typedef struct Person {
char name[32];
int age;
} Person;
Person person;
Verglichen mit der traditionellen Methode zum Deklarieren von Strukturen müssen Programmierer nicht jedes Mal eine struct
haben, wenn sie eine Instanz dieser Struktur deklarieren.
Beachten Sie, dass der Name Person
(im Gegensatz zu struct Person
) erst beim abschließenden Semikolon definiert wird. Daher müssen Sie für verknüpfte Listen und Baumstrukturen, die einen Zeiger auf denselben Strukturtyp enthalten müssen, entweder Folgendes verwenden:
typedef struct Person {
char name[32];
int age;
struct Person *next;
} Person;
oder:
typedef struct Person Person;
struct Person {
char name[32];
int age;
Person *next;
};
Die Verwendung eines typedef
für eine union
Typ ist sehr ähnlich.
typedef union Float Float;
union Float
{
float f;
char b[sizeof(float)];
};
Eine ähnliche Struktur kann verwendet werden, um die Bytes zu analysieren, aus denen ein float
Wert besteht.
Einfache Verwendung von Typedef
Um einem Datentyp kurze Namen zu geben
Anstatt:
long long int foo;
struct mystructure object;
man kann verwenden
/* write once */
typedef long long ll;
typedef struct mystructure mystruct;
/* use whenever needed */
ll foo;
mystruct object;
Dies reduziert den erforderlichen Schreibaufwand, wenn der Typ mehrmals im Programm verwendet wird.
Portabilität verbessern
Die Attribute von Datentypen variieren je nach Architektur. Beispielsweise kann ein int
in einer Implementierung ein 2-Byte-Typ und in einer anderen ein 4-Byte-Typ sein. Angenommen, ein Programm muss einen 4-Byte-Typ verwenden, um ordnungsgemäß ausgeführt zu werden.
In einer Implementierung sei die Größe von int
2 Bytes und die long
von 4 Byte. In einem anderen Fall sei die Größe von int
4 Bytes und die long
von 8 Bytes. Wenn das Programm mit der zweiten Implementierung geschrieben wurde,
/* program expecting a 4 byte integer */
int foo; /* need to hold 4 bytes to work */
/* some code involving many more ints */
Damit das Programm in der ersten Implementierung ausgeführt werden kann, müssen alle int
Deklarationen in long
geändert werden.
/* program now needs long */
long foo; /*need to hold 4 bytes to work */
/* some code involving many more longs - lot to be changed */
Um dies zu vermeiden, kann man typedef
/* program expecting a 4 byte integer */
typedef int myint; /* need to declare once - only one line to modify if needed */
myint foo; /* need to hold 4 bytes to work */
/* some code involving many more myints */
Dann muss nur die typedef
Anweisung jedes Mal geändert werden, anstatt das gesamte Programm zu untersuchen.
Der Header <stdint.h>
und der zugehörige Header <inttypes.h>
definieren Standardtypnamen (mithilfe von typedef
) für Ganzzahlen verschiedener Größen. Diese Namen sind oft die beste Wahl in modernem Code, für den Ganzzahlen mit fester Größe erforderlich sind. Zum Beispiel ist uint8_t
ein vorzeichenloser 8-Bit-Integer-Typ. int64_t
ist ein 64-Bit-Integer-Typ mit Vorzeichen. Der Typ uintptr_t
ist ein vorzeichenloser Integer-Typ, der groß genug ist, um einen Zeiger auf ein Objekt aufzunehmen. Diese Typen sind theoretisch optional - sie sind jedoch selten verfügbar. Es gibt Varianten wie uint_least16_t
(der kleinste vorzeichenlose Integer-Typ mit mindestens 16 Bit) und int_fast32_t
(der am schnellsten vorzeichenbehaftete Integer-Typ mit mindestens 32 Bit). Außerdem sind intmax_t
und uintmax_t
die größten Integer-Typen, die von der Implementierung unterstützt werden. Diese Typen sind obligatorisch.
Zur Angabe einer Verwendung oder zur Verbesserung der Lesbarkeit
Wenn eine Gruppe von Daten einen bestimmten Zweck typedef
, können Sie mit typedef
einen aussagekräftigen Namen vergeben. Wenn die Eigenschaft der Daten sich so ändert, dass der Basistyp geändert werden muss, müsste außerdem nur die Anweisung typedef
geändert werden, anstatt das gesamte Programm zu untersuchen.
Typedef für Funktionszeiger
Wir können typedef
, um die Verwendung von Funktionszeigern zu vereinfachen. Stellen Sie sich vor, wir haben einige Funktionen, die alle dieselbe Signatur haben und deren Argument verwendet, um etwas auf unterschiedliche Weise auszudrucken:
#include<stdio.h>
void print_to_n(int n)
{
for (int i = 1; i <= n; ++i)
printf("%d\n", i);
}
void print_n(int n)
{
printf("%d\n, n);
}
Jetzt können wir mit einem typedef
einen benannten Funktionszeigertyp namens Drucker erstellen:
typedef void (*printer_t)(int);
Dadurch wird ein Typ mit dem Namen printer_t
für einen Zeiger auf eine Funktion erstellt, die ein einzelnes int
Argument printer_t
und nichts zurückgibt, was der Signatur der oben genannten Funktionen entspricht. Um es zu verwenden, erstellen wir eine Variable des erstellten Typs und weisen ihr einen Zeiger auf eine der fraglichen Funktionen zu:
printer_t p = &print_to_n;
void (*p)(int) = &print_to_n; // This would be required without the type
Dann rufen Sie die Funktion auf, auf die die Funktionszeigervariable zeigt:
p(5); // Prints 1 2 3 4 5 on separate lines
(*p)(5); // So does this
Somit erlaubt das typedef
eine einfachere Syntax im Umgang mit Funktionszeigern. Dies wird deutlicher, wenn Funktionszeiger in komplexeren Situationen verwendet werden, z. B. Argumente für Funktionen.
Wenn Sie eine Funktion verwenden, die einen Funktionszeiger als Parameter verwendet, ohne dass ein Funktionszeigertyp definiert ist, wäre die Funktionsdefinition
void foo (void (*printer)(int), int y){
//code
printer(y);
//code
}
Mit dem typedef
es jedoch:
void foo (printer_t printer, int y){
//code
printer(y);
//code
}
Ebenso können Funktionen Funktionszeiger zurückgeben, und die Verwendung eines typedef
kann dabei die Syntax vereinfachen.
Ein klassisches Beispiel ist die signal
von <signal.h>
. Die Deklaration dafür (aus dem C-Standard) lautet:
void (*signal(int sig, void (*func)(int)))(int);
Dies ist eine Funktion, die zwei Argumente int
- ein int
und einen Zeiger auf eine Funktion, die ein int
als Argument verwendet und nichts zurückgibt - und das einen Zeiger auf die Funktion wie sein zweites Argument zurückgibt.
Wenn wir einen Typ SigCatcher
als Alias für den Zeiger auf den Funktionstyp definiert haben:
typedef void (*SigCatcher)(int);
dann könnten wir signal()
deklarieren mit:
SigCatcher signal(int sig, SigCatcher func);
Im Großen und Ganzen ist dies einfacher zu verstehen (auch wenn der C-Standard sich nicht dafür entschieden hat, einen Typ zu definieren, der die Aufgabe erfüllt). Die signal
nimmt zwei Argumente, einen int
und einen SigCatcher
, und es gibt eine SigCatcher
- wo ein SigCatcher
einen Zeiger auf eine Funktion, die eine dauert int
Argument und gibt nichts.
Die Verwendung von typedef
Namen für Zeiger auf Funktionstypen macht das Leben zwar einfacher, kann aber auch für andere typedef
, die Ihren Code später verwalten werden. typedef
Sie daher mit Vorsicht und der richtigen Dokumentation vor. Siehe auch Funktionszeiger .