简体   繁体   中英

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. Is there a real difference other than you can avoid repeating the struct keyword? I've heard that you should never use the typedef variant in C but nobody said why.

There is no difference, typedef just removes the requirement to prefix variable declarations with struct .

Whether or not to typedef structs by default is a religious war fought mostly by people with too much time on their hands. 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".

Part the first: struct ure types

struct introduces a structure, which is an aggregate datatype consisting of a set of named members. (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 . 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. 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; I prefer to name types in CamelCase, and for me a structure or union tag is standing in for a typename. 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 . 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. (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 .

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.

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. For example, const struct Triangle*[8] is an array of eight members, each of which is a pointer to an unmodifiable 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.)

To make complex types a bit easier to use, C allows you to declare an alias name for a type. Aliases are declared with typedef and otherwise look exactly like the declaration of a variable. So, for example, you can declare a variable of type int with

int someNumber;

and thus you can declare an alias for the type int with

typedef int someType;

Similarly, you could declare an array of eight pointers to const Triangle elements with

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. 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 . 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. (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".

And there isn't. What we have here is a very ordinary struct , declared and defined. And we have a very ordinary type alias which gives an alternate name for that 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.)

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. 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. 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.

Is there a real difference other than you can avoid repeating the struct keyword?

You have a definition of struct triangle_s either way, and you can use that form of its type name either way. 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. That is the point of typedef .

I've heard that you should never use the typedef variant in C but nobody said why.

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:

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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