C Language
typedef
Sök…
Introduktion
typedef
mekanismen gör det möjligt att skapa alias för andra typer. Det skapar inte nya typer. Människor använder ofta typedef
att förbättra portabiliteten för kod, för att ge alias till struktur- eller facktyper eller för att skapa alias för funktionstyp (eller funktionspekare).
I C-standarden klassificeras typedef
som en "lagringsklass" för bekvämlighet; det inträffar syntaktiskt där lagringsklasser som static
eller extern
kan visas.
Syntax
- typedef existensnamn aliasnamn;
Anmärkningar
Nackdelar med Typedef
typedef
kan leda till förorening av namnområdet i stora C-program.
Nackdelar med Typedef-strukturer
Dessutom är typedef
d strukturer utan etikettnamn en viktig orsak till onödigt påläggning av beställningsrelationer mellan sidfiler.
Överväga:
#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
Med en sådan definition, utan att använda typedefs
, är det möjligt för en sammanställningsenhet att inkludera foo.h
att få FOO_DEF
definitionen. Om den inte försöker ta bort bar
i foo
strukturen kommer det inte att behöva inkludera filen bar.h
Typedef vs #define
#define
är ett C-förprocessordirektiv som också används för att definiera alias för olika datatyper som liknar typedef
men med följande skillnader:
typedef
är begränsat till att ge symboliska namn på typer endast där#define
kan användas för att definiera alias för värden också.typedef
tolkning utförs av kompilatorn medan#define
uttalanden behandlas av#define
.Observera att
#define cptr char *
följt avcptr a, b;
gör inte samma sak somtypedef char *cptr;
följt avcptr a, b;
. Med#define
ärb
en vanligchar
variabel, men det är också en pekare medtypedef
.
Typedef för strukturer och fackföreningar
Du kan ge alias namn till en struct
:
typedef struct Person {
char name[32];
int age;
} Person;
Person person;
Jämfört med det traditionella sättet att deklarera strukturer, behöver inte programmerare ha struct
varje gång de förklarar en instans av den strukturen.
Observera att namnet Person
(i motsats till struct Person
) inte definieras förrän det sista semikolonet. För länkade listor och trädstrukturer som måste innehålla en pekare till samma strukturtyp måste du således använda antingen:
typedef struct Person {
char name[32];
int age;
struct Person *next;
} Person;
eller:
typedef struct Person Person;
struct Person {
char name[32];
int age;
Person *next;
};
Användningen av en typedef
för en union
typ är mycket lika.
typedef union Float Float;
union Float
{
float f;
char b[sizeof(float)];
};
En struktur som liknar denna kan användas för att analysera byte som utgör ett float
.
Enkla användningar av Typedef
För att ge korta namn på en datatyp
Istället för:
long long int foo;
struct mystructure object;
man kan använda
/* write once */
typedef long long ll;
typedef struct mystructure mystruct;
/* use whenever needed */
ll foo;
mystruct object;
Detta minskar mängden maskinskrivning som behövs om typen används många gånger i programmet.
Förbättra portabiliteten
Datatypens attribut varierar mellan olika arkitekturer. Till exempel kan en int
vara en 2-byte-typ i en implementering och en 4-byte-typ i en annan. Anta att ett program måste använda en 4-bytyp för att köra korrekt.
I en implementering, låt storleken på int
vara 2 byte och den på long
vara 4 byte. I en annan, låt storleken på int
vara 4 byte och den på long
vara 8 byte. Om programmet skrivs med den andra implementeringen,
/* program expecting a 4 byte integer */
int foo; /* need to hold 4 bytes to work */
/* some code involving many more ints */
För att programmet ska köras i den första implementeringen måste alla int
deklarationer ändras till long
.
/* program now needs long */
long foo; /*need to hold 4 bytes to work */
/* some code involving many more longs - lot to be changed */
För att undvika detta kan man använda 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 */
Sedan typedef
bara typedef
varje gång istället för att granska hela programmet.
<stdint.h>
och den relaterade <inttypes.h>
rubriken definierar standardtypnamn (med typedef
) för heltal i olika storlekar, och dessa namn är ofta det bästa valet i modern kod som behöver heltal med fast storlek. Till exempel är uint8_t
en osignerad 8-bitars heltalstyp; int64_t
är en signerad 64-bitars heltalstyp. Typen uintptr_t
är en osignerad heltalstyp som är tillräckligt stor för att hålla någon pekare att objektera. Dessa typer är teoretiskt frivilliga - men det är sällsynt att de inte är tillgängliga. Det finns varianter som uint_least16_t
(den minsta osignerade heltalstypen med minst 16 bitar) och int_fast32_t
(den snabbast signerade heltalstypen med minst 32 bitar). intmax_t
och uintmax_t
är också de största heltalstyperna som stöds av implementeringen. Dessa typer är obligatoriska.
För att ange en användning eller för att förbättra läsbarheten
Om en uppsättning data har ett särskilt syfte kan man använda typedef
att ge det ett meningsfullt namn. Dessutom, om egenskapen till data ändras så att bastypen måste ändras, måste bara typedef
uttalandet ändras i stället för att undersöka hela programmet.
Typedef för funktionspekare
Vi kan använda typedef
att förenkla användningen av funktionspekare. Föreställ dig att vi har några funktioner, alla med samma signatur, som använder deras argument för att skriva ut något på olika sätt:
#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 kan vi använda en typedef
att skapa en namngiven funktionspekartyp som heter skrivare:
typedef void (*printer_t)(int);
Detta skapar en typ med namnet printer_t
för en pekare till en funktion som tar ett enda int
argument och returnerar ingenting, vilket matchar signaturen för de funktioner vi har ovan. För att använda den skapar vi en variabel av den skapade typen och tilldelar den en pekare till en av funktionerna i fråga:
printer_t p = &print_to_n;
void (*p)(int) = &print_to_n; // This would be required without the type
För att anropa den funktion som funktionspekvariabeln pekar på:
p(5); // Prints 1 2 3 4 5 on separate lines
(*p)(5); // So does this
Således tillåter typedef
en enklare syntax när man hanterar funktionspekare. Detta blir tydligare när funktionspekare används i mer komplexa situationer, till exempel argument för funktioner.
Om du använder en funktion som tar en funktionspekare som en parameter utan en funktionspekertyp definierad skulle funktionsdefinitionen vara,
void foo (void (*printer)(int), int y){
//code
printer(y);
//code
}
Men med typedef
är det:
void foo (printer_t printer, int y){
//code
printer(y);
//code
}
På samma sätt kan funktioner returnera funktionspekare och återigen kan användningen av en typedef
göra syntaxen enklare när du gör det.
Ett klassiskt exempel är signal
från <signal.h>
. Förklaringen för den (från C-standarden) är:
void (*signal(int sig, void (*func)(int)))(int);
Det är en funktion som tar två argument - en int
och en pekare till en funktion som tar en int
som ett argument och returnerar ingenting - och som returnerar en pekare för att fungera som dess andra argument.
Om vi definierade en typ SigCatcher
som ett alias för pekaren att fungera:
typedef void (*SigCatcher)(int);
då kan vi deklarera signal()
med:
SigCatcher signal(int sig, SigCatcher func);
Sammantaget är detta lättare att förstå (även om C-standarden inte valde att definiera en typ för att göra jobbet). signal
tar två argument, en int
och en SigCatcher
, och den returnerar en SigCatcher
- där en SigCatcher
är en pekare till en funktion som tar ett int
argument och returnerar ingenting.
Även om att använda typedef
namn för pekaren för att fungera typer underlättar livet, kan det också leda till förvirring för andra som kommer att behålla din kod senare, så använd med försiktighet och korrekt dokumentation. Se även Funktionspekare .