为什么允许使用`typedef struct x x`?

时间:2019-06-10 20:44:57

标签: c

我正在读modern C这本书,很惊讶发现以下代码有效

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

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

但是,这本书没有详细介绍。这是如何运作的?为什么point中的main()引用typedef,因为struct point是在之后定义的?

4 个答案:

答案 0 :(得分:6)

typedef struct point point;行有两件事:

  • 它将创建struct point
  • 转发声明
  • 它为struct point创建了一个类型别名,称为point

如果您需要在结构完全定义之前就知道其退出,则前向声明非常有用,例如:

typedef struct x X;

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

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

答案 1 :(得分:2)

在定义struct foo之前,可以在许多地方使用struct foo { ... }。这就是所谓的“不完整类型”。

它很有用,因为它使您可以定义抽象类型:

foo_header.h

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

通过这种方式,库的用户可以使用结构指针和在这些指针上工作的函数,而无需了解结构的实际定义方式,这对于封装非常有用。

它也用于递归类型:

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

C之所以支持它,是因为它易于实现:要编译使用struct foo *的代码,编译器只需知道指针的大小即可。它不在乎struct成员。

类似地,在您的typedef示例中,编译器不需要知道结构的详细信息即可为其创建类型别名。

答案 2 :(得分:2)

这有效,因为C具有多个名称空间

  • 标签(由goto或结尾的:消除歧义);
  • 标记structenumunion的名称(通过structunionenum关键字进行歧义):< / li> structunion成员的
  • 名称(由领先的.->消除,每个structunion类型都充当其名称自己的名称空间,因此不同的structunion类型可以使用相同的成员名称);
  • 所有其他标识符(变量和函数名称,typedef名称,枚举常量等)。

因此,您可以在同一代码中使用与标签,标签名称,成员名称和常规标识符相同的名称,并且编译器可以区分它们:

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
}

答案 3 :(得分:1)

此示例直接来自C标准的6.7.2.3节:

  

以下替代公式使用typedef机制:

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

它转发的内容声明了struct,然后为其创建了类型别名。