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 von cptr a, b; macht nicht dasselbe wie typedef char *cptr; gefolgt von cptr a, b; . Mit der #define , b ist ein einfacher char variable, aber es ist auch ein Zeiger mit dem typedef .

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.

C99

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 .



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow