What is 'forward declaration' and the difference between 'typedef struct X' and 'struct X'?
我是C编程的初学者,我知道
1 2 3 | typedef struct { some members; } struct_name; |
然后它将类似于为匿名结构提供别名(因为它没有标记名)。因此不能用于转发声明。我不知道远期申报是什么意思。
另外,我想知道以下代码:
1 2 3 | typedef struct NAME { some members; } struct_alias; |
此外,我们可以像这样声明一个
1 | struct_alias variable1; |
和/或类似:
1 | struct NAME variable2; |
或类似:
1 | NAME variable3; |
当需要使用循环结构声明时,
1 2 3 4 5 6 7 8 9 | struct a { struct b * b_pointer; int c; }; struct b { struct a * a_pointer; void * d; }; |
当
当您对匿名结构进行typedef时,编译器将不允许您在typedef之前使用它的名称。
这是非法的:
1 2 3 4 5 6 7 8 9 10 11 | struct a { b * b_pointer; int c; }; typedef struct { struct a * a_pointer; void * d; } b; // struct b was never declared or defined |
但这是合法的:
1 2 3 4 5 6 7 8 9 10 11 | struct a { struct b * b_pointer; int c; }; typedef struct b { struct a * a_pointer; void * d; } b; // struct b is defined and has an alias type called b |
这就是:
1 2 3 4 5 6 7 8 9 10 11 12 | typedef struct b b; // the type b referes to a yet undefined type struct b struct a { b * struct_b_pointer; int c; }; struct b { struct a * a_pointer; void * d; }; |
这(仅在C中,在C++中是非法的):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | typedef int b; struct a { struct b * struct_b_pointer; b b_integer_type; int c; }; struct b { struct a * a_pointer; void * d; }; // struct b and b are two different types all together. Note: this is not allowed in C++ |
forward声明是一种承诺,它将定义一些您在无法定义时对编译器所做的事情。编译器可以使用您的单词来解释其他声明,否则它将无法解释这些声明。
一个常见的例子是设计为链接列表中的节点的
1 2 3 4 5 6 7 | // Forward declaration struct element; typedef struct { int value; // Use of the forward declaration struct element *next; } element; // Complete definition |
and so it cant be used for forward declaration
我认为作者的观点是,给你的
1 2 3 4 5 | typedef struct element { int value; // No need for a forward declaration here struct element *next; } element; |
前向声明是一种先于实际定义的声明,通常是为了在定义不可用时能够引用声明的类型。当然,不是所有的事情都可以用声明的未定义的结构来完成,但是在特定的上下文中,可以使用它。这种类型被称为不完整类型,对其使用有许多限制。例如:
1 2 3 4 5 6 7 8 9 10 | struct X; // forward declaration void f(struct X*) { } // usage of the declared, undefined structure // void f(struct X) { } // ILLEGAL // struct X x; // ILLEGAL // int n =sizeof(struct X); // ILLEGAL // later, or somewhere else altogether struct X { /* ... */ }; |
这可能很有用,例如打破循环依赖关系,或者减少编译时间,因为定义通常要大得多,因此解析它需要更多的资源。
在您的示例中,
1 2 | struct_alias variable1; struct NAME variable2; |
是正确的;
1 | NAME variable3; |
不是,因为在C中,需要
这两个都是一样的
1 2 3 | struct_alias variable1; struct NAME variable1; |
这是违法的
1 | NAME variable3; |
见这篇关于远期申报的文章。
如前所述,C/C++中的前向声明是对实际定义不可用的声明。它是一个声明,告诉编译器"有一个数据类型abc"。
假设这是某个键/值存储区
1 2 3 4 5 6 7 8 | ... struct my_dict_t; struct my_dict_t* create(); char* get_value(const struct my_dict_t* dict, const char* name); char* insert(struct my_dict_t* dict, const char* name, char* value); void destroy(struct my_dict_t* dict); ... |
你对
1 2 3 4 5 6 7 | #include"my_dict.h" ... struct my_dict_t* dict = create(); if(0 != insert(dict,"AnEntry", strdup("AValue"))) { ... } ... |
原因是:您只使用指向数据结构的指针。
指针只是数字,处理它们时,你不需要知道它们指向什么。
只有当你尝试真正地访问它们,比如
1 2 3 | struct my_dict_t* dict = create(); printf("%s ", dict->value); /* Impossible if only a forward decl is available */ |
因此,为了实现这些功能,需要实际定义
1 2 3 4 5 6 7 8 9 10 11 | #include"my_dict.h" struct my_dict_t { char* value; const char* name; struct my_dict_t* next; } struct my_dict_t* create() { return calloc(1, sizeof(struct my_dict_t)); } |
这在一些情况下很方便,比如
- 用于解决循环类型依赖关系,如Sergei L.解释的。
- 用于封装,如上面的示例中所示。
所以剩下的问题是:当使用上面的函数时,为什么我们不能完全忽略前向声明呢?最后,编译器可以知道所有
但是,编译器会执行类型检查:它需要确认你没有做类似的事情
1 2 3 4 | ... int i = 12; char* value = get_value(&i,"MyName"); ... |
它不需要知道