typedef - typedef

typedef é uma palavra-chave reservada nas linguagens de programação C e C ++ . É usado para criar um nome adicional ( alias ) para outro tipo de dados , mas não cria um novo tipo, exceto no caso obscuro de um typedef qualificado de um tipo de array, onde os qualificadores de typedef são transferidos para o tipo de elemento de array. Como tal, é freqüentemente usado para simplificar a sintaxe de declaração de estruturas de dados complexas consistindo em tipos de estrutura e união , mas é tão comum em fornecer nomes de tipo descritivos específicos para tipos de dados inteiros de comprimentos variados .

Sintaxe

A sintaxe da declaração de typedef é:

typedef declaração de tipo ;

O nome do novo alias de tipo segue a mesma sintaxe da declaração de qualquer outro identificador C, portanto, de forma mais detalhada:

typedef identificador de definição de tipo

Na biblioteca padrão C e nas especificações POSIX, o identificador para a definição de typedef geralmente é sufixado com _t, como em size_t e time_t . Isso é praticado em outros sistemas de codificação, embora POSIX reserve explicitamente essa prática para tipos de dados POSIX .

Exemplos

typedef int length;

Isso cria o tipo lengthcomo um sinônimo do tipo int.

Uso de documentação

Uma declaração de typedef pode ser usada como documentação, indicando o significado de uma variável dentro do contexto de programação, por exemplo, pode incluir a expressão de uma unidade de medida ou contagens. As declarações genéricas,

int current_speed;
int high_score;

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

pode ser expresso declarando tipos específicos de contexto:

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) {
        // ...
    }
}

Ambas as seções do código são executadas de forma idêntica. No entanto, o uso de declarações de typedef no segundo bloco de código deixa claro que as duas variáveis, embora representem o mesmo tipo de dados int, armazenam dados diferentes ou incompatíveis. A definição em congratulate()de your_scoreindica ao programador que current_speed(ou qualquer outra variável não declarada como a points) não deve ser passada como um argumento. Isso não seria tão aparente se ambos fossem declarados como variáveis ​​do inttipo de dados. No entanto, a indicação é apenas para o programador ; o compilador C / C ++ considera ambas as variáveis ​​como sendo do tipo inte não sinaliza avisos de incompatibilidade de tipo ou erros para tipos de argumento "errados" congratulate(points your_score)no trecho de código abaixo:

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

Simplificação de tipo

Um typedef pode ser usado para simplificar a declaração de um tipo composto ( estrutura , união ) ou tipo de ponteiro . Por exemplo,

struct MyStruct {
    int data1;
    char data2;
};

Isso define o tipo de dados struct MyStruct. Uma declaração de variável desse tipo em C também requer a palavra-chave struct, mas pode ser omitida em C ++:

struct MyStruct a;

Uma declaração typedef elimina o requisito de especificação structem C. Por exemplo, a declaração

typedef struct MyStruct newtype;

é reduzido a:

newtype a;

A declaração de estrutura e typedef também podem ser combinados em uma única instrução:

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

Ou pode ser usado da seguinte maneira:

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

Em C ++ , em contraste com C, as palavras-chave struct, classe enumsão opcionais em declarações de variáveis ​​separadas das definições, desde que não haja ambigüidade para outro identificador:

struct MyStruct x;
MyStruct y;

Como tal, MyStructpode ser usado onde quer que newtypepossa ser usado. Entretanto, o contrário não é verdade; por exemplo, os métodos do construtor de MyStructnão podem ser nomeados newtype.

Um exemplo notório em que até C ++ precisa da structpalavra-chave é a chamada de sistema POSIX stat que usa uma estrutura de mesmo nome em seus argumentos:

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

Aqui, tanto C quanto C ++ precisam da structpalavra - chave na definição do parâmetro.

Ponteiros

O typedef pode ser usado para definir um novo tipo de ponteiro.

typedef int *intptr;

intptr ptr;

// Same as:
// int *ptr;

intptré um novo alias com o tipo de ponteiro int *. A definição intptr ptr;,, define uma variável ptrcom o tipo int *. Portanto, ptré um ponteiro que pode apontar para uma variável do tipo int.

O uso de typedef para definir um novo tipo de ponteiro às vezes pode causar confusão. Por exemplo:

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;

Acima, intptr cliff, allen;significa definir 2 variáveis ​​com int*tipo para ambas. Isso ocorre porque um tipo definido por typedef é um tipo, não uma expansão. Em outras palavras,, intptrque é o int*tipo, decora cliffe allen. Pois intptr cliff2, *allen2;, o intptrtipo decora o cliff2e *allen2. Portanto, intptr cliff2, *allen2;é equivalente a 2 definições separadas intptr cliff2;e intptr *allen2. intptr *allen2significa que allen2é um ponteiro apontando para uma memória com int*tipo. Em breve, allen2tem o tipo int**,.

Estruturas e indicadores de estrutura

Typedefs também podem simplificar definições ou declarações para tipos de ponteiros de estrutura . Considere isto:

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

Usando typedef, o código acima pode ser reescrito assim:

typedef struct Node Node;

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

