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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7553750/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 09:43:02  来源:igfitidea点击:

What is an opaque pointer in C?

cpointersopaque-pointers

提问by Renjith G

May I know the usage and logic behind the opaque pointer concept in C?

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

回答by paxdiablo

An opaque pointer is one in which no details are revealed of the underlying data (from a dictionary definition: opaque: adjective; not able to be seen through; not transparent).

不透明指针是一种不显示底层数据细节的指针(来自字典定义:不透明:形容词;无法看透;不透明)。

For example, you may declare in a header file (this is from some of my actual code):

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

typedef struct pmpi_s *pmpi;

which declares a type pmpiwhich is a pointer to the opaque structurestruct pmpi_s, hence anything you declare as pmpiwill be an opaque pointer.

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

Users of that declaration can freely write code like:

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

pmpi xyzzy = NULL;

without knowing the actual "definition" of the structure.

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

Then, in the code that knows about the definition (ie, the code providing the functionality for pmpihandling, you can "define" the structure:

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

and easily access the individual fields of it, something that users of the header file cannot do.

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

More information can be found on the Wikipedia pagefor opaque pointers..

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

The main use of it is to hide implementation details from users of your library. Encapsulation (despite what the C++ crowd will tell you) has been around for a long time :-)

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

You want to publish just enough details on your library for users to effectively make use of it, and no more.Publishing more gives users details that they may come to rely upon (such as the fact the size variable szis at a specific location in the structure, which may lead them to bypass your controls and manipulate it directly.

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

Then you'll find your customers complaining bitterly when you change the internals. Without that structure information, your API is limited only to what you provide and your freedom of action regarding the internals is maintained.

然后,当您更改内部结构时,您会发现您的客户苦苦抱怨。如果没有这些结构信息,您的 API 将仅限于您提供的内容,并且您对内部结构的行动自由将得到维护。

回答by Rahul

Opaque as the name suggests is something we can't see through. E.g. wood is opaque. Opaque pointer is a pointer which points to a data structure whose contents are not exposed at the time of its definition.

顾名思义,不透明是我们看不透的东西。例如,木材是不透明的。不透明指针是指向数据结构的指针,其内容在定义时未公开。

Example:

例子:

struct STest* pSTest;

It is safe to assign NULLto an opaque pointer.

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

pSTest = NULL; 

回答by Kaz

Opaque pointers are used in the definitions of programming interfaces (API's).

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

Typically they are pointers to incomplete structure types, declared like:

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

typedef struct widget *widget_handle_t;

Their purpose is to provide the client program a way to hold a reference to an object managed by the API, without revealing anything about the implementation of that object, other than its address in memory (the pointer itself).

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

The client can pass the object around, store it in its own data structures, and compare two such pointers whether they are the same or different, but it cannot dereference the pointers to peek at what is in the object.

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

The reason this is done is to prevent the client program from becoming dependent on those details, so that the implementation can be upgraded without having to recompile client programs.

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

Because the opaque pointers are typed, there is a good measure of type safety. If we have:

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

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

int api_function(widget_handle_t, gadget_handle_t);

if the client program mixes up the order of the arguments, there will be a diagnostic from the compiler, because a struct gadget *is being converted to a struct widget *without a cast.

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

That is the reason why we are defining structtypes that have no members; each structdeclaration with a different new tag introduces a new type that is not compatible with previously declared structtypes.

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

What does it mean for a client to become dependent? Suppose that a widget_thas width and height properties. If it isn't opaque and looks like this:

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

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

then the client can just do this to get the width and height:

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

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

whereas under the opaque paradigm, it would have to use access functions (which are not inlined):

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

// 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);

Notice how the widgetauthors used the shorttype to save space in the structure, and that has been exposed to the client of the non-opaque interface. Suppose that widgets can now have sizes that don't fit into shortand the structure has to change:

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

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

Client code must be re-compiled now to pick up this new definition. Depending on the tooling and deployment workflow, there may even be a risk that this isn't done: old client code tries to use the new library and misbehaves by accessing the new structure using the old layout. That can easily happen with dynamic libraries. The library is updated, but the dependent programs are not.

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

The client which uses the opaque interface continues to work unmodified and so doesn't require recompiling. It just calls the new definition of the accessor functions. Those are in the widget library and correctly retrieve the new inttyped values from the structure.

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

Note that, historically (and still currently here and there) there has also been a lackluster practice of using the void *type as an opaque handle type:

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

typedef void *widget_handle_t;
typedef void *gadget_handle_t;

int api_function(widget_handle_t, gadget_handle_t);

Under this scheme, you can do this, without any diagnostic:

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

api_function("hello", stdout);

The Microsoft Windows API is an example of a system in which you can have it both ways. By default, various handle types like HWND(window handle) and HDC(device context) are all void *. So there is no type safety; a HWNDcould be passed where a HDCis expected, by mistake. If you do this:

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

#define STRICT
#include <windows.h>

then these handles are mapped to mutually incompatible types to catch those errors.

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