Pergunta

Eu vi muitos programas que consistem em estruturas como a abaixo

typedef struct 
{
    int i;
    char k;
} elem;

elem user;

Por que é necessário tantas vezes? Qualquer razão específica ou área aplicável?

Foi útil?

Solução

Como Greg Hewgill disse, os meios typedef você já não tem que escrever struct em todo o lugar. Isso não só economiza teclas, ele também pode fazer o código mais limpo, uma vez que proporciona uma mais abstração pouquinho.

Coisas como

typedef struct {
  int x, y;
} Point;

Point point_new(int x, int y)
{
  Point a;
  a.x = x;
  a.y = y;
  return a;
}

torna-se mais limpo quando você não precisa ver o "struct" palavra-chave em todo o lugar, parece mais como se realmente há um tipo chamado "Point" no seu idioma. Que, após a typedef, é o caso, eu acho.

Observe também que enquanto o seu exemplo (e meu) omitido nomear a si struct, na verdade, nomeando também é útil para quando você deseja fornecer um tipo opaco. Então você teria um código como este no cabeçalho, por exemplo:

typedef struct Point Point;

Point * point_new(int x, int y);

e, em seguida, fornecer a definição struct no arquivo de implementação:

struct Point
{
  int x, y;
};

Point * point_new(int x, int y)
{
  Point *p;
  if((p = malloc(sizeof *p)) != NULL)
  {
    p->x = x;
    p->y = y;
  }
  return p;
}

Neste último caso, você não pode retornar o ponto por valor, desde a sua definição está oculto de usuários do arquivo de cabeçalho. Esta é uma técnica amplamente utilizada em GTK + , por exemplo.

Atualizar Note que existem também projectos altamente considerado C, onde esse uso de typedef para esconder struct é considerado uma má idéia, o kernel Linux é provavelmente o mais bem conhecido tal projeto. Veja capítulo 5 do O Linux Kernel CodingStyle documento por palavras iradas Linus. :) O meu ponto é que o "deve" na pergunta é, talvez, não gravada na pedra, depois de tudo.

Outras dicas

É incrível como muitas pessoas se esta errado. POR FAVOR não typedef struct em C, ele desnecessariamente polui o namespace global que é tipicamente muito já poluído em grandes programas em C.

Além disso, estruturas typedef sem um nome de marca são uma das principais causas da imposição desnecessária de relações de ordenação entre os arquivos de cabeçalho.

Considere o seguinte:

#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

Com essa definição, um, não usar typedefs, é possível que uma unidade compiland para incluir foo.h para chegar à definição FOO_DEF. Se ele não tenta excluir a referência o membro 'bar' da estrutura foo então não haverá necessidade de incluir o arquivo "bar.h".

Além disso, uma vez que os namespaces são diferentes entre os nomes de marcas e os nomes de membros, é possível escrever código muito legível, tais como:

struct foo *foo;

printf("foo->bar = %p", foo->bar);

Uma vez que os namespaces são separados, não há conflito na nomeação de variáveis ??coincidentes com seu nome tag struct.

Se eu tiver que manter seu código, vou retirar suas estruturas typedef.

