Могу ли я узнать об использовании и логике концепции непрозрачного указателя в C?
Что такое непрозрачный указатель в C?
Ответы (3)
Непрозрачный указатель - это указатель, в котором не раскрываются подробности базовых данных (из определения словаря: opaque: прилагательное; не может быть видно насквозь; непрозрачный).
Например, вы можете объявить в файле заголовка (это часть моего фактического кода):
typedef struct pmpi_s *pmpi;
который объявляет тип pmpi
, который является указателем на непрозрачную структуру struct pmpi_s
, поэтому все, что вы объявляете как pmpi
, будет непрозрачным указателем.
Пользователи этого объявления могут свободно писать такой код, как:
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).
};
и легко получить доступ к его отдельным полям, чего не могут сделать пользователи файла заголовка.
Дополнительную информацию о непрозрачных указателях можно найти на странице Википедии.
Основное его использование - скрыть детали реализации от пользователей вашей библиотеки. Инкапсуляция (несмотря на то, что вам скажет толпа C ++) существует уже давно :-)
Вы хотите опубликовать достаточно сведений о своей библиотеке, чтобы пользователи могли эффективно ее использовать, и не более того. Публикация дополнительных сведений дает пользователям сведения, на которые они могут полагаться (например, тот факт, что переменная размера sz
находится в определенном месте в структуре, что может привести к тому, что они будут обходить ваши элементы управления и напрямую манипулировать им.
Тогда вы обнаружите, что ваши клиенты горько жалуются, когда вы меняете внутренности. Без этой структурной информации ваш API ограничен только тем, что вы предоставляете, и ваша свобода действий в отношении внутренних компонентов сохраняется.
xyzzy
, да. Непрозрачный указатель type - pmpi
, а непрозрачная структура - struct pmpi
. Основное его использование связано с инкапсуляцией, способностью скрывать детали реализации от пользователей. Я дополню ответ подробностями.
- person paxdiablo; 26.09.2011
fopen
или fclose
для FILE *
. Является ли FILE
действительно непрозрачным, зависит от того, что вы найдете в stdio.h
- если он полностью объявлен там, а не просто тип, подобный тому, который указан в этом ответе, пользователи могут получить доступ к внутренним компонентам, поэтому он непрозрачен только по обоюдному согласию :-)
- person paxdiablo; 09.01.2015
typedef struct pmpi_s *pmpi;
не идеален: кто-то может предположить, что, например, void f(const pmpi val)
не имеет побочных эффектов на свой аргумент, что было бы неверно.
- person Dmitry Grigoryev; 01.09.2016
your->Genius_Level++
- person DepressedDaniel; 08.03.2017
Непрозрачные указатели используются в определениях интерфейсов программирования (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
имеет свойства ширины и высоты. Если он непрозрачный и выглядит так:
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>
затем эти дескрипторы сопоставляются с взаимно несовместимыми типами, чтобы перехватить эти ошибки.
Непрозрачность, как следует из названия, мы не видим. Например. древесина непрозрачна. Непрозрачный указатель - это указатель, который указывает на структуру данных, содержимое которой не отображается во время ее определения.
Пример:
struct STest* pSTest;
Непрозрачному указателю безопасно присвоить NULL
.
pSTest = NULL;