【问题标题】:OOP and forward declaration of structure in CC中的OOP和结构的前向声明
【发布时间】:2021-05-20 13:43:10
【问题描述】:

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

我的教科书使用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 进行定义。 我想知道为什么我没有收到编译错误并且它起作用了。

【问题讨论】:

  • 如果您在 class.c 中#include&lt;class.h&gt;,您将得到所需的编译器错误,因为编译器可以比较函数签名并告诉您它们冲突。
  • 您拥有的代码应该会生成一个编译器警告,指出您将错误的类型传递给test_print,因此您可能需要打开编译器诊断。
  • 这被称为未定义的行为......其中包括“似乎可以工作”的情况。
  • @0___________ 这是在 C 中实现私有封装的一种众所周知的方式。私有封装如何不是 OOP? OO 是一种设计程序的方式,而不是一种编写程序的方式......
  • @0___________ 我不相信模拟程序设计是可能的。无论语言如何,您要么有程序设计,要么没有。

标签: c oop struct compilation


【解决方案1】:

我想知道为什么我没有收到编译错误

当你编译 main.c 时,编译器通过来自 class.h 的前向声明被告知有一个带有签名 struct test_a * test_init(void); 的函数

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

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

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

..它成功了

会发生什么:

  1. 一个指向 test_b 的指针被赋值给一个指向 test_a 变量的指针

  2. 然后将变量作为参数传递给期望指向 test_b 的指针的函数

因此,一旦您使用了指针,它就按照创建时的方式使用(即作为指向 test_b 的指针)。在两者之间,您只是存储在另一个指针类型的变量中。

这样好吗? 没有

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

【讨论】:

    【解决方案2】:

    它“有效”是因为您没有在 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)
    {
        ...
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-12
      • 2012-06-09
      • 2013-04-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多