OOP 和 C 中结构的前向声明

OOP and forward declaration of structure in C

我正在学习 C 语言,最近学习了如何使用 C 编写 OOP。除了用于创建新的结构类型的名称之外,大部分内容对我来说并不难理解 class .

我的教科书使用 struct dummy_t 进行前向声明,并使用 typedef struct {...} dummy_t 进行定义。在我的理解中,这是两种不同的类型,因为前者是 struct dummy 类型,而后者是 struct 类型,没有名称标签,但教科书中的示例代码运行良好。

所以我特意修改了示例代码,让结构名称的区别更加清晰。下面是我试过的代码行。

//class.h

struct test_a;
struct test_a * test_init(void);
void test_print(struct test_a*);
//class.c
#include <stdio.h>
#include <stdlib.h>

typedef struct dummy{
    int x;
    int y;
} test_b;

test_b * test_init(void){
    test_b * temp = (test_b *) malloc(sizeof(test_b));
    temp->x = 10;
    temp->y = 11;
    return temp;
}

void test_print(test_b* obj){
    printf("x: %d, y: %d\n", obj->x, obj->y);
}
//main.c
#include "class.h"

int main(void){
    struct test_a * obj;
    obj = test_init();
    test_print(obj);

    return 0;
}
// It printed "x: 10, y: 10"

如您所见,我使用 struct test_a 进行前向声明,使用 typedef struct dummy {...} test_b 进行定义。 我想知道为什么我没有收到编译错误并且它起作用了。

它 'worked' 因为你没有在 class.c 中包含 class.h。所以编译器看不到实现与声明不匹配。

正确的方法是(但为了清楚起见没有 typedef):

// class.h
struct test_a;
struct test_a* test_init(void);

//class.c
#include "class.h"
struct test_a {
    int x;
    int y;
};

struct test_a* test_init(void)
{
   ...
}

头文件中的 struct test_a 使名称 test_a 被编译器识别为一个结构。但是因为它现在没有结构中的内容,所以您只能使用指向这种结构的指针。

成员在实现文件中定义,只能在那里使用。

如果你想使用 typedef:

// header
typedef struct test_a_struct test_a;
test_a* test_init(void);

//implementation
struct test_a_struct {
    int x;
    int y;
};
test_a* test_init(void)
{
    ...
}

I am wondering why I did not get the compile error

当您编译 main.c 时,编译器通过 class.h 的前向声明告诉编译器存在一个带有签名 struct test_a * test_init(void);

的函数

编译器只能相信它,即没有错误,也不会发出警告。

编译时 class.c 没有前向声明,只有函数定义,即没有错误,没有警告。

将 .h 文件包含到相应的 .c 文件中总是一个好主意。如果您在 class.c 中有一个 #include "class.h",编译器将能够检测到不匹配。

..and it worked

发生的事情是:

  1. 指向test_b的指针被分配给指向test_a变量的指针

  2. 然后将变量作为参数传递给需要指向 test_b

    的函数的函数

因此,一旦您使用了指针,它就会像创建时一样使用(即作为指向 test_b 的指针)。在你之间只是存储在另一个指针类型的变量中。

可以吗? 没有

在为另一种指针类型定义的对象中存储指向一种类型的指针是不正确的。这是未定义的行为。在这种情况下,它“恰好起作用”。在现实生活中,它会在大多数系统上“恰好可以工作”,因为大多数系统对所有类型都使用相同的指针布局。但根据 C 标准,这是未定义的行为。