C++中的”结构”与’typedef struct’的区别?

Difference between 'struct' and 'typedef struct' in C++?

在C++中,有没有不同之处:

1
struct Foo { ... };

1
typedef struct { ... } Foo;


在C++中,只有细微的差别。这是C的延期,在这方面有区别。

C语言标准(C89第3.1.2.3条、C99第6.2.3条和C11第6.2.3条)规定了不同类别标识符的单独名称空间,包括标签标识符(用于struct/union/enum和普通标识符(用于typedef和其他标识符)。

如果你只是说:

1
2
struct Foo { ... };
Foo x;

您会得到一个编译器错误,因为Foo只在标记名称空间中定义。

你必须声明它是:

1
struct Foo x;

每当你想提到一个Foo时,你总是不得不称它为struct Foo。这会很快让人恼火,因此您可以添加一个typedef

1
2
struct Foo { ... };
typedef struct Foo Foo;

现在,struct Foo(在标记名称空间中)和plain Foo(在普通标识符名称空间中)都引用相同的东西,并且您可以自由声明不带struct关键字的Foo类型的对象。

构造:

1
typedef struct Foo { ... } Foo;

只是声明和typedef的缩写。

最后,

1
typedef struct { ... } Foo;

声明一个匿名结构并为它创建一个typedef。因此,使用此构造,它在标记命名空间中没有名称,只有typedef命名空间中的名称。这意味着它也不能被转发声明。如果要进行正向声明,必须在标记命名空间中为其指定一个名称。

在C++中,所有的EDCOX1,0,EDCOX1,1,E/OCX1,2,EDCX1,17个声明都像隐式EDOCX1,3’ED一样,只要名字不被另一个相同名称的声明所隐藏。详情请参阅迈克尔?伯尔的回答。


在这篇DDJ文章中,DanSaks解释了一个小的领域,如果你不定义你的结构(和类),bug可以在这个领域中蔓延。:

If you want, you can imagine that C++
generates a typedef for every tag
name, such as

1
typedef class string string;

Unfortunately, this is not entirely
accurate. I wish it were that simple,
but it's not. C++ can't generate such
typedefs for structs, unions, or enums
without introducing incompatibilities
with C.

For example, suppose a C program
declares both a function and a struct
named status:

1
int status(); struct status;

Again, this may be bad practice, but
it is C. In this program, status (by
itself) refers to the function; struct
status refers to the type.

If C++ did automatically generate
typedefs for tags, then when you
compiled this program as C++, the
compiler would generate:

1
typedef struct status status;

Unfortunately, this type name would
conflict with the function name, and
the program would not compile. That's
why C++ can't simply generate a
typedef for each tag.

In C++, tags act just like typedef
names, except that a program can
declare an object, function, or
enumerator with the same name and the
same scope as a tag. In that case, the
object, function, or enumerator name
hides the tag name. The program can
refer to the tag name only by using
the keyword class, struct, union, or
enum (as appropriate) in front of the
tag name. A type name consisting of
one of these keywords followed by a
tag is an elaborated-type-specifier.
For instance, struct status and enum
month are elaborated-type-specifiers.

Thus, a C program that contains both:

1
int status(); struct status;

behaves the same when compiled as C++.
The name status alone refers to the
function. The program can refer to the
type only by using the
elaborated-type-specifier struct
status.

So how does this allow bugs to creep
into programs? Consider the program in
Listing 1. This program defines a
class foo with a default constructor,
and a conversion operator that
converts a foo object to char const *.
The expression

1
p = foo();

in main should construct a foo object
and apply the conversion operator. The
subsequent output statement

1
2
cout << p << '
'
;

should display class foo, but it
doesn't. It displays function foo.

This surprising result occurs because
the program includes header lib.h
shown in Listing 2. This header
defines a function also named foo. The
function name foo hides the class name
foo, so the reference to foo in main
refers to the function, not the class.
main can refer to the class only by
using an elaborated-type-specifier, as
in

1
p = class foo();

The way to avoid such confusion
throughout the program is to add the
following typedef for the class name
foo:

1
typedef class foo foo;

immediately before or after the class
definition. This typedef causes a
conflict between the type name foo and
the function name foo (from the
library) that will trigger a
compile-time error.

I know of no one who actually writes
these typedefs as a matter of course.
It requires a lot of discipline. Since
the incidence of errors such as the
one in Listing 1 is probably pretty
small, you many never run afoul of
this problem. But if an error in your
software might cause bodily injury,
then you should write the typedefs no
matter how unlikely the error.

I can't imagine why anyone would ever
want to hide a class name with a
function or object name in the same
scope as the class. The hiding rules
in C were a mistake, and they should
not have been extended to classes in
C++. Indeed, you can correct the
mistake, but it requires extra
programming discipline and effort that
should not be necessary.


还有一个重要区别:typedefs不能被转发声明。因此,对于typedef选项,您必须#include包含typedef的文件,这意味着#include是您的.h的所有文件都包括该文件,无论它是否直接需要,等等。它肯定会影响到大型项目的构建时间。

如果没有typedef,在某些情况下,您可以在.h文件的顶部添加一个struct Foo;的正向声明,并且只在.cpp文件中添加#include的结构定义。


有区别,但很微妙。这样看:struct Foo引入了一种新的类型。第二个为未命名的struct类型创建一个名为foo(而不是新类型)的别名。

7.1.3 The typedef specifier

1 [...]

A name declared with the typedef specifier becomes a typedef-name. Within the scope of its declaration, a
typedef-name is syntactically equivalent to a keyword and names the type associated with the identifier in
the way described in Clause 8. A typedef-name is thus a synonym for another type. A typedef-name does not introduce a new type the way a class declaration (9.1) or enum declaration does.

8 If the typedef declaration defines an unnamed class (or enum), the first typedef-name declared by the declaration
to be that class type (or enum type) is used to denote the class type (or enum type) for linkage
purposes only (3.5). [ Example:

1
typedef struct { } *ps, S; // S is the class name for linkage purposes

因此,typedef总是用作另一类型的占位符/同义词。


不能对typedef结构使用forward声明。

结构本身是匿名类型,因此您没有要转发声明的实际名称。

1
2
3
4
typedef struct{
    int one;
    int two;
}myStruct;

这样的前瞻性声明是行不通的:

1
2
3
4
5
struct myStruct; //forward declaration fails

void blah(myStruct* pStruct);

//error C2371: 'myStruct' : redefinition; different basic types


结构是创建数据类型。typedef用于设置数据类型的昵称。


C++中的"TyButfStutt"和"Stult"之间的一个重要区别是,"TyPulfStruts"中的内联成员初始化将不起作用。

1
2
3
4
5
// the 'x' in this struct will NOT be initialised to zero
typedef struct { int x = 0; } Foo;

// the 'x' in this struct WILL be initialised to zero
struct Foo { int x = 0; };


C++中没有区别,但是我相信在C中,它将允许您在没有显式地执行的情况下声明结构FUO的实例:

1
struct Foo bar;