什么是'前向声明'以及'typedef struct X'和'struct X'之间的区别?

时间:2013-09-06 12:58:42

标签: c struct typedef

我是C编程的初学者,我知道struct类型声明和typedef结构声明之间的区别。我遇到了一个答案,说如果我们定义一个struct喜欢:

typedef struct { 
    some members;
} struct_name;

然后它就像为匿名结构提供别名(因为它没有标记名称)。所以它不能用于前向声明。我不知道前向声明的含义。

另外,我想知道以下代码:

typedef struct NAME { 
    some members;
} struct_alias;

NAMEstruct_alias之间有什么区别吗?或者两者都是平等的 struct_alias是struct NAME的别名?

此外,我们可以声明类似struct NAME类型的变量:

struct_alias variable1;

和/或喜欢:

struct NAME variable2;

或者喜欢:

NAME variable3; 

5 个答案:

答案 0 :(得分:47)

当需要循环结构声明时,

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

答案 1 :(得分:22)

转发声明是一个承诺,用于定义在无法定义的位置对编译器执行的操作。编译器可以使用您的单词来解释否则无法解释的其他声明。

一个常见的例子是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;

答案 2 :(得分:13)

前向声明是实际定义之前的声明,通常用于在定义不可用时引用声明的类型。当然,并非所有事情都可以使用声明未定义的结构来完成,但在某些情况下可以使用它。这种类型称为不完整,并且对其使用有许多限制。例如:

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 NAMEstruct_alias确实相同。

struct_alias variable1;
struct NAME variable2;

是正确的;

NAME variable3;

不是,因为在C中需要struct关键字。

答案 3 :(得分:8)

struct_aliasstruct NAME相同,struct_aliasstruct NAME的别名

这两者都相同且允许

struct_alias variable1;  

struct NAME variable1; 

这是非法的

NAME variable3;   

请参阅Forward declaration

上的这篇文章

答案 4 :(得分:1)

正如其他人之前所说,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"))) {
    ...
}
...

原因是:您只是将POINTERS用于数据结构。

POINTERS只是数字,为了处理它们,你不需要知道它们指向的是什么。

只有当您尝试实际访问它们时才会这样,例如

struct my_dict_t* dict = create();
printf("%s\n", 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()所期望的类型。