Por que devemos digitar uma estrutura com tanta frequência em C?

407

Eu já vi muitos programas que consistem em estruturas como a abaixo

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

elem user;

Por que é necessário com tanta frequência? Algum motivo específico ou área aplicável?

Manoj Dúvidas
fonte
14
Resposta mais completa e precisa: stackoverflow.com/questions/612328/…
AbiusX
Ele tem desvantagens eu acho que você não pode criar uma lista de links com anônima struct porque a linha struct * ptrdentro da estrutura fará com que erro
Raghib Ahsan
9
A 'resposta mais completa e precisa' é a diferença entre struct e typedef struct em C ++ , e há diferenças significativas entre C e C ++ nessa área que tornam essa resposta não totalmente apropriada para uma pergunta sobre C.
Jonathan Leffler
Esta pergunta possui uma duplicata typedef struct vs definições de estrutura que também possui respostas estelares.
Jonathan Leffler
2
OTOH, kernel.org/doc/html/v4.10/process/coding-style.html nos diz que não devemos fazer esses typedefs.
glglgl

Respostas:

454

Como Greg Hewgill disse, o typedef significa que você não precisa mais escrever em structtodo o lugar. Isso não apenas salva as teclas, como também pode tornar o código mais limpo, pois fornece um pouco mais de abstração.

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

fica mais limpo quando você não precisa ver a palavra-chave "struct" em todo o lugar, parece mais que realmente existe um tipo chamado "Ponto" no seu idioma. Qual, depois do typedef, é o caso, eu acho.

Observe também que, enquanto o seu exemplo (e o meu) omitiu nomear o struct próprio, na verdade nomear 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 forneça a structdefinição 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, pois sua definição está oculta dos usuários do arquivo de cabeçalho. Essa é uma técnica usada amplamente no GTK + , por exemplo.

ATUALIZAÇÃO Observe que também existem projetos C altamente conceituados nos quais esse uso typedefpara ocultar structé considerado uma má idéia, o kernel do Linux é provavelmente o projeto mais conhecido desse tipo. Veja o Capítulo 5 do documento Linux Kernel CodingStyle para as palavras irritadas de Linus. :) O que quero dizer é que o "deveria" na pergunta talvez não seja imutável, afinal.

descontrair
fonte
58
Você não deve usar identificadores com um sublinhado seguido por uma letra maiúscula, pois eles são reservados (consulte a seção 7.1.3, parágrafo 1). Embora seja pouco provável que seja um problema, é um comportamento tecnicamente indefinido quando você os usa (7.1.3, parágrafo 2).
dreamlax
16
@dreamlax: Caso não esteja claro para outras pessoas, isso apenas inicia um identificador com um sublinhado e uma maiúscula que você não deve fazer; você é livre para usar isso no meio de um identificador.
Brianmearns
12
É interessante que o exemplo dado aqui (no qual o typedef impede o uso de "struct" "em todo o lugar") seja realmente maior que o mesmo código sem o typedef, pois salva exatamente um uso da palavra "struct". A quantidade de abstração obtida raramente se compara a favor da ofuscação adicional.
William Pursell
7
@Rerito fyi, página 166 do rascunho C99 , Todos os identificadores que começam com um sublinhado e uma letra maiúscula ou outro sublinhado são sempre reservados para qualquer uso. E todos os identificadores que começam com um sublinhado são sempre reservados para uso como identificadores com escopo de arquivo nos espaços de nomes comuns e de tags.
e2-e4
10
Curiosamente, a codificação diretrizes kernel do Linux dizem que devemos ser muito mais conservadora sobre o uso de typedefs (seção 5): kernel.org/doc/Documentation/CodingStyle
gsingh2011
206

É incrível quantas pessoas entendem errado. POR FAVOR, não digite typedef structs em C, polui desnecessariamente o espaço para nome global, que normalmente já está muito poluído em grandes programas em C.

Além disso, estruturas digitadas sem um nome de marca são uma das principais causas da imposição desnecessária de relacionamentos de pedidos entre os arquivos de cabeçalho.

Considerar:

#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, não usando typedefs, é possível que uma unidade compiland inclua foo.h para chegar à FOO_DEFdefinição. Se ele não tentar desreferenciar o membro 'bar' da fooestrutura, não será necessário incluir o arquivo "bar.h".

Além disso, como os espaços de nomes são diferentes entre os nomes das tags e os nomes dos membros, é possível escrever um código muito legível, como:

struct foo *foo;

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

