我是C编程的初学者,我知道struct
类型声明和typedef
结构声明之间的区别。我偶然知道一个答案,说如果我们定义一个struct
,比如:
typedef struct {
some members;
} struct_name;
然后就像为匿名结构提供别名一样(因为它没有标签名称)。因此,它不能用于前向声明。我不知道前瞻声明是什么意思。
另外,我想知道以下代码:
typedef struct NAME {
some members;
} struct_alias;
NAME
和struct_alias
有什么区别吗?或者都相等为 struct_alias
是结构名称的别名?
此外,我们是否可以声明一个 struct NAME
类型的变量,如下所示:
struct_alias variable1;
和/或类似:
struct NAME variable2;
或类似:
NAME variable3;
>当您需要循环结构声明时,struct
前向声明可能很有用。例:
struct a {
struct b * b_pointer;
int c;
};
struct b {
struct a * a_pointer;
void * d;
};
当声明struct a
时,它还不知道struct b
的规格,但你可以转发引用它。
当您键入匿名结构时,编译器将不允许您在 typedef 之前使用它的名称。
这是非法的:
struct a {
b * b_pointer;
int c;
};
typedef struct {
struct a * a_pointer;
void * d;
} b;
// struct b was never declared or defined
虽然这是合法的:
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
这也是这样的:
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++ 中是非法的):
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++
前向声明是一种承诺,用于定义您在无法定义时向编译器所做的内容。编译器可以使用您的单词来解释其他声明,否则它将无法解释。
一个常见的例子是设计为链表中节点的struct
:您需要将指向节点的指针放入struct
中,但编译器不会让您在没有前向声明或标签的情况下执行此操作:
// Forward declaration
struct element;
typedef struct {
int value;
// Use of the forward declaration
struct element *next;
} element; // Complete definition
所以它不能用于前向声明
我认为作者的观点是,给你的struct
一个标签等同于一个前向声明:
typedef struct element {
int value;
// No need for a forward declaration here
struct element *next;
} element;
前向声明是实际定义之前的声明,通常是为了能够在定义不可用时引用声明的类型。当然,并非所有事情都可以使用声明的未定义结构来完成,但在某些情况下可以使用它。这种类型称为不完整,并且对其使用有许多限制。例如:
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 { /* ... */ };
这可能很有用,例如打破循环依赖关系,或减少编译时间,因为定义通常要大得多,因此需要更多的资源来解析它。
在您的示例中,struct NAME
和 struct_alias
确实是等效的。
struct_alias variable1;
struct NAME variable2;
是正确的;
NAME variable3;
不是,因为在 C 中需要 struct
关键字。
>struct_alias
和struct NAME
是相同的,struct_alias
是struct NAME
的别名
这两者都是相同且允许的
struct_alias variable1;
struct NAME variable1;
这是非法的
NAME variable3;
请参阅这篇关于远期声明的文章
正如其他人之前所说,C/C++ 中的前向声明是对实际定义不可用的事物的声明。它是一个声明,告诉编译器"有一个数据类型ABC"。
让我们假设这是某个键/值存储my_dict.h
的标头:
...
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);
...
你对my_dict_t
一无所知,但实际上,使用商店你不需要知道:
#include "my_dict.h"
...
struct my_dict_t* dict = create();
if(0 != insert(dict, "AnEntry", strdup("AValue"))) {
...
}
...
这样做的原因是:您只对数据结构使用指针。
指针只是数字,为了处理它们,你不需要知道它们指向什么。
这只有在您尝试实际访问它们时才重要,例如
struct my_dict_t* dict = create();
printf("%sn", dict->value); /* Impossible if only a forward decl is available */
因此,要实现这些功能,您需要一个 my_struct_t
的实际定义。您可以在源文件中执行此操作my_dict.c
如下所示:
#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.解释的那样。
- 对于封装,如上例所示。
所以剩下的问题是:为什么我们在使用上述函数时不能完全省略前向声明?最后,编译器知道所有dict
都是指针就足够了。
但是,编译器会执行类型检查:它需要验证你没有做类似的事情
...
int i = 12;
char* value = get_value(&i, "MyName");
...
它不需要知道my_dict_t
的外观,但它需要知道&i
不是get_value()
期望的指针类型。