Ce este un pointer opac în C? (Programare, C, Pointeri, Pointeri Opaci)

Renjith G a intrebat.

Pot să știu utilizarea și logica din spatele conceptului de pointer opac în C?

Comentarii

  • Da, puteți: en.wikipedia.org/wiki/Opaque_pointer –  > Por David Heffernan.
  • 27

  • Nu am înțeles nimic din pagina wiki. –  > Por Renjith G.
  • Hmmm; această întrebare despre C a fost dublată de una etichetată C++. Asta nu este ideal. Există destule elemente comune încât nu este o problemă majoră, dar C++ are opțiuni și caracteristici care nu sunt disponibile în C. –  > Por Jonathan Leffler.
  • În legătură cu acestea: Structuri C opace: cum ar trebui declarate? –  > Por Gabriel Staples.
3 răspunsuri
paxdiablo

Un pointer opac este un pointer în care nu sunt dezvăluite detalii despre datele care stau la bază (dintr-o definiție de dicționar: opac: adjectiv; care nu poate fi văzut prin; care nu este transparent).

De exemplu, puteți declara într-un fișier header (aceasta este din o parte din codul meu real):

typedef struct pmpi_s *pmpi;

care declară un tip pmpi care este un pointer la opacitatea structură opacă struct pmpi_s, prin urmare, orice lucru pe care îl declarați ca pmpi va fi un pointer opac.

Utilizatorii acestei declarații pot scrie liber cod de genul:

pmpi xyzzy = NULL;

fără a cunoaște „definiția” reală a structurii.

Apoi, în codul care cunoaște definiția (adică, codul care furnizează funcționalitatea pentru pmpi manipulare, se poate „defini” structura:

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).
};

și să acceseze cu ușurință câmpurile individuale ale acesteia, lucru pe care utilizatorii fișierului de antet nu îl pot face.

Mai multe informații pot fi găsite pe site-ul pagina Wikipedia pentru pointeri opaci…

Principala utilizare este de a ascunde detaliile de implementare de utilizatorii bibliotecii dumneavoastră. Încapsularea (în ciuda a ceea ce vă va spune mulțimea C++) există de mult timp 🙂

