简体   繁体   English

C 中的 struct 和 typedef struct 之间的真正区别是什么?

[英]What is the real difference between struct and typedef struct in C?

There are two main ways of defining structs:定义结构体有两种主要方式:

struct triangle_s {
    int a,b,c;
};

and

typedef struct triangle_s {
    int a,b,c;
} triangle;

It has been asked many times but every answer is about not having to write struct so many times when using the typedef variant.已经被问过很多次了,但每个答案都是关于在使用typedef变体时不必多次编写struct Is there a real difference other than you can avoid repeating the struct keyword?除了可以避免重复struct关键字之外,还有什么真正的区别吗? I've heard that you should never use the typedef variant in C but nobody said why.我听说你永远不应该在 C 中使用typedef变体,但没有人说为什么。

There is no difference, typedef just removes the requirement to prefix variable declarations with struct .没有区别, typedef只是删除了使用struct前缀变量声明的要求。

Whether or not to typedef structs by default is a religious war fought mostly by people with too much time on their hands.默认情况下是否使用typedef结构是一场宗教战争,主要是由手头有太多时间的人进行的。 When working inside an existing code base you should do whatever the coding standard or the surrounding code does.在现有代码库中工作时,您应该执行编码标准或周围代码所做的任何操作。 For your own personal code do whatever you prefer.对于您自己的个人代码,请随心所欲。

There is no such thing as a "typedef struct".没有“typedef 结构”这样的东西。

Part the first: struct ure types第一部分: struct类型