De um artigo de idade por Dan Saks ( http://www.ddj.com/ ? CPP / 184403396 pgno = 3 ):


As regras de linguagem C para nomeação estruturas são um pouco excêntrico, mas eles são bastante inofensivos. No entanto, quando estendido para as classes em C ++, aqueles mesma regrinhas rachaduras abertas para que os insetos rastejar por eles.

Em C, o nome s que aparecem nas

struct s
    {
    ...
    };

é uma tag. Um nome de tag não é um tipo nome. Dada a definição acima, declarações como

s x;    /* error in C */
s *p;   /* error in C */

são erros em C. Você deve escrevê-los como

struct s x;     /* OK */
struct s *p;    /* OK */

Os nomes dos sindicatos e enumerações também são etiquetas em vez de tipos.

Em C, as marcas são diferentes de todos os outros nomes (para funções, tipos, variáveis ??e constantes de enumeração). compiladores C manter as tags em um símbolo tabela que se não é conceitualmente fisicamente separada da mesa que contém todos os outros nomes. Assim, ele é possível para um programa C ter tanto uma tag e um nome de outra com a mesma grafia no mesmo âmbito. Por exemplo,

struct s s;

é uma declaração válida que declara s variável do tipo struct s. Pode não ser uma boa prática, mas compiladores C deve aceitá-lo. Eu nunca vi um justificativa para o porquê C foi concebido este maneira. Eu sempre pensei que era uma erro, mas não é.

Muitos programadores (incluindo o seu realmente) prefiro pensar em nomes struct como nomes de tipo, para que eles definir um alias para a tag usando um typedef. Para exemplo, definindo

struct s
    {
    ...
    };
typedef struct s S;

permite que você use S no lugar de struct s, como em

S x;
S *p;

Um programa não pode usar S como o nome do tanto um tipo e um variável (ou função ou constante enumeração):

S S;    // error

Isso é bom.

O nome da marca em um struct, união, ou definição enum é opcional. Muitos programadores dobrar a definição struct no typedef e dispensar a tag completamente, como em:

typedef struct
    {
    ...
    } S;

O artigo ligado também tem uma discussão sobre como o comportamento do C ++ de não requireing um typedef pode causar problemas nome esconderijos sutis. Para evitar esses problemas, é uma boa idéia para typedef suas classes e estruturas em C ++, também, mesmo que à primeira vista parece ser desnecessário. Em C ++, com o typedef o nome esconderijo tornar-se um erro que o compilador diz-lhe sobre em vez de uma fonte oculta de problemas potenciais.

Usando um typedef evita ter que escrever struct cada vez que você declarar uma variável desse tipo:

struct elem
{
 int i;
 char k;
};
elem user; // compile error!
struct elem user; // this is correct

Uma outra boa razão para sempre typedef enumerações e estruturas resultados deste problema:

enum EnumDef
{
  FIRST_ITEM,
  SECOND_ITEM
};

struct StructDef
{
  enum EnuumDef MyEnum;
  unsigned int MyVar;
} MyStruct;

Observe o erro de digitação no EnumDef na estrutura (PTG u MDEF)? Isso compila sem erro (ou aviso) e é (dependendo da interpretação literal do Padrão C) correcta. O problema é que eu só criou uma nova definição de enumeração (Vazio) dentro do meu struct. Eu não sou (como pretendido) usando a definição anterior EnumDef.

Com um typdef mesmo tipo de erros de digitação teria resultado em um erros do compilador para usar um tipo desconhecido:

typedef 
{
  FIRST_ITEM,
  SECOND_ITEM
} EnumDef;

typedef struct
{
  EnuumDef MyEnum; /* compiler error (unknown type) */
  unsigned int MyVar;
} StructDef;
StrructDef MyStruct; /* compiler error (unknown type) */

eu defendo SEMPRE typedef'ing estruturas e enumerações.

Não só para poupar algum digitação (sem trocadilhos;).), Mas porque é mais seguro

Linux kernel do estilo de codificação Capítulo 5 dá grandes prós e contras (principalmente contras) de usando typedef.

Por favor, não usar coisas como "vps_t".

É um erro usar typedef para estruturas e ponteiros. Quando você vê um

vps_t a;

na fonte, o que significa isso?

Por outro lado, se ele diz que

struct virtual_container *a;

Você pode realmente dizer o que "a" é.

Muitas pessoas pensam que typedefs "ajuda legibilidade". Não tão. Eles são úteis apenas para:

(a) objetos totalmente opacos (onde o typedef é usada ativamente para hide que é o objeto).

Exemplo: "pte_t" etc. opaco objetos que você só pode acessar usando as funções de acesso adequada

NOTA! Opacidade e "funções de acesso" não são boas em si mesmas. A razão que nós tê-los para coisas como pte_t etc. é que não há realmente absolutamente de zero informações portably acessível lá.

(b) Limpar inteiro tipos, onde a abstração ajuda confusão evitar se é "int" ou "longo".

u8 / u16 / u32 são typedefs perfeitamente bem, embora eles se encaixam na categoria (d) melhor do que aqui.

NOTA! Mais uma vez - é preciso haver um razão para isso. Se algo está "unsigned long", então não há nenhuma razão para fazer

typedef unsigned long myflags_t;

mas se há uma razão clara para por isso que em determinadas circunstâncias pode ser um "int não assinado" e sob outras configurações podem ser "unsigned long", então por todos os meios vá em frente e usar um typedef.

(c) quando você usa escassa para literalmente criar um new tipo para verificação de tipo.

(d) Novos tipos que são idênticos aos tipos C99 padrão, em certas circunstâncias excepcionais.

Embora ele levaria apenas um curto período de tempo para os olhos e cérebro para se acostumar com os tipos padrão como 'uint32_t', algumas pessoas se opõem ao seu uso de qualquer maneira.

Portanto, o específica do Linux 'u8 / u16 / u32 / U64' tipos e seus equivalentes assinados que são idênticos aos tipos padrão são permitidos -., Embora eles não são obrigatórios no novo código do seu próprio

Ao editar o código que já usa um ou outro conjunto de tipos existente, você deve estar de acordo com as escolhas existentes nesse código.

(e) Tipos de seguros para o uso em espaço de usuário.

Em certas estruturas que são visíveis do espaço do usuário, não podemos exigir tipos C99 e não pode usar o formulário 'u32' acima. Assim, usamos __u32 e tipos semelhantes em todas as estruturas que são compartilhados com userspace.