Doriți să publicați doar suficiente detalii despre biblioteca dvs. pentru ca utilizatorii să o poată utiliza în mod eficient, și nu mai mult. Publicarea mai multor detalii le oferă utilizatorilor detalii pe care aceștia ar putea ajunge să se bazeze (cum ar fi faptul că variabila de mărime sz se află într-o anumită locație din structură, ceea ce i-ar putea determina să ocolească controalele dvs. și să o manipuleze direct.

Apoi vă veți găsi clienții dumneavoastră plângându-se amarnic atunci când schimbați elementele interne. Fără aceste informații despre structură, API-ul dumneavoastră este limitat doar la ceea ce furnizați, iar libertatea dumneavoastră de acțiune în ceea ce privește elementele interne este menținută.

Comentarii

  • Ei bine, la ce folosește acest lucru? –  > Por Renjith G.
  • Opacitatea pointer este xyzzy, da. Pointerul opac tip este pmpi iar tipul de pointer opac este struct pmpi. Principalul utilizare are legătură cu încapsularea, capacitatea de a ascunde detaliile de implementare de utilizatori. Voi actualiza răspunsul cu detalii. –  > Por paxdiablo.
  • Mulțumesc foarte mult pentru ajutor. apreciez cooperarea dvs. –  > Por Renjith G.
  • @VinothKumar, singurul lucru pe care un utilizator ar trebui să îl facă cu pointerul opac este să îl obțină de la, sau să îl transmită la, funcțiile care fac știu despre componentele sale interne, la fel ca fopen sau fclose pentru FILE *. Fie că FILE este cu adevărat opac depinde de ceea ce se găsește în stdio.h – dacă este declarat în întregime acolo, mai degrabă decât un tip similar cu cel din acest răspuns, utilizatorii pot ajunge la elementele interne, astfel încât este opac doar prin consimțământ reciproc 🙂 –  > Por paxdiablo.
  • Rețineți că typedef struct pmpi_s *pmpi; nu este ideal: cineva poate presupune că, de ex. void f(const pmpi val) nu are efecte secundare asupra argumentului său, ceea ce nu ar fi adevărat. –  > Por Dmitri Grigoriev.
Kaz

Indicatorii opaci sunt utilizați în definițiile interfețelor de programare (API-uri).

În mod obișnuit, aceștia sunt pointeri la tipuri de structuri incomplete, declarate astfel:

typedef struct widget *widget_handle_t;

Scopul lor este de a oferi programului client o modalitate de a păstra o referință la un obiect gestionat de API, fără a dezvălui nimic despre implementarea acelui obiect, în afară de adresa sa în memorie (pointerul propriu-zis).

Clientul poate transmite obiectul, îl poate stoca în propriile structuri de date și poate compara doi astfel de pointeri dacă sunt identici sau diferiți, dar nu poate să dereglementeze pointerii pentru a vedea ce se află în obiect.

Motivul pentru care se procedează astfel este acela de a împiedica programul client să devină dependent de aceste detalii, astfel încât implementarea să poată fi actualizată fără a fi nevoie să se recompileze programele client.

Deoarece indicatorii opaci sunt tipizați, există o bună măsură de siguranță a tipului. Dacă avem:

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

int api_function(widget_handle_t, gadget_handle_t);

dacă programul client încurcă ordinea argumentelor, va exista un diagnostic din partea compilatorului, deoarece o struct gadget * este convertit în a struct widget * fără o cast.

Acesta este motivul pentru care definim struct care nu au membri; fiecare tip struct declarație cu o nouă etichetă diferită introduce un nou tip care nu este compatibil cu cele declarate anterior. struct declarate anterior.

Ce înseamnă pentru un client să devină dependent? Să presupunem că un widget_t are proprietăți de lățime și înălțime. Dacă nu este opac și arată astfel:

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

atunci clientul poate să facă acest lucru pentru a obține lățimea și înălțimea:

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

în timp ce, în paradigma opacă, ar trebui să utilizeze funcții de acces (care nu sunt incluse):

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

Observați că widget autorii au folosit short pentru a economisi spațiu în structură și care a fost expus clientului interfeței neopacte. Să presupunem că widgeturile pot avea acum dimensiuni care nu se potrivesc în short și structura trebuie să se schimbe:

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

Codul clientului trebuie recompilat acum pentru a prelua această nouă definiție. În funcție de fluxul de lucru al instrumentelor și al implementării, poate exista chiar riscul ca acest lucru să nu fie făcut: codul client vechi încearcă să utilizeze noua bibliotecă și se comportă greșit prin accesarea noii structuri folosind vechea configurație. Acest lucru se poate întâmpla cu ușurință în cazul bibliotecilor dinamice. Biblioteca este actualizată, dar programele dependente nu sunt actualizate.

Clientul care utilizează interfața opacă continuă să funcționeze nemodificat și, prin urmare, nu necesită recompilare. Acesta doar apelează noua definiție a funcțiilor de accesor. Acestea se află în biblioteca de widgeturi și recuperează corect noua funcție int valori tipizate din structură.

Rețineți că, din punct de vedere istoric (și încă în prezent, aici și acolo), a existat, de asemenea, o practică lipsită de strălucire de a utiliza void * ca un tip de mâner opac:

typedef void *widget_handle_t;
typedef void *gadget_handle_t;

int api_function(widget_handle_t, gadget_handle_t);

În cadrul acestei scheme, puteți face acest lucru, fără niciun diagnostic:

api_function("hello", stdout);

API-ul Microsoft Windows este un exemplu de sistem în care se pot avea ambele variante. În mod implicit, diverse tipuri de mânere precum HWND (window handle) și HDC (context de dispozitiv) sunt toate void *. Prin urmare, nu există siguranță de tip; un HWND ar putea fi trecut acolo unde un HDC este așteptat, din greșeală. Dacă faceți acest lucru:

#define STRICT
#include <windows.h>

atunci aceste mânere sunt mapate în tipuri incompatibile între ele pentru a detecta aceste erori.

Rahul

Opac, așa cum sugerează și numele, este ceva prin care nu putem vedea. De exemplu, lemnul este opac. Un pointer opac este un pointer care indică o structură de date al cărei conținut nu este expus în momentul definirii sale.

Exemplu:

struct STest* pSTest;

Este sigur să atribuiți NULL unui pointer opac.

pSTest = NULL;