Zoeken…


Invoering

Met het typedef mechanisme kunnen aliassen voor andere typen worden gemaakt. Er worden geen nieuwe typen gemaakt. Mensen gebruiken vaak typedef om de overdraagbaarheid van code te verbeteren, om aliassen te geven aan structuur- of typedef , of om aliassen te maken voor functie (of functie pointer) types.

In de C-standaard is typedef voor het gemak geclassificeerd als een 'opslagklasse'; het gebeurt syntactisch waar opslagklassen zoals static of extern kunnen verschijnen.

Syntaxis

  • typedef bestaande_naam alias_naam;

Opmerkingen


Nadelen van Typedef

typedef kan leiden tot vervuiling van de naamruimte in grote C-programma's.

Nadelen van Typedef Structs

typedef 'd typedef zonder typedef zijn ook een belangrijke oorzaak van het onnodig opleggen van bestelrelaties tussen header-bestanden.

Overwegen:

#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

Met een dergelijke definitie, zonder typedefs , is het mogelijk voor een compilatie-eenheid om foo.h te nemen om bij de FOO_DEF definitie te komen. Als het niet probeert het lid van de bar van de foo struct af te leiden, hoeft het bestand bar.h opgenomen.

Typedef versus #define

#define is een C-processorprocessor die ook wordt gebruikt om de aliassen te definiëren voor verschillende gegevenstypen die lijken op typedef maar met de volgende verschillen:

  • typedef is beperkt tot het geven van symbolische namen aan typen, alleen als #define kan worden gebruikt om ook alias voor waarden te definiëren.

  • typedef interpretatie wordt uitgevoerd door de compiler terwijl #define statements worden verwerkt door de pre-processor.

  • Merk op dat #define cptr char * gevolgd door cptr a, b; doet niet hetzelfde als typedef char *cptr; gevolgd door cptr a, b; . Met #define , b is een duidelijke char variabele, maar ook een pointer met typedef .

Typedef voor structuren en vakbonden

U kunt aliasnamen geven aan een struct :

typedef struct Person {
    char name[32];
    int age;
} Person;

Person person;

In vergelijking met de traditionele manier om structs te declareren, zouden programmeurs geen struct moeten hebben elke keer dat ze een instantie van die struct declareren.

Merk op dat de naam Person (in tegenstelling tot struct Person ) niet is gedefinieerd tot de laatste puntkomma. Dus voor gekoppelde lijsten en boomstructuren die een pointer naar hetzelfde structuurtype moeten bevatten, moet u een van beide gebruiken:

typedef struct Person {
    char name[32];
    int age;
    struct Person *next;
} Person;

of:

typedef struct Person Person;

struct Person {
    char name[32];
    int age;
    Person *next;
};

Het gebruik van een typedef voor een union type is zeer vergelijkbaar.

typedef union Float Float;

union Float
{
    float f;
    char  b[sizeof(float)];
};

Een soortgelijke structuur kan worden gebruikt om de bytes te analyseren die een float .

Eenvoudig gebruik van Typedef

Voor het geven van korte namen aan een gegevenstype

In plaats van:

long long int foo;
struct mystructure object;

men kan gebruiken

/* write once */
typedef long long ll;
typedef struct mystructure mystruct;

/* use whenever needed */
ll foo;
mystruct object;

Dit vermindert de hoeveelheid typen die nodig is als het type vaak in het programma wordt gebruikt.

Verbetering van de draagbaarheid

De kenmerken van gegevenstypen variëren over verschillende architecturen. Een int kan bijvoorbeeld een type van 2 bytes zijn in de ene implementatie en een type van 4 bytes in een andere. Stel dat een programma een type van 4 bytes moet gebruiken om correct te kunnen worden uitgevoerd.

Laat in één implementatie de grootte van int 2 bytes zijn en die van long 4 bytes. In een andere, laat de grootte van int 4 bytes zijn en die van long 8 bytes. Als het programma is geschreven met de tweede implementatie,

/* program expecting a 4 byte integer */
int foo; /* need to hold 4 bytes to work */
/* some code involving many more ints */

Om het programma in de eerste implementatie uit te voeren, moeten alle int verklaringen te long worden gewijzigd.

/* program now needs long */
long foo; /*need to hold 4 bytes to work */
/* some code involving many more longs - lot to be changed */

Om dit te voorkomen, kan men 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 */

Dan zou alleen de typedef instructie elke keer moeten worden gewijzigd, in plaats van het hele programma te onderzoeken.