Talvez existam outros casos também, mas a regra deve ser basicamente a usar NUNCA um typedef a menos que você pode combinar claramente uma dessas regras.

Em geral, um ponteiro ou uma estrutura que tem elementos que razoavelmente podem ser acessados ??diretamente deve não ser um typedef.

Acontece que há prós e contras. A fonte de informação útil é o livro seminal "Expert C Programming" ( Capítulo 3 ). Resumidamente, em C você tem vários espaços de nomes: marcas, tipos, nomes de membros e identificadores . typedef introduz um alias para um tipo e o localiza no namespace tag. Ou seja,

typedef struct Tag{
...members...
}Type;

define duas coisas. Um Tag no namespace tag e um tipo no tipo de namespace. Assim, você pode fazer as duas coisas Type myType e struct Tag myTagType. Declarações como struct Type myType ou Tag myTagType são ilegais. Além disso, em uma declaração como esta:

typedef Type *Type_ptr;

nós definimos um ponteiro para o nosso tipo. Então, se nós declaramos:

Type_ptr var1, var2;
struct Tag *myTagType1, myTagType2;

então var1, var2 e myTagType1 são ponteiros para Type, mas myTagType2 não.

No livro acima mencionado, menciona que estruturas typedefing não são muito útil, pois só poupa o programador de escrever a palavra struct. No entanto, tenho uma objeção, como muitos outros programadores C. Embora às vezes se transforma para ofuscar alguns nomes (é por isso que não é aconselhável em grandes bases de código, como o kernel) quando você deseja implementar o polimorfismo em C ajuda muito olhada aqui para mais detalhes . Exemplo:

typedef struct MyWriter_t{
    MyPipe super;
    MyQueue relative;
    uint32_t flags;
...
}MyWriter;

você pode fazer:

void my_writer_func(MyPipe *s)
{
    MyWriter *self = (MyWriter *) s;
    uint32_t myFlags = self->flags;
...
}

Assim, você pode acessar um membro externo (flags) pela estrutura interna (MyPipe) através de casting. Para mim, é menos confuso para lançar todo o tipo do que fazer (struct MyWriter_ *) s; cada vez que você deseja realizar tal funcionalidade. Nestes casos breve referência é um grande negócio, especialmente se você fortemente empregar a técnica em seu código.

Finalmente, o último aspecto com tipos typedefed é a incapacidade de estendê-los, em contraste com macros. Se, por exemplo, você tem:

#define X char[10] or
typedef char Y[10]

Você pode então declarar

unsigned X x; but not
unsigned Y y;

Nós realmente não me importo por isso por estruturas porque não se aplica aos especificadores de armazenamento (volatile e const).

Eu não acho declarações para a frente são ainda possíveis com typedef. Uso de struct, enum, e da união permitir encaminhar declarações quando dependências (sabe) é bidirecional.

Estilo: Uso de typedef em C ++ faz um pouco de sentido. Quase pode ser necessária quando se lida com modelos que requerem múltiplas e / ou variáveis ??parâmetros. O typedef ajuda a manter a nomeação em linha reta.

Não é assim na linguagem de programação C. O uso de typedef mais frequentemente serve nenhuma finalidade mas para ofuscar o uso de estrutura de dados. Uma vez que apenas {struct (6), enum (4), a união (5)} número de teclas são usadas para declarar um tipo de dados não há quase nenhum uso para o aliasing da estrutura. É que tipo de dados a um sindicato ou um struct? Usando a declaração não typdefed simples permite saber imediatamente que tipo é.

Observe como Linux é escrito com estrita evitar este absurdo aliasing typedef traz. O resultado é um estilo minimalista e limpo.

Vamos começar com o básico e trabalhar o nosso caminho.

Aqui está um exemplo de definição de estrutura:

struct point
  {
    int x, y;
  };

Aqui o nome point é opcional.

A estrutura pode ser declarada durante a sua definição ou depois.

Declarando durante a definição

struct point
  {
    int x, y;
  } first_point, second_point;

Declarando após definição

struct point
  {
    int x, y;
  };
struct point first_point, second_point;

Agora, observe cuidadosamente o último caso acima; Você precisa escrever struct point para declarar estruturas desse tipo, se você decidir criar esse tipo em um ponto mais tarde no código.

Entre typedef. Se você pretende criar nova Estrutura (Structure é um costume de dados do tipo) em um momento posterior em seu programa usando o mesmo modelo, usando typedef durante a sua definição pode ser uma boa idéia, pois você pode economizar algum digitação avançar.

typedef struct point
  {
    int x, y;
  } Points;

Points first_point, second_point;

Uma palavra de cautela, enquanto nomeando seu tipo personalizado

