No C ++, há apenas uma diferença sutil. É uma remanescente de C, na qual faz a diferença.
O padrão da linguagem C ( C89 §3.1.2.3 , C99 §6.2.3 e C11 §6.2.3 ) exige espaços de nomes separados para diferentes categorias de identificadores, incluindo identificadores de tag (para struct/ union/ enum) e identificadores comuns (para typedefe outros identificadores) .
Se você acabou de dizer:
structFoo{...};Foo x;
você receberia um erro do compilador, porque Fooé definido apenas no namespace da tag.
Você teria que declarar como:
structFoo x;
Sempre que você quiser se referir a Foo, sempre precisará chamá-lo de struct Foo. Isso fica irritante rapidamente, então você pode adicionar um typedef:
structFoo{...};typedefstructFooFoo;
Agora struct Foo(no namespace da tag) e simplesmente Foo(no namespace do identificador comum) se referem à mesma coisa, e você pode declarar livremente objetos do tipo Foosem a structpalavra - chave.
A construção:
typedefstructFoo{...}Foo;
é apenas uma abreviação para a declaração e typedef.
Finalmente,
typedefstruct{...}Foo;
declara uma estrutura anônima e cria um typedefpara ela. Portanto, com essa construção, ele não tem um nome no espaço para nome da tag, apenas um nome no espaço para nome typedef. Isso significa que também não pode ser declarado para a frente. Se você deseja fazer uma declaração de encaminhamento, é necessário dar um nome a ela no espaço de nome da tag .
Em C ++, todos os struct/ union/ enum/ classdeclarações agir como eles são implicitamente typedef'ed, desde que o nome não está oculta por outra declaração com o mesmo nome. Veja a resposta de Michael Burr para obter todos os detalhes.
Enquanto o que você diz é verdade, AFAIK, a instrução 'typedef struct {...} Foo;' cria um alias para uma estrutura sem nome.
Dirkgently 04/03/09
25
Boa captura, há uma diferença sutil entre "typedef struct Foo {...} Foo;" e "typedef struct {...} Foo;".
Adam Rosenfield
8
Em C, as tags struct, tags union e tags de enumeração compartilham um namespace, em vez de (struct e union) usando dois, conforme reivindicado acima; o espaço de nomes referenciado para nomes de typedef é de fato separado. Isso significa que você não pode ter os dois 'union x {...};' e 'struct x {...};' em um único escopo.
11139 Jonathan Leffler
10
Além da coisa não-muito-tipada, outra diferença entre os dois trechos de código na pergunta é que Foo pode definir um construtor no primeiro exemplo, mas não no segundo (já que classes anônimas não podem definir construtores ou destruidores) .
21609 Steve Jessop
1
@Lazer: Não são diferenças sutis, mas Adam meios (como ele continua a dizer) que você pode usar 'Tipo var' para declarar variáveis sem um typedef.
Fred Nurk
226
Em este artigo DDJ , Dan Saks explica uma pequena área onde os erros podem rastejar através Se você não typedef suas estruturas (e classes!):
Se desejar, você pode imaginar que o C ++ gera um typedef para cada nome de tag, como
typedefclass string string;
Infelizmente, isso não é totalmente preciso. Eu gostaria que fosse assim tão simples, mas não é. O C ++ não pode gerar esses typedefs para estruturas, uniões ou enumerações sem introduzir incompatibilidades com C.
Por exemplo, suponha que um programa C declare uma função e uma estrutura denominada status:
int status();struct status;
Novamente, isso pode ser uma prática ruim, mas é C. Nesse programa, status (por si só) refere-se à função; status de estrutura refere-se ao tipo
Se o C ++ gerou automaticamente typedefs para tags, quando você compilou este programa como C ++, o compilador geraria:
typedefstruct status status;
Infelizmente, esse nome de tipo entra em conflito com o nome da função e o programa não é compilado. É por isso que o C ++ não pode simplesmente gerar um typedef para cada tag.
No C ++, as tags agem exatamente como os nomes de typedef, exceto que um programa pode declarar um objeto, função ou enumerador com o mesmo nome e o mesmo escopo de uma tag. Nesse caso, o nome do objeto, função ou enumerador oculta o nome da marca. O programa pode se referir ao nome da marca apenas usando a classe de palavra-chave, struct, union ou enum (conforme apropriado) na frente do nome da marca. Um nome de tipo que consiste em uma dessas palavras-chave seguidas por uma tag é um especificador de tipo elaborado. Por exemplo, o status da estrutura e o mês enum são especificadores de tipo elaborado.
Assim, um programa C que contém ambos:
int status();struct status;
se comporta da mesma maneira quando compilado como C ++. Somente o status do nome se refere à função. O programa pode se referir ao tipo apenas usando o status de estrutura do especificador de tipo elaborado.
Então, como isso permite que os erros entrem nos programas? Considere o programa na
Listagem 1 . Este programa define uma classe foo com um construtor padrão e um operador de conversão que converte um objeto foo em char const *. A expressão
p = foo();
O main deve construir um objeto foo e aplicar o operador de conversão. A instrução de saída subsequente
cout << p <<'\n';
deve exibir a classe foo, mas não. Ele exibe a função foo.
Esse resultado surpreendente ocorre porque o programa inclui o cabeçalho lib.h mostrado na Listagem 2 . Este cabeçalho define uma função também chamada foo. O nome da função foo oculta o nome da classe foo, portanto, a referência a foo no main se refere à função, não à classe. main pode se referir à classe apenas usando um especificador de tipo elaborado, como em
p =class foo();
A maneira de evitar essa confusão em todo o programa é adicionar o seguinte typedef ao nome da classe foo:
typedefclass foo foo;
imediatamente antes ou depois da definição da classe. Esse typedef causa um conflito entre o nome do tipo foo e o nome da função foo (da biblioteca) que acionará um erro em tempo de compilação.
Não conheço ninguém que realmente escreva esses typedefs, como é óbvio. Exige muita disciplina. Como a incidência de erros como o da Listagem 1 é provavelmente muito pequena, você nunca enfrenta esse problema. Mas se um erro no seu software puder causar lesões corporais, você deverá escrever os typedefs, independentemente do quão improvável seja o erro.
Não consigo imaginar por que alguém iria querer ocultar um nome de classe com uma função ou nome de objeto no mesmo escopo da classe. As regras de ocultação em C foram um erro e não deveriam ter sido estendidas às classes em C ++. Na verdade, você pode corrigir o erro, mas requer disciplina e esforço extra de programação que não devem ser necessários.
Caso você tente "class foo ()" e ele falhe: No ISO C ++, "class foo ()" é uma construção ilegal (o artigo foi escrito '97, antes da padronização, ao que parece). Você pode colocar "typedef class foo foo;" no main, então você pode dizer "foo ();" (porque então, o typedef-name é lexicamente mais próximo do que o nome da função). Sintaticamente, em T (), T deve ser um especificador de tipo simples. especificadores de tipo elaborados não são permitidos. Ainda assim, é uma boa resposta, é claro.
Johannes Schaub - litb
Listing 1e os Listing 2links estão quebrados. Dar uma olhada.
Prasoon Saurav 13/10/10
3
Se você usar convenções de nomenclatura diferentes para classes e funções, também evitará o conflito de nomenclatura sem precisar adicionar typedefs extras.
Zstewart
64
Mais uma diferença importante: typedefs não podem ser declarados para a frente. Portanto, para a typedefopção, você deve #includeo arquivo que contém o typedef, ou seja, tudo o que #includeé seu .htambém inclui esse arquivo, se ele precisa diretamente ou não, e assim por diante. Definitivamente, pode impactar seus tempos de construção em projetos maiores.
Sem o typedef, em alguns casos, você pode adicionar uma declaração de encaminhamento struct Foo;no topo do seu .harquivo e apenas #includea definição de estrutura no seu .cpparquivo.
Por que a exposição da definição de estrutura afeta o tempo de construção? O compilador faz uma verificação extra, mesmo que não precise (dada a opção typedef, para que o compilador conheça a definição) quando vê algo como Foo * nextFoo; ?
Rich
3
Não é realmente uma verificação extra, é apenas mais um código que o compilador precisa lidar. Para cada arquivo cpp que encontra esse typedef em algum lugar de sua cadeia de inclusão, compila o typedef. Em projetos maiores, o arquivo .h contendo o typedef pode facilmente ser compilado centenas de vezes, embora os cabeçalhos pré-compilados ajudem bastante. Se você conseguir usar uma declaração de encaminhamento, será mais fácil limitar a inclusão do .h que contém a especificação de estrutura completa apenas ao código que realmente importa e, portanto, o arquivo de inclusão correspondente é compilado com menos frequência.
31414 Joe
@ no comentário anterior (que não seja o proprietário da postagem). Eu quase perdi sua resposta. Mas obrigado pela informação.
Rich
Isso não é mais verdade no C11, onde é possível digitar a mesma estrutura com o mesmo nome várias vezes.
michaelmeyer
32
Não é uma diferença, mas sutil. Veja desta maneira: struct Foointroduz um novo tipo. O segundo cria um alias chamado Foo (e não um novo tipo) para um structtipo sem nome .
7.1.3 O especificador typedef
1 [...]
Um nome declarado com o especificador typedef se torna um nome typedef. No escopo de sua declaração, um typedef-name é sintaticamente equivalente a uma palavra-chave e nomeia o tipo associado ao identificador da maneira descrita na Cláusula 8. Um typedef-name é, portanto, sinônimo de outro tipo. Um typedef-name não introduz um novo tipo da mesma forma que uma declaração de classe (9.1) ou enum.
8 Se a declaração typedef definir uma classe (ou enum) sem nome, o primeiro nome de typedef declarado pela declaração como esse tipo de classe (ou tipo de enum) será usado para indicar o tipo de classe (ou tipo de enum) apenas para fins de vinculação ( 3.5) [Exemplo:
typedefstruct{}*ps, S;// S is the class name for linkage purposes
Portanto, um typedef sempre é usado como espaço reservado / sinônimo para outro tipo.
Não recebo o segundo erro para o protótipo de função. Por que diz "redefinição; diferentes tipos básicos"? o compilador não precisa saber como é a definição do myStruct, certo? Independentemente do código (o tipo typedef ou a declaração forward), myStruct indica um tipo de estrutura, certo?
Rich
@ Rich Está reclamando que há um conflito de nomes. Há uma declaração direta dizendo "procure uma estrutura chamada myStruct" e, em seguida, há o typedef que está renomeando uma estrutura sem nome como "myStruct".
Yochai Timmer #
Você quer dizer colocar declaração typedef e forward no mesmo arquivo? Eu fiz, e o gcc o compilou bem. myStruct é corretamente interpretado como a estrutura sem nome. Tag myStructvive no namespace da tag e typedef_ed myStructvive no namespace normal, onde outros identificadores, como nome da função, nomes de variáveis locais, residem. Portanto, não deve haver nenhum conflito. Eu posso mostrar meu código se você duvidar de algum erro.
Rich
@ Rich GCC dá o mesmo erro, o texto varia um pouco: gcc.godbolt.org/…
Yochai Timmer
Acho que entendo que quando você só tem uma typedefdeclaração encaminhada com o typedefnome ed, não se refere à estrutura sem nome. Em vez disso, a declaração de encaminhamento declara uma estrutura incompleta com tag myStruct. Além disso, sem ver a definição de typedef, o protótipo de função que usa o typedefnome ed não é legal. Portanto, temos que incluir todo o typedef sempre que precisarmos usar myStructpara denotar um tipo. Corrija-me se eu não entendi você. Obrigado.
Rich
0
Uma diferença importante entre uma 'typedef struct' e uma 'struct' no C ++ é que a inicialização do membro embutido em 'typedef structs' não funcionará.
// the 'x' in this struct will NOT be initialised to zerotypedefstruct{int x =0;}Foo;// the 'x' in this struct WILL be initialised to zerostructFoo{int x =0;};
Não é verdade. Nos dois casos, xé inicializado. Veja o teste no IDE online do Coliru (eu o inicializei para 42, portanto é mais óbvio do que com zero que a atribuição realmente ocorreu).
Colin D Bennett
Na verdade, eu testei no Visual Studio 2013 e não foi inicializado. Esse foi um problema que encontramos no código de produção. Todos os compiladores são diferentes e só precisam atender a certos critérios.
user2796283
-3
Não há diferença em C ++, mas acredito que em C permitiria declarar instâncias da estrutura Foo sem fazer explicitamente:
Respostas:
No C ++, há apenas uma diferença sutil. É uma remanescente de C, na qual faz a diferença.
O padrão da linguagem C ( C89 §3.1.2.3 , C99 §6.2.3 e C11 §6.2.3 ) exige espaços de nomes separados para diferentes categorias de identificadores, incluindo identificadores de tag (para
struct
/union
/enum
) e identificadores comuns (paratypedef
e outros identificadores) .Se você acabou de dizer:
você receberia um erro do compilador, porque
Foo
é definido apenas no namespace da tag.Você teria que declarar como:
Sempre que você quiser se referir a
Foo
, sempre precisará chamá-lo destruct Foo
. Isso fica irritante rapidamente, então você pode adicionar umtypedef
:Agora
struct Foo
(no namespace da tag) e simplesmenteFoo
(no namespace do identificador comum) se referem à mesma coisa, e você pode declarar livremente objetos do tipoFoo
sem astruct
palavra - chave.A construção:
é apenas uma abreviação para a declaração e
typedef
.Finalmente,
declara uma estrutura anônima e cria um
typedef
para ela. Portanto, com essa construção, ele não tem um nome no espaço para nome da tag, apenas um nome no espaço para nome typedef. Isso significa que também não pode ser declarado para a frente. Se você deseja fazer uma declaração de encaminhamento, é necessário dar um nome a ela no espaço de nome da tag .Em C ++, todos os
struct
/union
/enum
/class
declarações agir como eles são implicitamentetypedef
'ed, desde que o nome não está oculta por outra declaração com o mesmo nome. Veja a resposta de Michael Burr para obter todos os detalhes.fonte
Em este artigo DDJ , Dan Saks explica uma pequena área onde os erros podem rastejar através Se você não typedef suas estruturas (e classes!):
fonte
Listing 1
e osListing 2
links estão quebrados. Dar uma olhada.Mais uma diferença importante:
typedef
s não podem ser declarados para a frente. Portanto, para atypedef
opção, você deve#include
o arquivo que contém otypedef
, ou seja, tudo o que#include
é seu.h
também inclui esse arquivo, se ele precisa diretamente ou não, e assim por diante. Definitivamente, pode impactar seus tempos de construção em projetos maiores.Sem o
typedef
, em alguns casos, você pode adicionar uma declaração de encaminhamentostruct Foo;
no topo do seu.h
arquivo e apenas#include
a definição de estrutura no seu.cpp
arquivo.fonte
Não é uma diferença, mas sutil. Veja desta maneira:
struct Foo
introduz um novo tipo. O segundo cria um alias chamado Foo (e não um novo tipo) para umstruct
tipo sem nome .Portanto, um typedef sempre é usado como espaço reservado / sinônimo para outro tipo.
fonte
Você não pode usar a declaração de encaminhamento com a estrutura typedef.
A estrutura em si é do tipo anônimo, portanto, você não tem um nome real para encaminhar a declaração.
Uma declaração encaminhada como esta não funcionará:
fonte
myStruct
vive no namespace da tag e typedef_edmyStruct
vive no namespace normal, onde outros identificadores, como nome da função, nomes de variáveis locais, residem. Portanto, não deve haver nenhum conflito. Eu posso mostrar meu código se você duvidar de algum erro.typedef
declaração encaminhada com otypedef
nome ed, não se refere à estrutura sem nome. Em vez disso, a declaração de encaminhamento declara uma estrutura incompleta com tagmyStruct
. Além disso, sem ver a definição detypedef
, o protótipo de função que usa otypedef
nome ed não é legal. Portanto, temos que incluir todo o typedef sempre que precisarmos usarmyStruct
para denotar um tipo. Corrija-me se eu não entendi você. Obrigado.Uma diferença importante entre uma 'typedef struct' e uma 'struct' no C ++ é que a inicialização do membro embutido em 'typedef structs' não funcionará.
fonte
x
é inicializado. Veja o teste no IDE online do Coliru (eu o inicializei para 42, portanto é mais óbvio do que com zero que a atribuição realmente ocorreu).Não há diferença em C ++, mas acredito que em C permitiria declarar instâncias da estrutura Foo sem fazer explicitamente:
fonte
Struct é criar um tipo de dados. O typedef é definir um apelido para um tipo de dados.
fonte