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

时间:2009-07-20 17:02:08

标签: c

在问题Why should we typedef a struct so often in C?中,展开回答:

  

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

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

ADD:

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

 pointer_to_struct->y = some_value; 

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

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

7 个答案:

答案 0 :(得分:30)

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

在档案 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的封装功能,就像您将C ++类的数据成员声明为私有一样。

答案 1 :(得分:5)

他的意思是你不能在头文件中返回struct by-value,因为为此,必须完全声明struct。但这种情况发生在C文件中(在他的示例中,使X完整类型的声明在C文件中“隐藏”,而不是暴露在标题中)。如果这是struct

的第一个声明,则以下仅声明一个不完整的类型
struct X;

然后,您可以声明函数

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

答案 2 :(得分:5)

在头文件中:

typedef struct _point * Point;

编译器看到它之后知道:

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

编译器不知道:

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

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

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

Point f() {
   ....
}

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

_point f() {
  ....
}

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

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

答案 3 :(得分:3)

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

typedef struct _Point Point;

Point * point_new(int x, int y);

然后您不知道Point的实施细节。

答案 4 :(得分:3)

看看这个:opaque pointer

答案 5 :(得分:2)

老问题,更好的回答:

在标题文件中:

typedef struct _Point Point;

在C档案中:

struct _Point
{
   int X;
   int Y;
};

答案 6 :(得分:1)

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

// 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。当然,如果可以猜测成员的偏移量和数据类型,它仍然可以访问数据,但是对于不透明指针你也会遇到同样的问题(尽管在这种情况下客户端不知道对象大小)。 p>

例如,pthreads库中使用的各种结构使用pthread_tpthread_cond_t等类型的不透明字节结构 - 您仍然可以创建这些结构的实例堆栈(你经常这样做),但你不知道它们里面有什么。只需看一看您的/usr/include/pthreads.h及其包含的各种文件。