Nada impede que você use _t sufixo no final do seu nome de tipo personalizado, mas POSIX reservas padrão o uso do sufixo _t para designar nomes padrão tipo biblioteca.

o nome (opcionalmente) dar a estrutura é chamada de nome do tag e, como foi observado, não é um tipo em si mesmo. Para chegar ao tipo requer o prefixo struct.

GTK + lado, eu não tenho certeza do tagname é usado qualquer coisa como tão comumente como um typedef com o tipo struct, então em C ++ que é reconhecido e você pode omitir a palavra-chave struct e usar o tagname como o nome do tipo também:


    struct MyStruct
    {
      int i;
    };

    // The following is legal in C++:
    MyStruct obj;
    obj.i = 7;

typedef não irá fornecer um conjunto co-dependentes de estruturas de dados. Isso você não pode fazer com typdef:

struct bar;
struct foo;

struct foo {
    struct bar *b;
};

struct bar {
    struct foo *f;
};

É claro que você sempre pode adicionar:

typedef struct foo foo_t;
typedef struct bar bar_t;

O que exatamente é o ponto de que?

A> um AIDS typdef no significado e documentação de um programa, permitindo que criação de sinônimos mais significativas para tipos de dados . Além disso, eles ajudam parametrizar um programa contra problemas de portabilidade (K & R, pg147, C prog lang).

B> a estrutura define um tipo . Estruturas permite agrupamento conveniente de uma colecção de vars para conveniência de manipulação (K & R, pg127, C prog lang.) Como uma única unidade

C> typedef'ing um struct é explicada em A acima.

D> Para mim, estruturas são tipos personalizados ou recipientes ou coleções ou namespaces ou tipos complexos, enquanto que um typdef é apenas um meio para criar mais apelidos.

Acontece que no C99 typedef é necessária. Ele está desatualizado, mas um monte de ferramentas (ala HackRank) uso c99 como sua implementação C puro. E typedef é necessária lá.

Eu não estou dizendo que eles devem mudar (talvez tem duas opções c) se o requisito alterado, aqueles de nós estudando para entrevistas no local seria SOL.

Em 'C' linguagem de programação a palavra-chave 'typedef' é usado para declarar um novo nome para algum objeto (struct, matriz, tipo function..enum). Por exemplo, vou usar um 'struct-s'. Em 'C' que muitas vezes declarar um fora 'struct' da função 'main'. Por exemplo:

struct complex{ int real_part, img_part }COMPLEX;

main(){

 struct KOMPLEKS number; // number type is now a struct type
 number.real_part = 3;
 number.img_part = -1;
 printf("Number: %d.%d i \n",number.real_part, number.img_part);

}

Cada vez que eu decidir usar um tipo struct vou precisar esta palavra-chave 'struct 'algo' 'nome'.' Typedef' vai simplesmente mudar o nome desse tipo e eu posso usar esse novo nome no meu programa cada vez que eu quiser. Assim, o nosso código será:

typedef struct complex{int real_part, img_part; }COMPLEX;
//now COMPLEX is the new name for this structure and if I want to use it without
// a keyword like in the first example 'struct complex number'.

main(){

COMPLEX number; // number is now the same type as in the first example
number.real_part = 1;
number.img)part = 5;
printf("%d %d \n", number.real_part, number.img_part);

}

Se você tiver algum objeto local (struct, array, valioso) que será usado em todo o seu programa você pode simplesmente dar-lhe um nome usando um 'typedef'.

Em todos, em linguagem C, struct / união / enum são instrução macro processado pelo pré-processador linguagem C (não confundir com o pré-processador que tratar "#include" e outros)

forma:

struct a
{
   int i;
};

struct b
{
   struct a;
   int i;
   int j;
};

struct b é gasto como algo parecido com isto:

struct b
{
    struct a
    {
        int i;
    };
    int i;
    int j;
}

e assim, em tempo de compilação-lo evoluir na pilha como algo como: b: int ai int i j int

que também por isso que é dificil ter estruturas selfreferent, C pré-processador redondos em um loop declaração de que não pode terminar.

typedef são do tipo especificador, Isso significa que apenas compilador C processá-lo e ele pode fazer como ele quer para a implementação otimizar o código assembler. É membro expend também não do tipo par estupidamente como pré-processador fazer com estruturas, mas usar mais complexo algoritmo de construção de referência, assim que a construção como:

typedef struct a A; //anticipated declaration for member declaration

typedef struct a //Implemented declaration
{
    A* b; // member declaration
}A;

é permitida e totalmente funcional. Esse dar implementação também o acesso à conversão de tipo compilator e remover alguns efeitos incomodando quando segmento de execução deixar o campo de aplicação de inicialização funções.

Isso significa que em typedefs C são mais perto como classe C ++ de estruturas solitárias.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top