Como os namespaces são separados, não há conflito em nomear variáveis ​​coincidentes com o nome da tag struct.

Se eu tiver que manter seu código, removerei suas estruturas digitadas.

Jerry Hicks
fonte
37
O mais surpreendente é que, 13 meses após a resposta, eu sou o primeiro a votar! typedef'ing structs é um dos maiores abusos de C e não tem lugar em códigos bem escritos. O typedef é útil para remover os tipos de ponteiros de função complicados e realmente não serve para nenhum outro propósito útil.
precisa saber é o seguinte
28
Peter van der Linden também argumenta contra as estruturas de digitação em seu livro esclarecedor "Expert C Programming - Deep C Secrets". A essência é: você quer saber que algo é uma estrutura ou união, não oculta.
Jens
34
O estilo de codificação do kernel Linux proíbe explicitamente as estruturas de digitação. Capítulo 5: Typedefs: "É um erro usar o typedef para estruturas e ponteiros". kernel.org/doc/Documentation/CodingStyle
jasso 9/12/12
63
Quais benefícios exatamente a digitação "struct" oferece repetidamente? E por falar em poluição, por que você deseja ter uma estrutura e uma função / variável / typedef com o mesmo nome em um espaço para nome global (a menos que seja um typedef para a mesma função)? O padrão seguro é usar typedef struct X { ... } X. Dessa forma, você pode usar o formato abreviado Xpara endereçar a estrutura em qualquer lugar em que a definição esteja disponível, mas ainda assim pode declarar e usar struct Xquando desejar.
Pavel Minaev 20/03/2013
7
Pessoalmente, muito raramente, se alguma vez uso typedef, não diria que outras pessoas não deveriam usá-lo, apenas não é o meu estilo. Eu gosto de ver uma estrutura antes de um tipo de variável, então eu sei imediatamente que é uma estrutura. O argumento mais fácil de digitar é um pouco manco, também é mais fácil digitar variáveis ​​que são uma única letra, também com conclusões automáticas quão difícil é digitar struct hoje em dia em qualquer lugar.
Nathan
138

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


As regras da linguagem C para nomear estruturas são um pouco excêntricas, mas são bastante inofensivas. No entanto, quando estendidas às classes em C ++, essas mesmas regras abrem pequenas brechas para erros de rastreamento.

Em C, o nome está aparecendo em

struct s
    {
    ...
    };

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

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

há erros em C. Você deve escrevê-los como

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

Os nomes de uniões e enumerações também são tags, e não tipos.

Em C, as tags são distintas de todos os outros nomes (para funções, tipos, variáveis ​​e constantes de enumeração). Os compiladores C mantêm tags em uma tabela de símbolos conceitualmente, se não fisicamente separada da tabela que contém todos os outros nomes. Portanto, é possível que um programa C tenha uma tag e outro nome com a mesma ortografia no mesmo escopo. Por exemplo,

struct s s;

é uma declaração válida que declara a variável s do tipo struct s. Pode não ser uma boa prática, mas os compiladores C devem aceitá-la. Eu nunca vi uma justificativa para o porquê C foi projetado dessa maneira. Eu sempre pensei que era um erro, mas aí está.

Muitos programadores (incluindo o seu verdadeiramente) preferem pensar em nomes de estrutura como nomes de tipo, então eles definem um alias para a tag usando um typedef. Por exemplo, definindo

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

permite usar S no lugar de struct s, como em

S x;
S *p;

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

S S;    // error

Isso é bom.

O nome da marca em uma definição de struct, união ou enum é opcional. Muitos programadores dobram a definição de struct no typedef e dispensam completamente a tag, como em:

typedef struct
    {
    ...
    } S;

O artigo vinculado também tem uma discussão sobre como o comportamento do C ++ de não exigir a typedefpode causar problemas sutis de ocultação de nomes. Para evitar esses problemas, também é uma boa ideia para typedefsuas classes e estruturas em C ++, mesmo que à primeira vista pareça desnecessário. No C ++, com o typedefnome oculto se torna um erro que o compilador informa sobre, e não uma fonte oculta de possíveis problemas.

Michael Burr
fonte
2
Um exemplo de onde o nome da tag é o mesmo que um nome que não é tag está no programa (POSIX ou Unix) com a int stat(const char *restrict path, struct stat *restrict buf)função Lá você tem uma função statno espaço de nome comum e struct statno espaço de nome da tag.
Jonathan Leffler
2
sua declaração, SS; // erro .... ESTÁ ERRADO Funciona bem. Quero dizer a sua afirmação de que "nós não podemos ter mesmo nome para typedef tag e var" é errado ... pls verificar
eRaisedToX
63