Em C, pode-se declarar várias variáveis ​​do mesmo tipo em uma única instrução, mesmo misturando estrutura com ponteiros ou não ponteiros. No entanto, seria necessário prefixar um asterisco para cada variável para designá-la como um ponteiro. A seguir, um programador pode presumir que errptrera de fato um Node *, mas um erro tipográfico significa que errptré um Node. Isso pode levar a erros sutis de sintaxe.

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

Ao definir o typedef Node *, é garantido que todas as variáveis ​​são tipos de ponteiros de estrutura, ou digamos, que cada variável é um tipo de ponteiro apontando para um tipo de estrutura .

typedef struct Node* NodePtr;

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

Ponteiros de função

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

O código anterior pode ser reescrito com especificações de 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);

Aqui MathFuncestá o novo alias para o tipo. A MathFuncé um ponteiro para uma função que retorna um inteiro e recebe como argumentos um número flutuante seguido por um inteiro.

Quando uma função retorna um ponteiro de função , pode ser ainda mais confuso sem typedef. A seguir está o protótipo de função do sinal (3) do FreeBSD :

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

A declaração da função acima é enigmática, pois não mostra claramente o que a função aceita como argumentos ou o tipo que ela retorna. Um programador novato pode até assumir que a função aceita um único intcomo seu argumento e não retorna nada, mas na realidade também precisa de um ponteiro de função e retorna outro ponteiro de função. Pode ser escrito de forma mais limpa:

typedef void (*sighandler_t)(int);

sighandler_t signal(int sig, sighandler_t func);

Matrizes

Um typedef também pode ser usado para simplificar a definição de tipos de array. Por exemplo,

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];

Aqui, arrTypeestá o novo alias para o char[6]tipo, que é um tipo de array com 6 elementos. Pois arrType *pArr;, pArré um ponteiro apontando para a memória do char[6]tipo.

Casts de tipo

Um typedef é criado usando a sintaxe de definição de tipo, mas pode ser usado como se tivesse sido criado usando a sintaxe de conversão de tipo . ( A conversão de tipo muda um tipo de dados.) Por exemplo, em cada linha após a primeira linha de:

// `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);

funcptré usado no lado esquerdo para declarar uma variável e é usado no lado direito para lançar um valor. Assim, o typedef pode ser usado por programadores que não desejam descobrir como converter a sintaxe de definição em sintaxe de conversão de tipo.

Sem o typedef, geralmente não é possível usar a sintaxe de definição e a sintaxe de conversão de forma intercambiável. Por exemplo:

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

Uso em C ++

Em C ++, os nomes de tipo podem ser complexos e typedef fornece um mecanismo para atribuir um nome simples ao tipo.

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;

    // ...
}

e

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 introduziu a possibilidade de expressar typedefs com em usingvez de typedef. Por exemplo, os dois typedefs acima podem ser escritos de forma equivalente como

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

Use com modelos

C ++ 03 não fornece typedefs com modelo . Por exemplo, para ter stringpair<T>representar std::pair<std::string, T>para cada tipo Tum não pode usar:

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

No entanto, se alguém estiver disposto a aceitar stringpair<T>::typeem vez de stringpair<T>, então é possível alcançar o resultado desejado por meio de um typedef dentro de uma classe ou estrutura modelo não utilizada:

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;

No C ++ 11 , typedefs modelados são adicionados com a seguinte sintaxe, que requer a usingpalavra - chave em vez da typedefpalavra - chave. (Veja os aliases do modelo .)

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;

Outras línguas

Em SystemVerilog , typedef se comporta exatamente da mesma forma que em C e C ++.

Em muitas linguagens funcionais de tipagem estática, como Haskell , Miranda , OCaml , etc., pode-se definir sinônimos de tipo , que são os mesmos que typedefs em C. Um exemplo em Haskell:

type PairOfInts = (Int, Int)

Este exemplo definiu um sinônimo de tipo PairOfIntscomo um tipo inteiro.

No Seed7, a definição de um tipo constante é usada para introduzir um sinônimo para um tipo:

const type: myVector is array integer;

Em Swift , usa-se a typealiaspalavra-chave para criar um typedef:

typealias PairOfInts = (Int, Int)

C # contém um recurso semelhante ao typedef ou à usingsintaxe do C ++.

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

Em D, a palavra-chave aliaspermite criar sinônimos de tipo ou tipo parcial.

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

Preocupações de uso

Kernighan e Ritchie declararam duas razões para usar um typedef. Primeiro, ele fornece um meio de tornar um programa mais portátil ou mais fácil de manter. Em vez de alterar um tipo em cada aparência dos arquivos de origem do programa, apenas uma única instrução typedef precisa ser alterada. size_t e ptrdiff_t em <stdlib.h> são esses nomes de typedef. Em segundo lugar, um typedef pode tornar uma definição ou declaração complexa mais fácil de entender.

Alguns programadores se opõem ao uso extensivo de typedefs. A maioria dos argumentos se concentra na ideia de que typedefs simplesmente ocultam o tipo de dados real de uma variável. Por exemplo, Greg Kroah-Hartman , um hacker e documentador do kernel do Linux , desencoraja seu uso para qualquer coisa, exceto para declarações de protótipo de função. Ele argumenta que essa prática não apenas ofusca desnecessariamente o código, mas também pode fazer com que os programadores usem acidentalmente grandes estruturas pensando que são tipos simples.

Veja também

Referências