struct introduces a structure, which is an aggregate datatype consisting of a set of named members. struct引入了一种结构,它是由一组命名成员组成的聚合数据类型。 (Arrays are also aggregates, but they consist of a number of identical members which are indexed. Unions have a set of member names, but can only contain one member at a time, so they are not aggregates. You probably didn't need to know that.) (数组也是聚合,但它们由许多被索引的相同成员组成。联合有一组成员名称,但一次只能包含一个成员,因此它们不是聚合。您可能不需要我知道。)

Structure types usually have tags, so the actual typename will be something like struct Triangle .结构类型通常有标签,因此实际的类型名称将类似于struct Triangle The tag ( Triangle ) is not in the same namespace as identifiers, so there is no problem using a tag which is also used for another purpose.标签 ( Triangle ) 与标识符不在同一个命名空间中,因此使用也用于其他目的的标签没有问题。 Some people like to append tags with _s and _u , indicating that they are structure or union tags respectively, but that's not my style;有些人喜欢用_s_u附加标签,分别表示它们是结构或联合标签,但这不是我的风格; I prefer to name types in CamelCase, and for me a structure or union tag is standing in for a typename.我更喜欢在 CamelCase 中命名类型,对我来说,结构或联合标记代表类型名。 But of course you are free to use your own conventions.但是当然您可以自由使用自己的约定。

If you use struct SomeTag in a program, you are effectively declaring that there is a structure whose tag is SomeTag .如果您在程序中使用struct SomeTag ,您实际上是在声明存在一个其标记为SomeTag的结构。 You're not required to fill in the declaration by naming or describing the structure's members, unless you need to refer to them in your code.您不需要通过命名或描述结构的成员来填写声明,除非您需要在代码中引用它们。 A structure whose members have not (yet) been declared is called incomplete , but it can still be used as part of a pointer type because the C standard guarantees that all structure pointers have the same format, regardless of the contents of the structure.其成员尚未(尚未)声明的结构称为不完整的,但它仍然可以用作指针类型的一部分,因为 C 标准保证所有结构指针都具有相同的格式,而不管结构的内容如何。 (That doesn't make them interchangeable, but it does mean that the compiler knows how big the pointers are.) A structure which never has its members defined and which is used only as the the target of a pointer type is called opaque . (这并不使它们可以互换,但它确实意味着编译器知道指针有多大。)从未定义其成员并且仅用作指针类型的目标的结构称为opaque

You can complete the declaration of a structure by adding a block of member declarations.您可以通过添加成员声明块来完成结构的声明。 So所以

struct Triangle {
    int a,b,c;
};

first declares that there is a structure whose name is struct Triangle , and then fills in the definition of that structure by declaring three named members which are all int s.首先声明存在一个名称为struct Triangle ,然后通过声明三个都是int的命名成员来填充该结构的定义。

Union declarations and definitions are all very similar, by the way.顺便说一下,联合声明和定义都非常相似。

A structure definition can be used in a declaration as though it were a type name.结构定义可以在声明中使用,就好像它是一个类型名称一样。 Or to put it another way, you can declare the tag for a structure type, immediately fill in the fields, and then declare one or more variables of that type:或者换句话说,您可以声明结构类型的标记,立即填写字段,然后声明该类型的一个或多个变量:

struct Triangle { int a, b, c; } aTriangle, anotherTriangle;

That's not a very common style, but it's important to know that the syntax is possible.这不是一种非常常见的风格,但重要的是要知道语法是可能的。

Finally, it is legal define a structure without giving it a tag.最后,定义一个结构而不给它一个标签是合法的。 Tagless structure types have a quirk: normally no two structure types can have the same tag, but all tagless structures are distinct.无标签结构类型有一个怪癖:通常没有两种结构类型可以具有相同的标签,但所有无标签结构都是不同的。 That means that you can declare a structure type which effectively has no name, and which is different from any other structure type, even a structure type with exactly the same members.这意味着您可以声明一个结构类型,它实际上没有名称,并且不同于任何其他结构类型,甚至是具有完全相同成员的结构类型。 That can be slightly useful if you have an aggregate which will only ever have one instance (a "singleton"), although I wouldn't really ever use this style myself.如果您有一个只有一个实例(“单例”)的聚合,这可能会稍微有用,尽管我自己不会真正使用这种样式。 But after a small detour, we'll see another use for this feature.但经过一小段弯路后,我们将看到此功能的另一种用途。

Part the second: type aliases第二部分:类型别名

C type names can be quite complicated, since they can be built up out of pieces. C 类型名称可能非常复杂,因为它们可以由多个部分组成。 For example, const struct Triangle*[8] is an array of eight members, each of which is a pointer to an unmodifiable struct Triangle .例如, const struct Triangle*[8]是一个包含八个成员的数组,每个成员都是一个指向不可修改的struct Triangle的指针。 double (*)(const struct Triangle*[8]) is a function which accepts one such array as an argument (or, more accurately, which accepts a pointer to the first element of such an array, because of array-to-pointer decay. But that's not relevant here.) double (*)(const struct Triangle*[8])是一个函数,它接受一个这样的数组作为参数(或者,更准确地说,它接受一个指向这样一个数组的第一个元素的指针,因为数组到指针衰变。但这在这里无关紧要。)

To make complex types a bit easier to use, C allows you to declare an alias name for a type.为了使复杂类型更易于使用,C 允许您为类型声明别名。 Aliases are declared with typedef and otherwise look exactly like the declaration of a variable.别名使用typedef声明,否则看起来与变量声明完全一样。 So, for example, you can declare a variable of type int with因此,例如,您可以声明一个int类型的变量

int someNumber;

and thus you can declare an alias for the type int with因此你可以为int类型声明一个别名

typedef int someType;

Similarly, you could declare an array of eight pointers to const Triangle elements with类似地,您可以声明一个包含八个指向const Triangle元素的指针的数组

const Triangle* eightSlices[8];

In exactly the same way, you can declare a name for the type of such an array with:以完全相同的方式,您可以为此类数组的类型声明一个名称:

typedef const Triangle* EightSlices[8];

Note that the name of the type goes exactly where the name of the object would go, which can be somewhere in the middle of the declaration.请注意,类型的名称恰好位于对象名称所在的位置,它可以位于声明中间的某个位置。

Part the third: both of the above in one statement第三部分:以上两者合二为一

As a simple example of declaring type aliases, here's how you declare an alias for a structure type:作为声明类型别名的简单示例,以下是为结构类型声明别名的方法:

An incomplete structure definition:一个不完整的结构定义:

typedef struct Triangle Triangle;

Or a complete structure definition:或者一个完整的结构定义:

typedef struct Triangle {
   int a, b, c;
} Triangle;

Or both, separately (and these could go in either order):或者两者都分开(这些可以按任何顺序进行):

typedef struct Triangle Triangle;
struct Triangle {
    int a, b, c;
};

Remember that structure tags and other identifiers (such as type aliases) are in different namespaces, so there is no conflict between the two uses of Triangle above.请记住,结构标记和其他标识符(例如类型别名)在不同的命名空间中,因此上面Triangle的两种用法没有冲突。 Some programmers feel that it is necessary to distinguish between them, even though one of them can only be used immediately after the word struct and the other one cannot be used following the word struct .一些程序员觉得有必要区分它们,即使其中一个只能紧跟在struct之后使用,另一个不能在struct Others -- and I think you can guess that I fall into this crowd -- find it convenient to deliberately use the same name for both, relying on the presence or absence of the word struct to let us know whether it is a type alias or a tag.其他人——我想你可以猜到我属于这群人——发现故意为两者使用相同的名称很方便,依靠单词struct的存在或不存在来让我们知道它是类型别名还是一个标签。 (And, more commonly, to indicate that we have no intention of ever again using the tag.) (而且,更常见的是,表明我们无意再次使用该标签。)

So, back to my opening comment:所以,回到我的开场白:

There is no such thing as a "typedef struct".没有“typedef 结构”这样的东西。

And there isn't.而且没有。 What we have here is a very ordinary struct , declared and defined.我们这里有一个非常普通的struct ,声明和定义。 And we have a very ordinary type alias which gives an alternate name for that struct .我们有一个非常普通的类型别名,它为该struct提供了一个替代名称。 And that's it.就是这样。

Note that you can give an alias to an anonymous type (such as a tagless structure type), after which the type is no longer anonymous.请注意,您可以为匿名类型(例如无标记结构类型)提供别名,之后该类型就不再是匿名的。 So there are some people who would leave out the tag in the above definition:所以有些人会在上面的定义中省略标签:

typedef struct {
    int a, b, c;
} Triangle;

That looks a lot like the singleton structure type mentioned above, but since it is a type it can be used to declare multiple instances.这看起来很像上面提到的单例结构类型,但由于它是一种可以用于声明多个实例的类型。 But I don't actually recommend this style.但我实际上并不推荐这种风格。

To each their own: an ignorable appendix各自为政:一个可忽略的附录

Everyone has their own style preferences, and most of these preferences are valid.每个人都有自己的风格偏好,而这些偏好大多是有效的。 Most of us have worked on more than one project, and since every project tends to develop its own style guide, we need to learn how to accept and use different styles in different projects.我们大多数人都参与过不止一个项目,而且由于每个项目都有自己的风格指南,我们需要学习如何在不同的项目中接受和使用不同的风格。 But I think most of us have some style with which we feel most comfortable, which we will revert to when we're writing code just for ourselves (or when we're starting a project with the intention of attracting colleagues prepared to conform to our style).但是我认为我们中的大多数人都有一些我们觉得最舒服的风格,当我们为自己编写代码时(或者当我们开始一个项目以吸引准备符合我们的同事的意图时,我们会恢复这种风格)风格)。 And what I've used above is my style.我上面使用的是我的风格。

In fact, I try to avoid the condensed declaration+definition+alias syntax, preferring the two-declaration version shown above:事实上,我尽量避免使用压缩声明+定义+别名的语法,更喜欢上面显示的两个声明版本:

typedef struct Triangle Triangle; /* type alias */
struct Triangle {
  int a, b, c;
};                                /* type definition */

The reason I prefer that is that it lets me define types with self-referring members, such as linked lists and trees:我更喜欢这样做的原因是它允许我使用自引用成员定义类型,例如链表和树:

typedef struct TriangleList TriangleList;
struct TriangleList {
    Triangle      slice;
    TriangleList* next;
};

(If I hadn't forward-aliased the type, I would have had to declare the member as struct TriangleList* next; which makes for even uglier alignment.) (如果我没有对类型进行前向别名化,我将不得不将成员声明为struct TriangleList* next;这会导致更难看的对齐。)

Sometimes I end up with mutually referring types, and in that case, I need to gather the aliases together before any of the structure definitions.有时我最终会得到相互引用的类型,在这种情况下,我需要在任何结构定义之前将别名收集在一起。 That also can be advantageous, because the alias definitions allow for opaque use of pointers to the type and can therefore be placed into a public header which does not include the type definitions at all.这也可能是有利的,因为别名定义允许不透明地使用指向类型的指针,因此可以放置到根本不包含类型定义的公共标头中。

But that's all just me.但这只是我。 Feel free to ignore it.随意忽略它。

There are two main ways of defining structs定义结构体有两种主要方式

No, there is only one:不,只有一个:

struct triangle_s {
    int a,b,c;
}

. . Such a definition can appear in several contexts, including, but not limited to, the two you have presented, but it's important to understand that the two statements you offered are each more than a structure definition.这样的定义可以出现在多种上下文中,包括但不限于您提供的两种情况,但重要的是要了解您提供的两种语句都不仅仅是结构定义。

This one's "more" is admittedly a bit vacuous:不可否认,这个“更多”有点空洞:

 struct triangle_s { int a,b,c; };

It is a declaration of the structure type and zero objects of that type.它是结构类型该类型的零对象的声明。 There is an identifier list between the closing brace and the semicolon, which in that particular case happens to be empty.在右大括号和分号之间有一个标识符列表,在这种特殊情况下它恰好是空的。 But it could also be this, for example:但也可能是这样,例如:

struct triangle_s {
    int a,b,c;
} triangle;

, which defines the type struct triangle_s and declares triangle as an object of that type. ,它定义了类型struct triangle_s并将triangle声明为该类型的对象。 And of course, that leads us to the second example当然,这将我们引向第二个例子

typedef struct triangle_s { int a,b,c; } triangle;

, which is exactly the same as the preceding one, except that the typedef keyword indicates that the identifier(s) declared are type aliases instead of object identifiers. ,这与前面的完全相同,除了typedef关键字指示声明的标识符是类型别名而不是对象标识符。 Again, the structure definition is just the part from the struct keyword to the associated closing brace, and note that typedef can be used to declare aliases for any type, not just structures.同样,结构定义只是从struct关键字到关联的右大括号的部分,请注意typedef可用于声明任何类型的别名,而不仅仅是结构。

Is there a real difference other than you can avoid repeating the struct keyword?除了可以避免重复 struct 关键字之外,还有什么真正的区别吗?

You have a definition of struct triangle_s either way, and you can use that form of its type name either way.无论哪种方式,您都有struct triangle_s的定义,并且您可以以任何一种方式使用其类型名称的该形式。 In the latter case, you can also use triangle (wherever it is in scope) as an alias for struct triangle_s , for any and every purpose that could be served by the form struct triangle_s , with no difference in meaning.在后一种情况下,你也可以用triangle (无论它是在范围内)作为一个别名struct triangle_s ,对于任何和一切可以由形式送达目的struct triangle_s ,在意义上没有区别。 That is the point of typedef .这就是typedef

I've heard that you should never use the typedef variant in C but nobody said why.我听说你永远不应该在 C 中使用 typedef 变体,但没有人说为什么。

Some people argue against the second form as a matter of style , on the basis that it commingles two different things in the same declaration -- a definition of the structure type and a declaration of an alias for it.有些人从风格上反对第二种形式,因为它在同一个声明中混合了两种不同的东西——结构类型的定义和它的别名声明。 That line of reasoning continues that if you want the typedef , then you should declare it separately:这条推理线继续说,如果你想要typedef ,那么你应该单独声明它:

struct triangle_s {
    int a,b,c;
};

typedef struct triangle_s triangle;

Again, however, this is a matter of code style, not functionality.然而,这又是一个代码风格问题,而不是功能问题。

There are other style points surrounding the use of typedef about which I, personally, feel much more strongly.关于typedef的使用,还有其他一些风格点,我个人对此有更强烈的感受。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM