繁体   English   中英

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

[英]How can I hide the declaration of a struct in C?

在问题中为什么我们应该在 C 中如此频繁地 typedef 结构? , unwind 回答说:

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

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

添加:

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

 pointer_to_struct->y = some_value; 

我为什么要使用方法来做到这一点? (像 Gtk+)

谢谢大家,再次为我的英语不好而感到抱歉。

看看这个库的例子,它使用了一个公共头文件、一个私有头文件和一个实现文件。

在文件public.h 中

struct Point;

struct Point* getSomePoint();

在文件private.h 中

struct Point
{
    int x;
    int y;
}

在文件private.c 中

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

如果你将这三个文件编译成一个库,你只需要将public.h和库目标文件提供给库的使用者。

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

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

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

他的意思是你不能在头中按值返回结构体,因为为此,结构体必须完全声明。 但这发生在 C 文件中(使X成为完整类型的声明“隐藏”在 C 文件中,而不是暴露在头文件中),在他的例子中。 以下仅声明一个不完整的类型,如果这是结构的第一个声明

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

在头文件中:

typedef struct _point * Point;

编译器看到后它知道:

  • 有一个名为_pointstruct
  • 有一个指针类型Point可以引用一个struct _point

编译器不知道:

  • struct _point是什么样的。
  • struct _point包含哪些成员。
  • struct _point有多大。

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

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

Point f() {
   ....
}

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

struct _point f() {
  ....
}

因为编译器对struct _point ,特别是它的大小,它需要它来构造返回值。

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

该帖子的意思是:如果您看到标题

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;
}

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM