typedef struct vs struct definições

Eu sou um novato em programação C, mas eu queria saber qual é a diferença entre o uso de typedef ao definir uma estrutura versus não usar typedef. Parece-me que realmente não há diferença, eles realizam o mesmo.

struct myStruct{ int one; int two; }; 

vs.

 typedef struct{ int one; int two; }myStruct; 

O idioma comum está usando ambos:

 typedef struct X { int x; } X; 

São definições diferentes. Para tornar a discussão mais clara, vou dividir a frase:

 struct S { int x; }; typedef struct SS; 

Na primeira linha você está definindo o identificador S dentro do espaço de nomes da estrutura (não no sentido C ++). Você pode usá-lo e definir variables ​​ou argumentos de function do tipo recém-definido, definindo o tipo do argumento como struct S :

 void f( struct S argument ); // struct is required here 

A segunda linha adiciona um alias de tipo S no espaço de nome global e, portanto, permite que você apenas escreva:

 void f( S argument ); // struct keyword no longer needed 

Observe que, como os espaços para nomes dos identificadores são diferentes, a definição de S nas estruturas e espaços globais não é um erro, pois não está redefinindo o mesmo identificador, mas criando um identificador diferente em um local diferente.

Para tornar a diferença mais clara:

 typedef struct S { int x; } T; void S() { } // correct //void T() {} // error: symbol T already defined as an alias to 'struct S' 

Você pode definir uma function com o mesmo nome da estrutura que os identificadores são mantidos em espaços diferentes, mas você não pode definir uma function com o mesmo nome que um typedef como esses identificadores colidem.

Em C ++, é um pouco diferente, pois as regras para localizar um símbolo mudaram sutilmente. C ++ ainda mantém os dois espaços identificadores diferentes, mas ao contrário de C, quando você define apenas o símbolo dentro do espaço identificador de class, você não é obrigado a fornecer a palavra-chave struct / class:

  // C++ struct S { int x; }; // S defined as a class void f( S a ); // correct: struct is optional 

Quais alterações são as regras de pesquisa, não onde os identificadores são definidos. O compilador pesquisará a tabela de identificadores globais e, depois de S não ter sido encontrado, procurará S dentro dos identificadores de class.

O código apresentado antes se comporta da mesma maneira:

 typedef struct S { int x; } T; void S() {} // correct [*] //void T() {} // error: symbol T already defined as an alias to 'struct S' 

Após a definição da function S na segunda linha, a estrutura S não pode ser resolvida automaticamente pelo compilador, e para criar um object ou definir um argumento desse tipo você deve voltar a include a palavra-chave struct :

 // previous code here... int main() { S(); struct S s; } 

struct e typedef são duas coisas muito diferentes.

A palavra-chave struct é usada para definir ou referenciar um tipo de estrutura. Por exemplo, isso:

 struct foo { int n; }; 

cria um novo tipo chamado struct foo . O nome foo é uma tag ; é significativo somente quando é imediatamente precedido pela palavra-chave struct , porque as tags e outros identificadores estão em espaços de nome distintos. (Isso é semelhante, mas muito mais restrito do que, o conceito C ++ de namespace s.)

Um typedef , apesar do nome, não define um novo tipo; apenas cria um novo nome para um tipo existente. Por exemplo, dado:

 typedef int my_int; 

my_int é um novo nome para int ; my_int e int são exatamente do mesmo tipo. Da mesma forma, dada a definição da struct acima, você pode escrever:

 typedef struct foo foo; 

O tipo já tem um nome, struct foo . A declaração typedef dá ao mesmo tipo um novo nome, foo .

A syntax permite combinar uma struct e typedef em uma única declaração:

 typedef struct bar { int n; } bar; 

Este é um idioma comum. Agora você pode se referir a este tipo de estrutura como struct bar ou apenas como bar .

Observe que o nome typedef não se torna visível até o final da declaração. Se a estrutura contém um ponteiro para si mesmo, você usa a versão da struct para se referir a ela:

 typedef struct node { int data; struct node *next; /* can't use just "node *next" here */ } node; 

Alguns programadores usarão identificadores distintos para a tag struct e para o nome typedef. Na minha opinião, não há boas razões para isso; usar o mesmo nome é perfeitamente legal e deixa claro que são do mesmo tipo. Se você precisar usar identificadores diferentes, use pelo menos uma convenção consistente:

 typedef struct node_s { /* ... */ } node; 

(Pessoalmente, prefiro omitir o typedef e referir ao tipo como struct bar . O typedef economiza um pouco de digitação, mas esconde o fato de que é um tipo de estrutura. Se você quer que o tipo seja opaco, isso pode ser um bom Se o código do cliente vai se referir ao membro n pelo nome, então não é opaco, é visivelmente uma estrutura, e na minha opinião faz sentido para se referir a ele como uma estrutura.Mas muitos programadores inteligentes discordam de mim Neste ponto, esteja preparado para ler e entender o código escrito de qualquer forma.)

(C ++ tem regras diferentes. Dada uma declaração de struct blah , você pode se referir ao tipo como blah , mesmo sem um typedef. Usar um typedef pode tornar seu código C um pouco mais C ++ – como – se você acha que é um bom coisa.)

Outra diferença não apontada é que dar um nome à estrutura (ou seja, struct myStruct) também permite que você forneça declarações de encaminhamento da estrutura. Então, em algum outro arquivo, você poderia escrever:

 struct myStruct; void doit(struct myStruct *ptr); 

sem ter que ter access à definição. O que eu recomendo é que você combine seus dois exemplos:

 typedef struct myStruct{ int one; int two; } myStruct; 

