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

Ren*_*h G 58 c pointers

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

pax*_*blo 100

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

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

typedef struct pmpi_s *pmpi;
Run Code Online (Sandbox Code Playgroud)

它声明了pmpi一个指向不透明结构 的指针的类型struct pmpi_s,因此你声明的任何东西pmpi都是不透明的指针.

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

pmpi xyzzy = NULL;
Run Code Online (Sandbox Code Playgroud)

不知道结构的实际"定义".

然后,在知道定义的代码中(即提供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).
};
Run Code Online (Sandbox Code Playgroud)

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

有关不透明指针的更多信息,请访问维基百科页面.

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

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

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

  • 不透明的_pointer_是`xyzzy`,是的.不透明指针_type_是`pmpi`而不透明_structure_是`struct pmpi`.它的主要用途是封装,即隐藏用户实现细节的能力.我会用详细信息更新答案. (5认同)
  • 嗯,这个有什么用? (2认同)
  • @VinothKumar,用户应该对不透明指针做的唯一一件事就是从 _do_ 知道其内部结构的函数中获取它或将其传递给函数,就像 `FILE *` 的 `fopen` 或 `fclose` 一样。`FILE` 是否真正不透明取决于您在 `stdio.h` 中找到的内容-如果它在那里完全声明而不仅仅是类似于本答案中的类型,则用户可以了解内部结构,因此只有在双方同意的情况下它才是不透明的:-) (2认同)
  • 注意`typedef struct pmpi_s*pmpi;`是不理想的:有人可能会认为例如`void f(const pmpi val)`对它的参数没有副作用,这是不正确的. (2认同)

Kaz*_*Kaz 12

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

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

typedef struct widget *widget_handle_t;
Run Code Online (Sandbox Code Playgroud)

它们的目的是为客户端程序提供一种方法来保存对由 API 管理的对象的引用,除了它在内存中的地址(指针本身)之外,不透露有关该对象实现的任何信息。

客户端可以传递对象,将其存储在自己的数据结构中,并比较两个这样的指针是否相同,但它不能取消对指针的引用以查看对象中的内容。

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

因为不透明指针是类型化的,所以有一个很好的类型安全措施。如果我们有:

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

int api_function(widget_handle_t, gadget_handle_t);
Run Code Online (Sandbox Code Playgroud)

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

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

客户变得依赖意味着什么?假设 awidget_t具有宽度和高度属性。如果它不是不透明的并且看起来像这样:

typedef struct widget {
  short width;
  short height;
} widget_t;
Run Code Online (Sandbox Code Playgroud)

然后客户端可以这样做来获取宽度和高度:

int widget_area = whandle->width * whandle->height;
Run Code Online (Sandbox Code Playgroud)

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

// 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);
Run Code Online (Sandbox Code Playgroud)

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

typedef struct widget {
  int width;
  int height;
} widget_t;
Run Code Online (Sandbox Code Playgroud)

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

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

请注意,从历史上看(现在仍然在这里和那里),使用该void *类型作为不透明句柄类型的做法也很乏味:

typedef void *widget_handle_t;
typedef void *gadget_handle_t;

int api_function(widget_handle_t, gadget_handle_t);
Run Code Online (Sandbox Code Playgroud)

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

api_function("hello", stdout);
Run Code Online (Sandbox Code Playgroud)

Microsoft Windows API 是一个系统示例,您可以在其中使用两种方式。默认情况下,各种句柄类型,如HWND(窗口句柄)和HDC(设备上下文)都是void *. 所以没有类型安全;aHWND可能会HDC错误地通过 a预期的地方。如果你这样做:

#define STRICT
#include <windows.h>
Run Code Online (Sandbox Code Playgroud)

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