O uso de typedefevita a necessidade de escrever structtoda vez que você declara uma variável desse tipo:

struct elem
{
 int i;
 char k;
};
elem user; // compile error!
struct elem user; // this is correct
Greg Hewgill
fonte
2
ok, não estamos tendo esse problema em C ++. Então, por que ninguém remove essa falha do compilador do C e faz com que seja igual à do C ++. Ok O C ++ está tendo algumas áreas de aplicação diferentes e com os recursos avançados.mas não podemos herdar alguns deles em C sem alterar o C original?
Manoj Duvida
4
Manoj, o nome da tag ("struct foo") é necessário quando você precisa definir uma estrutura que faça referência a si mesma. por exemplo, o "próximo" ponteiro em uma lista vinculada. Mais precisamente, o compilador implementa o padrão, e é isso que o padrão diz para fazer.
31968 Michael Carman
41
Não é uma falha no compilador C, faz parte do design. Eles mudaram isso para C ++, o que acho que facilita as coisas, mas isso não significa que o comportamento de C esteja errado.
Herms
5
Infelizmente, muitos 'programadores' definem uma estrutura e depois a digitam com algum nome 'não relacionado' (como struct myStruct ...; typedef struct myStruct susan *; Em quase todos os casos, um typedef leva a nada além de desorganizar o código, ocultando a definição real de uma variável / parâmetro, e mis-leva todos, incluindo o escritor original do código.
user3629249
11
@ user3629249 Concordo com você que o estilo de programação mencionado é horrível, mas esse não é um motivo para denegrir typedefestruturas em geral. Você também poderia fazer typedef struct foo foo;. Obviamente, a structpalavra - chave não é mais necessária, o que pode ser uma dica útil de que o alias de tipo que olhamos é um alias para uma estrutura, mas geralmente não é ruim. Considere-se também um caso em que o identificador do tipo resultante alias typedefindica que é uma abreviatura para uma estrutura, fe: typedef struct foo foo_struct;.
RobertS apoia Monica Cellio em
38

Um outro bom motivo para sempre digitar enums e struct resulta desse 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 (Enu u mDef)? Isso compila sem erro (ou aviso) e está (dependendo da interpretação literal do Padrão C) correto. O problema é que eu apenas criei uma nova definição de enumeração (vazia) dentro da minha estrutura. Não estou (como pretendido) usando a definição anterior EnumDef.

Com um typdef, tipos semelhantes de erros de digitação resultariam em erros de compilador ao 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 defenderia SEMPRE digitando estruturas e enumerações.

Não apenas para salvar algumas digitações (sem trocadilhos;)), mas porque é mais seguro.

cschol
fonte
11
Pior ainda, seu erro de digitação pode coincidir com uma tag diferente. No caso de uma estrutura, isso pode resultar na compilação correta do programa inteiro e no comportamento indefinido do tempo de execução.
21415 MM
3
esta definição: 'typedef {FIRST_ITEM, SECOND_ITEM} EnumDef;' não define uma enumeração. Eu escrevi centenas de programas enormes e tive a má sorte de realizar manutenção em programas que outros escreveram. Da mão dura da experiência, usar typedef em uma estrutura apenas leva a problemas. Esperamos que o programador não seja tão deficiente que tenha problemas ao digitar uma definição completa ao declarar uma instância struct. C não é básico, portanto, digitar mais alguns caracteres não prejudica a operação do programa.
User3629249
2
Para aqueles que sentem alguma aversão a digitar mais do que o número mínimo absoluto de caracteres, sugiro ingressar em um grupo que tenta escrever aplicativos com o número mínimo de pressionamentos de tecla. Apenas não use sua nova 'habilidade' em um ambiente de trabalho, especialmente em um ambiente de trabalho que execute rigorosamente as análises por pares
user3629249
Observe que geralmente é desencorajado o uso enumcomo um tipo, pois muitos compiladores emitem avisos estranhos ao usá-los 'incorretamente'. Por exemplo, inicializar um enumpara 0 pode dar um aviso de 'constante inteira não enum'. A declaração de encaminhamento de um enumtambém não é permitida. Deve-se usar int(ou unsigned int) em vez disso.
yyny
5
Esse exemplo não é compilado, nem eu esperaria. Compilando Debug / test.o test.c: 10: 17: erro: o campo tem um tipo incompleto 'enum EnuumDef' enum EnuumDef MyEnum; ^ test.c: 10: 8: note: declaração direta de 'enum EnuumDef' enum EnuumDef MyEnum; ^ 1 erro gerado. gnuc, com std = c99.
natersoz
30

