首页 文章

什么是'forward declaration'以及'typedef struct X'和'struct X'之间的区别?

提问于
浏览
37

我是C编程的初学者,我知道struct type declaration和typedef struct declaration之间的区别 . 我遇到了一个答案,说如果我们定义一个结构,如:

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 回答

  • 43

    当需要循环结构声明时, struct 前向声明可能很有用 . 例:

    struct a {
        struct b * b_pointer;
        int c;
    };
    
    struct b {
        struct a * a_pointer;
        void * d;
    };
    

    当声明 struct a 时,它还不知道 struct b 的规格,但您可以转发它 .

    当你输入一个匿名结构时,编译器将不允许你在typedef之前使用它的名字 .

    This is illegal:

    struct a {
        b * b_pointer;
        int c;
    };
    
    typedef struct {
        struct a * a_pointer;
        void * d;
    } b;
    
    // struct b was never declared or defined
    

    This though is legal:

    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
    

    So is this:

    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;
    };
    

    And this:

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

    前向声明是一种承诺,用于定义在无法定义的位置对编译器进行的操作 . 编译器可以使用您的单词来解释否则无法解释的其他声明 .

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

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

    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 关键字 .

  • 20

    struct_aliasstruct NAME 相同, struct_aliasstruct NAME 的别名

    这些都是相同的并且允许

    struct_alias variable1;  
    
    struct NAME variable1;
    

    这是非法的

    NAME variable3;
    

    Forward declaration上查看此文章

  • 0

    正如其他人之前所说的那样,C / C中的前向声明是对实际定义不可用的声明 . 它是一个声明,告诉编译器“有一个数据类型ABC” .

    让我们假装这是一个键/值存储 my_dict.h 的 Headers :

    ...
    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() 期望的指针类型 .

相关问题