C99

De kop <stdint.h> en de bijbehorende kop <inttypes.h> definiëren standaardtypenamen (met typedef ) voor gehele getallen van verschillende groottes, en deze namen zijn vaak de beste keuze in moderne code die hele getallen met een vaste grootte nodig heeft. uint8_t is bijvoorbeeld een niet-ondertekend 8-bits geheel getal; int64_t is een 64-bits geheel getal met teken. Het type uintptr_t is een geheel getal zonder teken dat groot genoeg is om elke aanwijzer naar object te bevatten. Deze types zijn theoretisch optioneel - maar het is zeldzaam dat ze niet beschikbaar zijn. Er zijn varianten zoals uint_least16_t (het kleinste niet-ondertekende geheel getal met ten minste 16 bits) en int_fast32_t (het snelste ondertekende geheel getal met ten minste 32 bits). Ook zijn intmax_t en uintmax_t de grootste typen gehele getallen die door de implementatie worden ondersteund. Deze types zijn verplicht.

Een gebruik opgeven of de leesbaarheid verbeteren

Als een set gegevens een specifiek doel heeft, kan men typedef gebruiken om er een betekenisvolle naam aan te geven. Bovendien, als de eigenschap van de gegevens zodanig verandert dat het typedef moet veranderen, zou alleen de typedef instructie moeten worden gewijzigd, in plaats van het hele programma te onderzoeken.

Typedef voor functie-aanwijzers

We kunnen typedef gebruiken om het gebruik van functie-aanwijzers te vereenvoudigen. Stel je voor dat we enkele functies hebben, allemaal met dezelfde handtekening, die hun argument gebruiken om iets op verschillende manieren af te drukken:

#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);
}

Nu kunnen we een typedef gebruiken om een benoemde functie pointer type genaamd printer te maken:

typedef void (*printer_t)(int);

Dit maakt een type, genaamd printer_t voor een pointer naar een functie waarvoor een enkel int argument nodig is en niets retourneert, dat overeenkomt met de handtekening van de functies die we hierboven hebben. Om het te gebruiken, maken we een variabele van het gemaakte type en wijzen we het een aanwijzer toe aan een van de betreffende functies:

printer_t p = &print_to_n;
void (*p)(int) = &print_to_n; // This would be required without the type

Om vervolgens de functie aan te roepen waarnaar wordt verwezen door de variabele van de functiepointer:

p(5);           // Prints 1 2 3 4 5 on separate lines
(*p)(5);        // So does this

Het typedef maakt dus een eenvoudigere syntaxis mogelijk bij het omgaan met functiewijzers. Dit wordt duidelijker wanneer functie-aanwijzers worden gebruikt in meer complexe situaties, zoals argumenten voor functies.

Als u een functie gebruikt die een functiepointer als parameter neemt zonder dat een functiepointertype is gedefinieerd, zou de functiedefinitie zijn,

void foo (void (*printer)(int), int y){
    //code
    printer(y);
    //code
}

Met de typedef het echter:

void foo (printer_t printer, int y){
    //code
    printer(y);
    //code
}

Evenzo kunnen functies functieaanwijzingen retourneren en nogmaals, het gebruik van een typedef kan de syntaxis eenvoudiger maken.

Een klassiek voorbeeld is de signal van <signal.h> . De verklaring daarvoor (uit de C-norm) is:

void (*signal(int sig, void (*func)(int)))(int);

Dat is een functie waarvoor twee argumenten nodig zijn - een int en een pointer naar een functie die een int als argument neemt en niets retourneert - en die een pointer retourneert om te functioneren als zijn tweede argument.

Als we een type SigCatcher als een alias voor de aanwijzer om te functioneren:

typedef void (*SigCatcher)(int);

dan kunnen we signal() declareren met:

SigCatcher signal(int sig, SigCatcher func);

Over het algemeen is dit eenvoudiger te begrijpen (hoewel de C-norm er niet voor heeft gekozen een type te definiëren om het werk te doen). Voor de signal zijn twee argumenten nodig, een int en een SigCatcher , en deze retourneert een SigCatcher - waarbij een SigCatcher een aanwijzer is voor een functie die een int argument gebruikt en niets retourneert.

Hoewel het gebruik van typedef namen voor pointer naar functietypes het leven gemakkelijker maakt, kan het ook tot verwarring leiden voor anderen die uw code later zullen onderhouden, dus wees voorzichtig en gebruik de juiste documentatie. Zie ook Functiepointers .



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow