typedef - typedef

typedef je vyhrazené klíčové slovo v programovacích jazycích C a C ++ . Používá se k vytvoření dalšího názvu ( aliasu ) pro jiný datový typ , ale nevytváří nový typ, s výjimkou obskurního případu kvalifikovaného typedefu typu pole, kde jsou kvalifikovaní typedef přeneseni na typ prvku pole. Jako takový se často používá ke zjednodušení syntaxe deklarování složitých datových struktur skládajících se ze strukturních a sjednocujících typů , ale je stejně běžný při poskytování specifických popisných názvů typů pro celočíselné datové typy různých délek .

Syntax

Syntaxe deklarace typedef je:

typedef prohlášení o typu ;

Název nového aliasu typu se řídí stejnou syntaxí jako deklarace jakéhokoli jiného identifikátoru C, proto v podrobnější podobě:

typedef identifikátor definice typu

Ve standardní knihovně C a ve specifikacích POSIX je často definován identifikátor definice typedef _t, například ve size_t a time_t . Toto se praktikuje v jiných kódovacích systémech, ačkoli POSIX výslovně vyhrazuje tento postup pro datové typy POSIX .

Příklady

typedef int length;

Tím se vytvoří typ lengthjako synonymum typu int.

Použití dokumentace

Deklaraci typedef lze použít jako dokumentaci uvedením významu proměnné v kontextu programování, např. Může zahrnovat vyjádření měrné jednotky nebo počtu. Obecná prohlášení,

int current_speed;
int high_score;

void congratulate(int your_score) {
    if (your_score > high_score) {
        // ...
    }
}

lze vyjádřit prohlášením kontextově specifických typů:

typedef int km_per_hour;
typedef int points;

// `km_per_hour` is synonymous with `int` here, and thus, the compiler treats
// our new variables as integers.
km_per_hour current_speed;
points high_score;

void congratulate(points your_score) {
    if (your_score > high_score) {
        // ...
    }
}

Obě části kódu se provádějí shodně. Použití deklarací typedef ve druhém bloku kódu však jasně ukazuje, že obě proměnné, zatímco představují stejný datový typ int, ukládají různá nebo nekompatibilní data. Definice v congratulate()of your_scoreudává pro programátora tohoto current_speed(nebo jakékoliv jiné proměnné nejsou deklarované jako points) by neměly být předán jako argument. To by nebylo tak zjevné, kdyby byly obě deklarovány jako proměnné intdatového typu. Indikace je však pouze pro programátora ; kompilátor C / C ++ považuje obě proměnné za typy inta neoznačuje varování ohledně neshod typu ani chyby pro „nesprávné“ typy argumentů pro congratulate(points your_score)v níže uvedeném fragmentu kódu:

void foo() {
    km_per_hour km100 = 100;
    congratulate(km100);
}

Zjednodušení typu

Typedef lze použít ke zjednodušení deklarace složeného typu ( struktura , sjednocení ) nebo typu ukazatele . Například,

struct MyStruct {
    int data1;
    char data2;
};

To definuje datový typ struct MyStruct. Deklarace proměnné tohoto typu v jazyce C také vyžaduje klíčové slovo struct, ale v C ++ může být vynecháno:

struct MyStruct a;

Deklarace typu typef vylučuje požadavek na specifikaci structv C. Například deklarace

typedef struct MyStruct newtype;

se snižuje na:

newtype a;

Deklaraci struktury a typedef lze také zkombinovat do jednoho příkazu:

typedef struct MyStruct {
    int data1;
    char data2;
} newtype;

Nebo jej lze použít následovně:

typedef struct {
    int data1;
    char data2;
} newtype;

V C ++ , na rozdíl od C, klíčová slova struct, classa enumjsou volitelné v deklarace proměnných, které jsou oddělené od definic, pokud není nejednoznačnost k jiným identifikátorem:

struct MyStruct x;
MyStruct y;

Jako takový MyStructlze použít kdekoli newtype. Opak však není pravdivý; například nelze konstruktorové metody pro MyStructpojmenovat newtype.

Notoricky známý příklad, kdy i C ++ potřebuje structklíčové slovo, je systémové volání POSIX stat, které ve svých argumentech používá strukturu stejného jména:

int stat(const char *filename, struct stat *buf)
{
    // ...
}

Zde C i C ++ potřebují structklíčové slovo v definici parametru.

Ukazatele

Typedef může být použit k definování nového typu ukazatele.

typedef int *intptr;

intptr ptr;

// Same as:
// int *ptr;

intptrje nový alias s typem ukazatele int *. Definice intptr ptr;definuje proměnnou ptrs typem int *. Takže, ptrje ukazatel, který může ukazovat na proměnné typu int.

Použití typedef k definování nového typu ukazatele může někdy vést k nejasnostem. Například:

typedef int *intptr;

// Both 'cliff' and 'allen' are of type int*.
intptr cliff, allen;

// 'cliff2' is of type int*, but 'allen2' is of type int**.
intptr cliff2, *allen2;

// Same as:
// intptr cliff2;
// intptr *allen2;

Výše intptr cliff, allen;znamená definování 2 proměnných s int*typem pro obě. Důvodem je, že typ definovaný typedef je typ, nikoli expanze. Jinými slovy, intptrcož je int*typ, zdobí obě cliffa allen. Pro intptr cliff2, *allen2;je intptrtyp byly opatřeny cliff2i *allen2. Je tedy intptr cliff2, *allen2;ekvivalentní dvěma samostatným definicím intptr cliff2;a intptr *allen2. intptr *allen2znamená, že allen2jde o ukazatel ukazující na paměť int*typu. Krátce, allen2má typ int**.

Struktury a ukazatele struktury

Typedefs může také zjednodušit definice nebo deklarace pro typy ukazatelů struktury . Zvaž toto:

struct Node {
    int data;
    struct Node *nextptr;
};

Pomocí typedef lze výše uvedený kód přepsat takto:

typedef struct Node Node;

struct Node {
    int data;
    Node *nextptr;
};

V jazyce C lze deklarovat více proměnných stejného typu v jediném příkazu, dokonce i míchání struktury s ukazatelem nebo ukazateli. Pro označení každé proměnné jako ukazatele je však nutné předponu označit hvězdičkou. V následujícím textu může programátor předpokládat, že errptrto skutečně bylo Node *, ale typografická chyba znamená, že errptrje to Node. To může vést k jemným syntaktickým chybám.

struct Node *startptr, *endptr, *curptr, *prevptr, errptr, *refptr;

Definováním typedef Node *je zajištěno, že všechny proměnné jsou typy ukazatelů struktury, nebo řekněme, že každá proměnná je typ ukazatele ukazující na typ struktury .

typedef struct Node* NodePtr;

NodePtr startptr, endptr, curptr, prevptr, errptr, refptr;

Ukazatele funkcí

int do_math(float arg1, int arg2) {
    return arg2;
}

int call_a_func(int (*call_this)(float, int)) {
    int output = call_this(5.5, 7);

    return output;
}

int final_result = call_a_func(&do_math);

Předchozí kód může být přepsán specifikacemi typedef:

typedef int (*MathFunc)(float, int);

int do_math(float arg1, int arg2) {
    return arg2;
}

int call_a_func(MathFunc call_this) {
    int output = call_this(5.5, 7);

    return output;
}

int final_result = call_a_func(&do_math);

Zde MathFuncje nový alias pro typ. A MathFuncje ukazatel na funkci, která vrací celé číslo a jako argumenty bere float následovaný celým číslem.

Když funkce vrací ukazatel funkce , může být bez typedef ještě matoucí. Následuje funkční prototyp signálu (3) z FreeBSD :

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

Deklarace funkce výše je záhadná, protože jasně neukazuje, co funkce přijímá jako argumenty, ani typ, který vrátí. Začínající programátor může dokonce předpokládat, že funkce přijme singl intjako svůj argument a nevrátí nic, ale ve skutečnosti také potřebuje ukazatel funkce a vrátí jiný ukazatel funkce. Dá se to napsat čistěji:

typedef void (*sighandler_t)(int);

sighandler_t signal(int sig, sighandler_t func);

Pole

Typedef lze také použít ke zjednodušení definice typů polí. Například,

typedef char arrType[6];

arrType arr = {1, 2, 3, 4, 5, 6};
arrType *pArr;

// Same as:
// char arr[6] = {1, 2, 3, 4, 5, 6};
// char (*pArr)[6];

Zde arrTypeje nový alias char[6]typu, což je typ pole se 6 prvky. Pro arrType *pArr;, pArrje ukazatel ukazující na paměť char[6]typu.

Zadejte odlitky

Typedef je vytvořen pomocí syntaxe definice typu, ale lze jej použít, jako by byl vytvořen pomocí syntaxe přetypování typu . ( Přetypování změní datový typ.) Například v každém řádku za prvním řádkem:

// `funcptr` is a pointer to a function which takes a `double` and returns an `int`.
typedef int (*funcptr)(double);

// Valid in both C and C++.
funcptr x = (funcptr) NULL;

// Only valid in C++.
funcptr y = funcptr(NULL);
funcptr z = static_cast<funcptr>(NULL);

funcptrse používá na levé straně k deklaraci proměnné a na pravé straně se používá k seslání hodnoty. Typedef tedy mohou používat programátoři, kteří nechtějí přijít na to, jak převést syntaxi definice na syntaxi typu cast.

Bez typedef není obecně možné používat syntaxi definic a syntaxi cast zaměnitelně. Například:

void *p = NULL;

// This is legal.
int (*x)(double) = (int (*)(double)) p;

// Left-hand side is not legal.
int (*)(double) y = (int (*)(double)) p;

// Right-hand side is not legal.
int (*z)(double) = (int (*p)(double));

Použití v C ++

V jazyce C ++ mohou být názvy typů složité a typedef poskytuje mechanismus k přiřazení jednoduchého názvu typu.

std::vector<std::pair<std::string, int>> values;

for (std::vector<std::pair<std::string, int>>::const_iterator i = values.begin(); i != values.end(); ++i)
{
    std::pair<std::string, int> const & t = *i;

    // ...
}

a

typedef std::pair<std::string, int> value_t;
typedef std::vector<value_t> values_t;

values_t values;

for (values_t::const_iterator i = values.begin(); i != values.end(); ++i)
{
    value_t const & t = *i;

    // ...
}

C ++ 11 představil možnost vyjádřit typedefs s usingmísto typedef. Například výše uvedené dva typové definice lze ekvivalentně zapsat jako

using value_t = std::pair<std::string, int>;
using values_t = std::vector<value_t>;

Použijte se šablonami

C ++ 03 neposkytuje šablony typedefs. Například mají stringpair<T>představovat std::pair<std::string, T>pro každý typ Tjeden nemůže používat:

template<typename T>
typedef std::pair<std::string, T> stringpair<T>; // Doesn't work

Pokud je však člověk ochoten přijmout stringpair<T>::typemísto stringpair<T>, pak je možné dosáhnout požadovaného výsledku pomocí typedef v jinak nepoužívané šablonované třídě nebo struktuře:

template<typename T>
class stringpair
{
private:
    // Prevent instantiation of `stringpair<T>`.
    stringpair();
public:
    // Make `stringpair<T>::type` represent `std::pair<std::string, T>`.
    typedef std::pair<std::string, T> type;
};

// Declare a variable of type `std::pair<std::string, int>`.
stringpair<int>::type my_pair_of_string_and_int;

V C ++ 11 se přidávají šablony s typovými definicemi s následující syntaxí, která vyžaduje usingklíčové slovo, nikoli typedefklíčové slovo. (Viz aliasy šablon .)

template <typename T>
using stringpair = std::pair<std::string, T>;

// Declare a variable of type `std::pair<std::string, int>`.
stringpair<int> my_pair_of_string_and_int;

Jiné jazyky

V SystemVerilog se typedef chová přesně tak, jak se chová v C a C ++.

V mnoha staticky zadaných funkčních jazycích, jako jsou Haskell , Miranda , OCaml atd., Lze definovat synonyma typu , která jsou stejná jako typedefs v C. Příklad v Haskell:

type PairOfInts = (Int, Int)

Tento příklad definoval synonymum typu PairOfIntsjako celočíselný typ.

V Seed7 se definice konstantního typu používá k zavedení synonyma pro typ:

const type: myVector is array integer;

V Swiftu se pomocí typealiasklíčového slova vytvoří typedef:

typealias PairOfInts = (Int, Int)

C # obsahuje funkci, která je podobná typedef nebo usingsyntaxi C ++.

using newType = global::System.Runtime.Interop.Marshal;
using otherType = Enums.MyEnumType;
using StringListMap = System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<string>>;

V D klíčové slovo aliasumožňuje vytvářet synonyma typu nebo částečného typu.

struct Foo(T){}
alias FooInt = Foo!int;
alias Fun = int delegate(int);

Obavy z používání

Kernighan a Ritchie uvedli dva důvody pro použití překlepu. Nejprve poskytuje prostředky, díky nimž je program přenosnější nebo snadnější na údržbu. Místo toho, abyste museli měnit typ v každém vzhledu ve zdrojových souborech programu, je třeba změnit pouze jeden příkaz typedef. size_t a ptrdiff_t v <stdlib.h> jsou takové názvy typedef. Zadruhé, definice typu může usnadnit pochopení složité definice nebo deklarace.

Někteří programátoři se staví proti rozsáhlému používání typedefů. Většina argumentů se soustředí na myšlenku, že typedefs jednoduše skryjí skutečný datový typ proměnné. Například Greg Kroah-Hartman , hacker a dokumentátor linuxového jádra , odrazuje od jejich použití pro cokoli kromě deklarací funkčních prototypů. Tvrdí, že tato praxe nejen zbytečně zamlžuje kód, ale může také způsobit, že programátoři omylem zneužijí velké struktury a budou je považovat za jednoduché typy.

Viz také

Reference