Что такое непрозрачный указатель в C?

Могу ли я узнать об использовании и логике концепции непрозрачного указателя в C?


person Renjith G    schedule 26.09.2011    source источник
comment
Да, вы можете: en.wikipedia.org/wiki/Opaque_pointer   -  person David Heffernan    schedule 26.09.2011
comment
Я ничего не понял из вики-страницы.   -  person Renjith G    schedule 26.09.2011
comment
Хм; этот вопрос о C был дублирован в один помеченный C ++. Это не идеально. Общности достаточно, чтобы это не было серьезной проблемой, но в C ++ есть параметры и функции, недоступные в C.   -  person Jonathan Leffler    schedule 09.02.2016
comment
По теме: Непрозрачные структуры C: как их объявлять?   -  person Gabriel Staples    schedule 20.11.2020


Ответы (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 ограничен только тем, что вы предоставляете, и ваша свобода действий в отношении внутренних компонентов сохраняется.

person paxdiablo    schedule 26.09.2011
comment
Ну что толку от этого? - person Renjith G; 26.09.2011
comment
Непрозрачный указатель равен xyzzy, да. Непрозрачный указатель type - pmpi, а непрозрачная структура - struct pmpi. Основное его использование связано с инкапсуляцией, способностью скрывать детали реализации от пользователей. Я дополню ответ подробностями. - person paxdiablo; 26.09.2011
comment
Большое спасибо за помощь. Благодарим вас за сотрудничество. - person Renjith G; 26.09.2011
comment
@paxdiablo Какая польза от указателя pmpi для конечного пользователя. Как конечный пользователь будет использовать этот непрозрачный указатель. Это может быть объявлено внутри модуля, который пишет функциональность, не могли бы вы процитировать какой-нибудь пример - person VINOTH ENERGETIC; 31.12.2014
comment
@paxdiablo Нет, спасибо, у меня есть пример. ФАЙЛ - один из непрозрачных пуантов. - person VINOTH ENERGETIC; 31.12.2014
comment
@VinothKumar, единственное, что пользователь должен сделать с непрозрачным указателем, - это получить его или передать в функции, которые действительно знают о его внутреннем устройстве, точно так же, как fopen или fclose для FILE *. Является ли FILE действительно непрозрачным, зависит от того, что вы найдете в stdio.h - если он полностью объявлен там, а не просто тип, подобный тому, который указан в этом ответе, пользователи могут получить доступ к внутренним компонентам, поэтому он непрозрачен только по обоюдному согласию :-) - person paxdiablo; 09.01.2015
comment
Обратите внимание, что typedef struct pmpi_s *pmpi; не идеален: кто-то может предположить, что, например, void f(const pmpi val) не имеет побочных эффектов на свой аргумент, что было бы неверно. - person Dmitry Grigoryev; 01.09.2016
comment
@DmitryGrigoryev Работает, спасибо! 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>

затем эти дескрипторы сопоставляются с взаимно несовместимыми типами, чтобы перехватить эти ошибки.

person Kaz    schedule 17.01.2020

Непрозрачность, как следует из названия, мы не видим. Например. древесина непрозрачна. Непрозрачный указатель - это указатель, который указывает на структуру данных, содержимое которой не отображается во время ее определения.

Пример:

struct STest* pSTest;

Непрозрачному указателю безопасно присвоить NULL.

pSTest = NULL; 
person Rahul    schedule 27.04.2019