C 如何使用结构声明

C 如何使用结构声明,c,struct,opaque-pointers,C,Struct,Opaque Pointers,我想隐藏struct define,所以我在源文件中定义struct,如下所示: //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 <std

我想隐藏struct define,所以我在源文件中定义struct,如下所示:

//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
#include "stddef.h"
#include "a.h"

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

   return 0;

}

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

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


为什么会失败?

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

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


这种方法允许开发人员向用户公开接口,而用户不知道数据结构的任何细节,允许更新软件和修改数据结构,同时保持向外的标题不变(并保持测试通过)。

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

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

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

但是,可以使用指向尚未定义的结构的指针。这允许一个人做如下事情:

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;
}
#包括
#包括“foo.h”
内部主(空){
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);
返回0;
}

如果需要真正不透明的类型,甚至可以在typedef中包含指针。通常情况下,这是一种不好的做法,但在这种特殊情况下,这是有一定意义的。有关此概念的详细信息,请参见。

Try#在结构a#u sIn
main()
的定义之后包含“a.h”,名称
a#t
是不透明类型。您只能定义指向不透明类型的指针。编译器不能为<代码> Ayt < /Cord>分配存储,而不知道要分配多少存储(这取决于结构定义),例如,考虑<代码>文件< /C> >类型,这是一个结构。调用
fopen
获取指向
文件的指针。然后您可以调用像
fread
fprintf
这样的函数,这些函数接受
文件*
。最后,
fclose
进行清理。所有这些都可以在调用方不知道
文件中的内容的情况下完成。是的,我想我理解了,谢谢。有点像封闭源代码软件的头文件,其定义对最终用户是可见的,但源文件位于.o、.lib文件等中……正是如此。通过隐藏结构的内部,库维护人员在想要更改结构或添加更多功能时不必担心向后兼容性。@user3386109我可以理解,如果你想隐藏结构定义,用户只能定义类型的指针,您必须实现一个api来创建struct实例(通过malloc)@sundq,没错。你还应该有一个功能,允许用户
释放结构需要更换,但
a.a=2也需要更换您还需要向API添加适当的getter和setter。