Isso lhe dá a conveniência do nome typedef mais conciso, mas ainda permite que você use o nome completo da estrutura, se necessário.

Em C (não C ++), você tem que declarar variables ​​struct como:

 struct myStruct myVariable; 

Para poder usar o myStruct myVariable; em vez disso, você pode typedef o struct:

 typedef struct myStruct someStruct; someStruct myVariable; 

Você pode combinar a definição da struct e typedef la em uma única declaração que declara uma struct anônima e a typedef .

 typedef struct { ... } myStruct; 

Se você usa struct sem typedef , você sempre terá que escrever

 struct mystruct myvar; 

É ilegal escrever

 mystruct myvar; 

Se você usar o typedef , não precisará mais do prefixo struct .

Em C, as palavras-chave do especificador de tipo de estruturas, uniões e enumerações são obrigatórias, ou seja, você sempre terá que prefixar o nome do tipo (sua tag ) com struct , union ou enum quando se referir ao tipo.

Você pode se livrar das palavras-chave usando um typedef , que é uma forma de ocultar informações como o tipo real de um object não será mais visível ao declará-lo.

Portanto, é recomendado (veja, por exemplo, o guia de estilo de codificação do kernel do Linux , Capítulo 5) para fazer isso apenas quando você realmente deseja ocultar essas informações e não apenas para salvar algumas teclas digitadas.

Um exemplo de quando você deve usar um typedef seria um tipo opaco que é usado apenas com as funções / macros correspondentes do acessador.

A diferença vem quando você usa o struct .

A primeira maneira que você tem que fazer:

 struct myStruct aName; 

A segunda maneira permite remover a palavra-chave struct .

 myStruct aName; 

Você não pode usar declaração direta com o typedef struct .

A struct si é um tipo anônimo, portanto, você não tem um nome real para encaminhar declarar.

 typedef struct{ int one; int two; } myStruct; 

Uma declaração de encaminhamento como essa não funcionará:

 struct myStruct; //forward declaration fails void blah(myStruct* pStruct); //error C2371: 'myStruct' : redefinition; different basic types 

O typedef , como é com outras construções, é usado para dar um tipo de dados um novo nome. Neste caso, é feito principalmente para tornar o código mais limpo:

 struct myStruct blah; 

vs.

 myStruct blah; 

O código a seguir cria uma estrutura anônima com o alias myStruct :

 typedef struct{ int one; int two; } myStruct; 

Você não pode referenciá-lo sem o alias porque não especifica um identificador para a estrutura.

Eu vejo alguns esclarecimentos em ordem sobre isso. C e C ++ não definem tipos de maneira diferente. C ++ era originalmente nada mais que um conjunto adicional de includes no topo de C.

O problema que virtualmente todos os desenvolvedores de C / C ++ têm hoje é: a) as universidades não estão mais ensinando os fundamentos, eb) as pessoas não entendem a diferença entre uma definição e uma declaração.

A única razão pela qual tais declarações e definições existem é para que o vinculador possa calcular deslocamentos de endereço para os campos na estrutura. É por isso que a maioria das pessoas usa códigos que são escritos incorretamente – porque o compilador é capaz de determinar o endereçamento. O problema surge quando alguém tenta fazer algo avançar, como uma fila, ou uma linked list, ou piggying-backing uma estrutura de O / S.

Uma declaração começa com ‘struct’, uma definição começa com ‘typedef’.

Além disso, uma estrutura possui um label de declaração antecipada e um label definido. A maioria das pessoas não sabe disso e usa o label de declaração de encaminhamento como um label de definição.

Errado:

 struct myStruct { int field_1; ... }; 

Eles acabaram de usar a declaração de encaminhamento para rotular a estrutura – agora o compilador está ciente disso – mas não é um tipo realmente definido. O compilador pode calcular o endereçamento – mas não é assim que deveria ser usado, por razões que mostrarei momentaneamente.

As pessoas que usam essa forma de declaração devem sempre colocar “struct” em praticamente todas as referências a ela – porque não é um novo tipo oficial.

Em vez disso, qualquer estrutura que não se referir a si mesma deve ser declarada e definida apenas desta maneira:

 typedef struct { field_1; ... }myStruct; 

Agora é um tipo real, e quando usado você pode usar como ‘myStruct’ sem ter que prefixar com a palavra ‘struct’.

Se você quiser uma variável de ponteiro para essa estrutura, inclua um label secundário:

 typedef struct { field_1; ... }myStruct,*myStructP; 

Agora você tem uma variável de ponteiro para essa estrutura, personalizada para ela.

DECLARAÇÃO FUTURA–

Agora, aqui estão as coisas extravagantes, como a declaração antecipada funciona. Se você deseja criar um tipo que se refira a si mesmo, como uma linked list ou um elemento da fila, é necessário usar uma declaração de encaminhamento. O compilador não considera a estrutura definida até chegar ao ponto-e-vírgula no final, portanto ela é declarada antes desse ponto.

 typedef struct myStructElement { myStructElement* nextSE; field_1; ... }myStruct; 

Agora, o compilador sabe que, embora não saiba o que o tipo inteiro ainda é, ele ainda pode referenciá-lo usando a referência de encaminhamento.

Por favor declare e digite suas estruturas corretamente. Na verdade, há uma razão.

Com o último exemplo, você omite a palavra-chave struct ao usar a estrutura. Então, em todo lugar no seu código, você pode escrever:

 myStruct a; 

ao invés de

 struct myStruct a; 

Isso economiza digitação e pode ser mais legível, mas isso é uma questão de gosto