C中的不透明指针是什么?

时间:2011-09-26 10:29:46

标签: c pointers

我可以在C中了解不透明指针概念背后的用法和逻辑吗?

3 个答案:

答案 0 :(得分:91)

不透明指针是指未显示基础数据细节的指针(来自字典定义: opaque:形容词;无法透视;不透明)。

例如,您可以在头文件中声明(这来自我的一些实际代码):

typedef struct pmpi_s *pmpi;

声明了一个类型pmpi,它是指向不透明的结构 struct pmpi_s的指针,因此您声明为pmpi的任何内容都将是一个不透明的指针。 / p>

该声明的用户可以自由编写如下代码:

pmpi xyzzy = NULL;

不知道结构的实际“定义”。

然后,在知道定义的代码中(即提供pmpi处理功能的代码,您可以“定义”结构:

struct pmpi_s {
    uint16_t *data;     // a pointer to the actual data array of uint16_t.
    size_t sz;          // the allocated size of data.
    size_t used;        // number of segments of data in use.
    int sign;           // the sign of the number (-1, 0, 1).
};

并轻松访问它的各个字段,这是头文件用户无法做到的。

可以在Wikipedia page上找到更多关于不透明指针的信息..

它的主要用途是隐藏库用户的实现细节。封装(尽管C ++人群会告诉你)已经存在了很长时间: - )

您希望在您的库中发布足够的详细信息,以便用户有效地使用它,而不再发布。发布更多内容会为用户提供他们可能依赖的详细信息(例如事实大小变量sz位于结构中的特定位置,这可能导致它们绕过您的控件并直接操作它。

然后,当您更改内部时,您会发现您的客户抱怨不已。如果没有该结构信息,您的API仅限于您提供的内容,并保持您对内部的行动自由。

答案 1 :(得分:7)

不透明指针用于编程接口(API)的定义。

通常,它们是指向不完整结构类型的指针,声明如下:

typedef struct widget *widget_handle_t;

它们的目的是为客户端程序提供一种对由API管理的对象进行引用的方式,而不泄露该对象的实现(内存中的地址(指针本身)除外)。

客户端可以传递对象,将其存储在其自己的数据结构中,并比较两个这样的指针(无论它们是相同还是不同),但是它不能取消引用这些指针来窥视对象中的内容。

这样做的原因是为了防止客户端程序变得依赖于那些细节,以便可以升级实现而不必重新编译客户端程序。

由于键入了不透明指针,因此可以很好地度量类型安全。如果我们有:

typedef struct widget *widget_handle_t;
typedef struct gadget *gadget_handle_t;

int api_function(widget_handle_t, gadget_handle_t);

如果客户端程序混合了参数的顺序,则编译器将进行诊断,因为struct gadget *将被转换为struct widget *,而没有强制转换。

这就是为什么我们要定义没有成员的struct类型的原因;每个带有不同新标签的struct声明都引入了与先前声明的struct类型不兼容的新类型。

客户成为依赖人意味着什么?假设widget_t具有width和height属性。如果它不是不透明并且看起来像这样:

typedef struct widget {
  short width;
  short height;
} widget_t;

然后客户只需执行此操作即可获取宽度和高度:

int widget_area = whandle->width * whandle->height;

在不透明的范式下,它必须使用访问函数(未内联):

// in the header file
int widget_getwidth(widget_handle_t *);
int widget_getheight(widget_handle_t *);

// client code
int widget_area = widget_getwidth(whandle) * widget_getheight(whandle);

请注意widget作者如何使用short类型来节省结构中的空间,并且该空间已暴露给非透明接口的客户端。假设小部件现在可以具有不适合short的大小,并且结构必须更改:

typedef struct widget {
  int width;
  int height;
} widget_t;

现在必须重新编译客户端代码才能使用此新定义。根据工具和部署工作流程的不同,甚至可能存在未完成的风险:旧的客户端代码尝试使用新的库,并且通过使用旧的布局访问新的结构而行为不端。对于动态库,这很容易发生。库已更新,但相关程序未更新。

使用不透明接口的客户端可以继续工作而不做修改,因此不需要重新编译。它只是调用访问器函数的新定义。这些位于小部件库中,可以从结构中正确检索新的int类型的值。

请注意,从历史上(至今仍在此),使用void *类型作为不透明的句柄类型也很乏味:

typedef void *widget_handle_t;
typedef void *gadget_handle_t;

int api_function(widget_handle_t, gadget_handle_t);

在此方案下,您可以执行此操作,而无需任何诊断:

api_function("hello", stdout);

Microsoft Windows API是系统的示例,您可以在两种系统中同时使用它。默认情况下,各种句柄类型,例如HWND(窗口句柄)和HDC(设备上下文)均为void *。因此,没有类型安全性。可以将HWND传递到期望HDC的地方,这是错误的。如果您这样做:

#define STRICT
#include <windows.h>

然后将这些句柄映射到互不兼容的类型以捕获这些错误。

答案 2 :(得分:0)

不透明,顾名思义,这是我们看不到的。例如木材是不透明的。不透明指针是指向数据结构的指针,该数据结构的内容在定义之时不公开。 例如:

struct STest * pSTest;

将NULL分配给不透明的指针是安全的。

pSTest = NULL;

相关问题