简体   繁体   中英

Why is `typedef struct x x` allowed?

I was reading the book modern C and was suprised to find that the following code works

typedef struct point point;
struct point {
    int x;
    int y;
};

int main() {
   point p = { .x = 1, .y = 2 };
}

However, the book does not go in detail about that. How does that work? Why does point in main() refer to the typedef since struct point is defined after that?

The line typedef struct point point; does two things:

  • It creates a forward declaration of struct point
  • It creates a type alias for struct point called point .

Forward declarations are useful if you need to know a struct exits before it is fully defined, for example:

typedef struct x X;

typedef struct y {
    int a;
    X *x;
} Y;

 struct x {
     int b;
     Y *y;
 };

You can use struct foo in many places before struct foo { ... } is defined. This is known as an "incomplete type".

It's useful because it lets you define abstract types:

foo_header.h

struct foo;  // abstract
struct foo *foo_create(void);
void do_stuff_with(struct foo *);
void foo_destroy(struct foo *);

This way users of the library can use struct pointers and functions that work on these pointers without knowing how the struct is actually defined, which is nice for encapsulation.

It's also used in recursive types:

struct node {
    int data;
    struct node *next;  // struct node isn't defined yet!
};
// here the definition of struct node is complete

C supports it because it's easy to implement: To compile code that uses struct foo * , the compiler only has to know how big the pointer is. It doesn't care about the struct members.

Similarly, in your typedef example, the compiler doesn't need to know the details of the struct to create a type alias for it.

This works because C has multiple name spaces :

  • labels (disambiguated by goto or trailing : );
  • tag names for struct , enum , and union (disambiguated by the struct , union , or enum keywords):
  • names for struct and union members (disambiguated by a leading . or -> , each struct or union type acts as its own namespace, so different struct and union types can use the same member names);
  • all other identifiers (variable and function names, typedef names, enumeration constants, etc.).

So you can use the same name as a label, a tag name, a member name, and a regular identifier all in the same code, and the compiler is able to distinguish between them:

struct x { int x; }; // tag name, member name

void foo( struct x x ) // tag name, all other identifiers
{
  if ( x.x ) // all other identifiers, member name
    goto x;  // label name

  // do something here

  x: printf( "At label x\n" ); // label name
}

This example comes straight from C standard, Section 6.7.2.3:

The following alternative formulation uses the typedef mechanism:

typedef struct tnode TNODE;
struct tnode {
 int count;
 TNODE *left, *right;
};
TNODE s, *sp;

What it does it forward declares struct, and than creates a type alias for it.

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