Estilo de codificação do kernel do Linux O Capítulo 5 oferece grandes prós e contras (principalmente os contras) do uso typedef.

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

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

vps_t a;

na fonte, o que isso significa?

Por outro lado, se diz

struct virtual_container *a;

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

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

(a) objetos totalmente opacos (onde o typedef é usado ativamente para ocultar o que o objeto é).

Exemplo: "pte_t" etc. objetos opacos que você só pode acessar usando as funções adequadas do acessador.

NOTA! Opacidade e "funções de acessador" não são boas em si mesmas. A razão pela qual os temos para coisas como pte_t etc. é que realmente há zero informações de portabilidade acessíveis lá.

(b) Limpar tipos inteiros, nos quais a abstração ajuda a evitar confusão, seja "int" ou "long".

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

NOTA! Novamente - é preciso haver uma razão para isso. Se algo é "sem assinatura por muito tempo", não há razão para fazer

typedef unsigned long myflags_t;

mas se houver uma razão clara para que, em determinadas circunstâncias, possa ser um "int sem assinatura" e em outras configurações possa ser "sem assinatura por muito tempo", então, por todos os meios, vá em frente e use um typedef.

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

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

Embora leve apenas um curto período de tempo para os olhos e o cérebro se acostumarem aos tipos padrão como 'uint32_t', algumas pessoas se opõem a seu uso de qualquer maneira.

Portanto, são permitidos os tipos 'u8 / u16 / u32 / u64' específicos do Linux e seus equivalentes assinados idênticos aos tipos padrão - embora não sejam obrigatórios no seu próprio código.

Ao editar o código existente que já usa um ou outro conjunto de tipos, você deve estar em conformidade com as opções existentes nesse código.

(e) Tipos seguros para uso no espaço do usuário.

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

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

Em geral, um ponteiro ou uma estrutura que possui elementos razoavelmente acessíveis diretamente nunca deve ser um typedef.

Yu Hao
fonte
4
'Opacidade e "funções de acessador" não são boas em si mesmas ". Alguém pode explicar o porquê? Eu acho que esconder informações e encapsular seria uma boa ideia.
Yawar 4/16
5
@ Yawar Acabei de ler este documento e tive exatamente o mesmo pensamento. Claro, C não é orientado a objetos, mas a abstração ainda é uma coisa.
Baldrickk
12

Acontece que existem prós e contras. Uma fonte útil de informação é o livro seminal "Expert C Programming" ( Capítulo 3 ). Resumidamente, em C, você tem vários espaços para nome: tags, tipos, nomes de membros e identificadores . typedefintroduz um alias para um tipo e o localiza no espaço para nome da marca. Nomeadamente,

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

define duas coisas. Uma marca no espaço para nome da marca e um tipo no espaço para nome do tipo. Então você pode fazer as duas coisas Type myTypee struct Tag myTagType. Declarações como struct Type myTypeou Tag myTagTypesã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 declararmos:

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

então var1, var2e myTagType1são ponteiros para digitar, mas myTagType2não.

No livro acima mencionado, ele menciona que as estruturas de digitação não são muito úteis, pois apenas evita que o programador escreva a palavra struct. No entanto, tenho uma objeção, como muitos outros programadores em C. Embora algumas vezes oculte 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, isso ajuda muito a procurar detalhes aqui . 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 da conversão. Para mim, é menos confuso transmitir o tipo inteiro do que fazer (struct MyWriter_ *) s;toda vez que você deseja executar essa funcionalidade. Nesses casos, é muito importante fazer uma breve referência, especialmente se você empregar fortemente a técnica em seu código.

Finalmente, o último aspecto com os typedeftipos ed é a incapacidade de estendê-los, em contraste com as macros. Se, por exemplo, você tiver:

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

você pode declarar

unsigned X x; but not
unsigned Y y;

Realmente não nos importamos com isso porque não se aplica aos especificadores de armazenamento ( volatilee const).

user1533288
fonte
11
MyPipe *s; MyWriter *self = (MyWriter *) s;e você acabou de quebrar o alias estrito.
Jonathon Reinhart
@JonathonReinhart Seria ilustrativo mencionar como isso pode ser evitado, por exemplo, como o GTK + extremamente feliz trabalha em torno dele: bugzilla.gnome.org/show_bug.cgi?id=140722 / mail.gnome.org/archives/gtk -devel-list / 2004-April / msg00196.html
underscore_d
"typedef introduz um alias para um tipo e o localiza no espaço de nomes da tag. Ou seja," typedef struct Tag{ ...members... }Type; "define duas coisas" "não faz muito sentido. se typedef define tags, então 'Type' aqui também deve ser uma tag. A verdade é que a definição define 2 tags e 1 tipo (ou 2 tipos e 1 tag não tenho certeza.): struct Tag, TagE Type. struct Tagé definitivamente um tipo. Tagé uma tag. mas a confusão é se Typeé uma tag ou um tipo
Qwertyzw
12

Não acho que declarações avançadas sejam possíveis com typedef. O uso de struct, enum e union permite o encaminhamento de declarações quando dependências (conhecidas) são bidirecionais.

Estilo: o uso de typedef no C ++ faz bastante sentido. Pode ser quase necessário ao lidar com modelos que requerem parâmetros múltiplos e / ou variáveis. O typedef ajuda a manter a nomeação correta.

Não é assim na linguagem de programação C. O uso do typedef geralmente não serve para nada, mas para ofuscar o uso da estrutura de dados. Como apenas o número {struct (6), enum (4), união (5)} de teclas são usadas para declarar um tipo de dados, quase não há uso para o aliasing da estrutura. Esse tipo de dados é uma união ou uma estrutura? O uso da declaração simples e sem tipo de letra permite que você saiba imediatamente que tipo é.

Observe como o Linux é escrito com estrita evitação deste absurdo alias do typedef. O resultado é um estilo minimalista e limpo.

natersoz
fonte
10
Limpar não seria repetido em structtodos os lugares ... Os Typedef fazem novos tipos. O que você usa? Tipos. Não nos importamos se é uma estrutura, união ou enumeração, é por isso que a digitamos.
GManNickG
13
Não, nós não importa se é uma estrutura ou união, contra um algum tipo atômica enum ou. Você não pode coagir uma estrutura a um número inteiro ou a um ponteiro (ou a qualquer outro tipo, por sinal), que é tudo o que às vezes é necessário para armazenar algum contexto. A existência de palavras-chave 'struct' ou 'union' melhora a localidade do raciocínio. Ninguém diz que você precisa saber o que há dentro da estrutura.
Bernd Jendrissek
11
@BerndJendrissek: Estruturas e uniões são diferentes de outros tipos, mas o código do cliente deve se preocupar com qual dessas duas coisas (estrutura ou união) algo como FILEé?
Supercat 27/03
4
@supercat O FILE é um bom uso do typedef. Eu acho que o typedef está sendo usado em excesso , não que seja um mau recurso do idioma. IMHO usando typedef para tudo é o cheiro de código "especularidade generalizada". Observe que você declara variáveis ​​como FILE * foo, nunca como FILE foo. Para mim, isso importa.
Bernd Jendrissek 29/03
2
@supercat: "Se as variáveis ​​de identificação de arquivo fossem do tipo FILE em vez de FILE * ..." Mas essa é exatamente a ambiguidade que os typedefs permitem! Estamos acostumados a aceitar um FILE * para não nos preocuparmos com isso, mas cada vez que você adiciona typedef você está introduzindo mais um pouco de sobrecarga cognitiva: essa API deseja foo_t args ou foo_t *? Carregar explicitamente a 'estrutura' melhora a localidade do raciocínio, se custar mais alguns caracteres por definição de função.
Bernd Jendrissek 30/03
4

Vamos começar com o básico e seguir em frente.

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

struct point
  {
    int x, y;
  };

Aqui o nome pointé opcional.

Uma estrutura pode ser declarada durante 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 pointpara declarar Estruturas desse tipo se decidir criar esse tipo em um momento posterior no seu código.

Enter typedef. Se você pretende criar uma nova Estrutura (a Estrutura é um tipo de dados personalizado) posteriormente no seu programa usando o mesmo blueprint, usar typedefdurante a sua definição pode ser uma boa idéia, pois você pode salvar algumas digitações no futuro.

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

Points first_point, second_point;

Uma palavra de cautela ao nomear seu tipo personalizado

Nada impede que você use o sufixo _t no final do nome do seu tipo personalizado, mas o padrão POSIX reserva o uso do sufixo _t para indicar os nomes dos tipos de biblioteca padrão.

Até parece
fonte
3

o nome que você (opcionalmente) atribui à estrutura é chamado de nome da marca e, como foi observado, não é um tipo em si. Para chegar ao tipo, é necessário o prefixo struct.

