【问题标题】:How to use struct declare如何使用结构声明
【发布时间】:2019-12-04 02:03:44
【问题描述】:

我想隐藏结构体定义,所以我在源文件中定义结构体,像这样:

//a.c
#include "a.h"

struct a_s
{
   int a;
   int b;
};

int func(a_t *a)
{
   printf("%d\n", a->a);
   return 0;
}


我在头文件中声明结构,如下所示:

//a.h
#ifndef TEST
#define TEST
#include <stdio.h>
#include <stddef.h>

typedef struct a_s a_t;
#endif

然后我使用struct a_t int main.c 文件,像这样:

#include "stddef.h"
#include "a.h"

int main()
{
   a_t a;
   a.a =2;
   func(&a);

   return 0;

}

但是当我通过 gcc -c main.c 编译 main.c 时,它失败了

main.c: In function ‘main’:
main.c:7:15: error: storage size of ‘a’ isn’t known
    struct a_s a;

为什么会失败?

【问题讨论】:

  • 在struct a_s定义后尝试#include "a.h"
  • main() 中,名称a_t 是不透明类型。您只能定义指向不透明类型的指针。
  • 编译器无法在不知道要分配多少存储空间的情况下为a_t分配存储空间(取决于结构定义)

标签: c struct opaque-pointers


【解决方案1】:

如果你想隐藏结构体定义,用户只能定义类型的指针,你必须实现一个创建结构体实例的api(通过malloc)和一个释放结构体实例的api(通过free)

【讨论】:

  • 不仅a_t a;需要被替换,a.a =2;也需要被替换你还需要在API中添加合适的getter和setter。
【解决方案2】:

如果您实例化一个对象 A a,链接器会搜索 A 的定义,以便编译器知道它需要分配多少内存。它搜索 a.h 并找到一个 typedef 但没有声明,因此错误是说它不知道 A 的大小。

如果程序的目的是对用户隐藏声明(和定义),您将需要使用 A *a,因为这对编译器说“有一个类型 A,它的内存将是存储在这个内存位置开始”,因此在运行时不需要任何关于数据大小或布局的信息,在运行时应该动态分配和释放内存。

这种方法允许开发人员向用户公开界面,而用户无需了解有关数据结构的任何细节,并允许更新软件和修改数据结构,同时保持向外的标题相同(并保持测试通过)。

【讨论】:

  • 例如,考虑FILE 类型,它是一个结构。您调用 fopen 来获取指向 FILE 结构的指针。然后您可以调用像freadfprintf 这样的函数,它们采用FILE *。最后,fclose 进行清理。所有这些都可以在调用者不知道 FILE 结构中的内容的情况下完成。
  • 是的,我想我明白了,谢谢。有点像封闭源代码软件的标题,其定义对最终用户可见,但源代码位于 .o、.lib 文件等中......
  • 没错。通过隐藏结构的内部结构,库维护者在想要更改结构或添加更多功能时不必担心向后兼容性。
  • @user3386109 可能我明白了,如果你想隐藏结构定义,用户只能定义类型的指针,你必须实现一个创建结构实例的api(通过malloc)
  • @sundq 没错。您还应该有一个功能,允许用户free 结构。
【解决方案3】:

您不能创建尚未定义的结构的实例,因为编译器不知道要为其分配多少空间。

您无法访问尚未定义的结构成员,因为编译器不知道它们的类型。

但是,您可以使用指向尚未定义的结构的指针。这允许人们执行以下操作:

foo.h:

typedef struct Foo Foo

Foo* Foo_new(int a, int b);
void Foo_destroy(Foo* this);
void Foo_set_a(Foo* this, int a);
void Foo_set_b(Foo* this, int b);
int Foo_get_a(Foo* this);
int Foo_get_b(Foo* this);
// ...

foo.c:

#include "a.h"

struct Foo {
   int a;
   int b;
};

Foo* Foo_new(int a, int b) {
   Foo* this = malloc(sizeof(Foo));
   this->a = a;
   this->b = b;
   return this;
}

void Foo_destroy(Foo* this) { free(this); }
void Foo_set_a(Foo* this, int a) { this->a = a; }
void Foo_set_b(Foo* this, int b) { this->b = b; }
int Foo_get_a(Foo* this) { return this->a; }
int Foo_get_b(Foo* this) { return this->b; }
// ...

main.c

#include <stdio.h>
#include "foo.h"

int main(void) {
   Foo* foo = Foo_new(3, 4);
   Foo_set_a(foo, 5);
   printf("%d %d\n",
      Foo_get_a(foo),
      Foo_get_b(foo),
   );
   Foo_destroy(foo);
   return 0;
}

如果你想要一个真正不透明的类型,你甚至可以在 typedef 中包含指针。通常,这将是一个不好的做法,但在这种特殊情况下它具有一定的意义。有关此概念的更多信息,请参阅this

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多