C Language
typedef
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 doorcptr a, b;
doet niet hetzelfde alstypedef char *cptr;
gevolgd doorcptr a, b;
. Met#define
,b
is een duidelijkechar
variabele, maar ook een pointer mettypedef
.
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.
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 .