GTK + à parte, não tenho certeza se o tagname é usado como um typedef para o tipo struct, portanto, no C ++ é reconhecido e você pode omitir a palavra-chave struct e usar o tagname como o nome do tipo:


    struct MyStruct
    {
      int i;
    };

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

philsquared
fonte
1

typedef não fornecerá um conjunto co-dependente de estruturas de dados. Isso você não pode fazer com o 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;

Qual é exatamente o ponto disso?

natersoz
fonte
1

Um typdef ajuda no significado e na documentação de um programa, permitindo a criação de sinônimos mais significativos para os tipos de dados . Além disso, eles ajudam a parametrizar um programa contra problemas de portabilidade (K&R, pg147, C prog lang).

B> uma estrutura define um tipo . As estruturas permitem o agrupamento conveniente de uma coleção de vars para facilitar o manuseio (K&R, pg127, C prog lang.) Como uma única unidade

C> digitando uma estrutura é explicado em A acima.

D> Para mim, estruturas são tipos personalizados, contêineres, coleções ou espaços de nomes ou tipos complexos, enquanto um typdef é apenas um meio de criar mais apelidos.

JamesAD-0
fonte
0

Acontece que, em C99, typedef é necessário. Está desatualizado, mas muitas ferramentas (ala HackRank) usam o c99 como sua implementação C pura. E typedef é necessário lá.

Não estou dizendo que eles deveriam mudar (talvez tenha duas opções C) se o requisito mudasse, aqueles que estudam para entrevistas no site seriam SOL.

Matthew Corey Brown
fonte
2
"Acontece que em C99 typedefé necessário." O que você quer dizer?
Julian Lopez #
A questão é sobre C, não C++. Em Ctypedefs são 'obrigatórios' (e provavelmente sempre serão). 'Obrigatório', como em, você não poderá declarar uma variável Point varName;e ter o tipo como sinônimo struct Point;sem a typedef struct Point Point;.
yyny
0

Na linguagem de programação 'C', a palavra-chave 'typedef' é usada para declarar um novo nome para algum objeto (tipo struct, array, function..enum). Por exemplo, vou usar um 'struct-s'. Em 'C', frequentemente declaramos uma 'estrutura' fora da função 'principal'. 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 de estrutura, precisarei dessa palavra-chave 'struct' something '' name '.' Typedef 'simplesmente renomeará esse tipo e eu posso usar esse novo nome no meu programa sempre que quiser. Portanto, 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ê tem algum objeto local (struct, array, valioso) que será usado em todo o programa, você pode simplesmente dar um nome a ele usando um 'typedef'.

RichardGeerify
fonte
-2

De qualquer forma, na linguagem C, struct / union / enum são instruções macro processadas pelo pré-processador da linguagem C (não se engane com o pré-processador que trata "#include" e outros)

tão :

struct a
{
   int i;
};

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

struct b é gasto da seguinte maneira:

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

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

é também por isso que é difícil ter estruturas autoreferentes, o pré-processador C é arredondado em um loop de desclassificação que não pode terminar.

typedef é um especificador de tipo, isso significa que apenas o compilador C processa e pode fazer o que ele deseja para otimizar a implementação do código do assembler. Ele também não gasta membro do tipo par estupidamente como o pré-processador faz com estruturas, mas usa um algoritmo de construção de referência mais complexo, de modo que construções como:

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

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

é permitido e totalmente funcional. Essa implementação também fornece acesso à conversão de tipo de compilador e remove alguns efeitos de erros quando o thread de execução sai do campo de aplicativo das funções de inicialização.

Isso significa que, em C, typedefs está mais próximo da classe C ++ do que estruturas solitárias.

doccpu
fonte
3
Não é difícil ter estruturas auto-referenciais. struct foo {struct foo * next; coisa int; }
Bernd Jendrissek
4
...o que? Dizer ao pré - processador para descrever a resolução de structse typedefs já é ruim o suficiente, mas o restante da sua escrita é tão confuso que acho difícil receber alguma mensagem dele. Uma coisa que posso dizer é que sua idéia de que um não- typedefd structnão pode ser declarado a frente ou usado como um membro opaco (ponteiro) é totalmente falsa. No seu 1º exemplo, struct bpode conter trivialmente um struct a *, não é typedefnecessário. As afirmações de que structs são peças simplesmente mudos de macro-expansão, e que typedefs dar-lhes revolucionários novos poderes, são dolorosamente incorreta
underscore_d