如何在C中隐藏结构的声明?

在问题中我们为什么要在C中经常输入一个结构? ,放松回答:

在后一种情况下,您不能返回Point by值,因为它的声明对头文件的用户是隐藏的。 例如,这是一种在GTK +中广泛使用的技术。

声明隐藏是如何完成的? 为什么我不能按值返回Point?

加:

我理解为什么我不能通过值返回结构,但是,仍然很难理解为什么我不能在我的函数中尊重这一点。 即如果我的结构有名为y的成员,为什么我不能这样做?

pointer_to_struct->y = some_value; 

我为什么要使用方法呢? (像Gtk +)

谢谢你们,再次为我糟糕的英语道歉。

使用公共头文件,私有头文件和实现文件来查看这个库的示例。

在文件public.h中

 struct Point; Point* getSomePoint(); 

在文件private.h中

 struct Point { int x; int y; } 

在文件private.c中

 Point* getSomePoint() { /* ... */ } 

如果将这三个文件合并到一个库中,则只将public.h和库对象文件提供给库的使用者。

getSomePoint必须返回指向Point的指针,因为public.h没有定义Point的大小,只有它是一个结构并且它存在。 库的消费者可以使用Point指针,但不能访问成员或复制它们,因为他们不知道结构的大小。

关于你的进一步问题:你不能解除引用,因为使用库的程序只有来自private.h的信息,它不包含成员声明。 因此它无法访问点结构的成员。

您可以将此视为C的封装function,就像您将C ++类的数据成员声明为私有一样。

他的意思是你不能在头文件中返回struct by-value,因为为此,必须完全声明struct。 但这种情况发生在C文件中(在他的示例中,使X成为完整类型的声明在C文件中被“隐藏”,并且没有暴露到标题中)。 如果这是结构的第一个声明,则下面仅声明一个不完整的类型

 struct X; 

然后,您可以声明该function

 struct X f(void); 

但是你不能定义这个函数,因为你不能创建那种类型的变量,更不用说返回它(它的大小是未知的)。

 struct X f(void) { // <- error here // ... } 

发生错误是因为“x”仍然不完整。 现在,如果只包含带有不完整声明的标题,那么就不能调用该函数,因为函数调用的表达式会产生一个不完整的类型,这是禁止发生的。

如果您要在两者之间提供完整类型struct X的声明,那么它将是有效的

 struct X; struct X f(void); // ... struct X { int data; }; struct X f(void) { // valid now: struct X is a complete type // ... } 

这也适用于使用typedef的方式:它们都命名相同,(可能不完整)类型。 一次使用普通标识符X ,另一次使用标签struct X

在头文件中:

 typedef struct _point * Point; 

在编译器看到它后,它知道:

  • 有一个名为_point的结构
  • 有一个指针类型Point可以引用_point

编译器不知道:

  • _point结构的样子
  • 它包含哪些成员
  • 它有多大

并且编译器不仅不知道它 – 我们程序员也不知道它。 这意味着我们无法编写依赖于_point属性的代码,这意味着我们的代码可能更具可移植性。

鉴于上面的代码,您可以编写如下函数:

 Point f() { .... } 

因为Point是一个指针,指针的大小都相同,编译器不需要知道任何其他内容。 但是你不能编写一个按值返回的函数:

 _point f() { .... } 

因为编译器对_point没有任何了解,特别是它的大小,它是构造返回值所需要的。

因此,我们只能通过Point类型引用_point,它实际上是一个指针。 这就是为什么标准C具有类似FILE的类型,它只能通过指针访问 – 您无法在代码中创建FILE结构实例。

该post的含义是:如果您看到标题

 typedef struct _Point Point; Point * point_new(int x, int y); 

那么你不知道Point的实现细节。

看看这个: 不透明的指针

老问题,更好的答案:

在头文件中:

 typedef struct _Point Point; 

在C文件中:

 struct _Point { int X; int Y; }; 

作为使用不透明指针的替代方法(正如其他人提到的那样),如果要避免使用堆内存,则可以返回一个不透明的字节包:

 // In public.h: struct Point { uint8_t data[SIZEOF_POINT]; // make sure this size is correct! }; void MakePoint(struct Point *p); // In private.h: struct Point { int x, y, z; }; void MakePoint(struct Point *p); // In private.c: void MakePoint(struct Point *p) { p->x = 1; p->y = 2; p->z = 3; } 

然后,您可以在客户端代码中在堆栈上创建结构的实例,但客户端不知道其中包含的内容 – 它只知道它是一个具有给定大小的字节blob。 当然,如果它可以猜测成员的偏移量和数据类型,它仍然可以访问数据,但是对于不透明指针你也会遇到同样的问题(尽管在这种情况下客户端不知道对象大小)。

例如, pthreads库中使用的各种结构使用不透明字节的结构,例如pthread_tpthread_cond_t等类型 – 你仍然可以在堆栈上创建那些实例(你通常会这样做),但你不知道是什么在他们中。 只需看一下你的/usr/include/pthreads.h及其包含的各种文件。