Darf ich die Verwendung und Logik hinter dem undurchsichtigen Zeigerkonzept in C kennen?
Was ist ein undurchsichtiger Zeiger in C?
Renjith G
paxdiablo
Ein undurchsichtiger Zeiger ist einer, bei dem keine Details der zugrunde liegenden Daten preisgegeben werden (aus einer Wörterbuchdefinition: undurchsichtig: Adjektiv; nicht durchschaubar; nicht transparent).
Beispielsweise können Sie in einer Header-Datei deklarieren (dies stammt aus einem Teil meines tatsächlichen Codes):
typedef struct pmpi_s *pmpi;
die einen Typ deklariert pmpi
was ein Hinweis auf das Undurchsichtige ist Struktur struct pmpi_s
also alles, was Sie als deklarieren pmpi
wird ein undurchsichtiger Zeiger sein.
Benutzer dieser Deklaration können Code wie diesen frei schreiben:
pmpi xyzzy = NULL;
ohne die eigentliche “Definition” der Struktur zu kennen.
Dann in dem Code, der die Definition kennt (d. h. der Code, der die Funktionalität für pmpi
Handling können Sie die Struktur “definieren”:
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).
};
und einfach auf die einzelnen Felder zugreifen, was Benutzern der Header-Datei nicht möglich ist.
Weitere Informationen finden Sie auf der Wikipedia-Seite für undurchsichtige Zeiger..
Der Hauptzweck besteht darin, Implementierungsdetails vor Benutzern Ihrer Bibliothek zu verbergen. Kapselung (ungeachtet dessen, was die C++-Crowd Ihnen sagen wird) gibt es schon lange 🙂
Sie möchten gerade genug Details zu Ihrer Bibliothek veröffentlichen, damit Benutzer sie effektiv nutzen können, und nicht mehr. Das Veröffentlichen von mehr gibt Benutzern Details, auf die sie sich verlassen können (z. B. die Tatsache, dass die Größenvariable sz
befindet sich an einer bestimmten Stelle in der Struktur, was dazu führen kann, dass sie Ihre Steuerelemente umgehen und direkt manipulieren.
Dann werden Sie feststellen, dass sich Ihre Kunden bitter beschweren, wenn Sie die Interna ändern. Ohne diese Strukturinformationen ist Ihre API nur auf das beschränkt, was Sie bereitstellen, und Ihre Handlungsfreiheit bezüglich der Interna bleibt erhalten.
-
Nun, was nützt das?
– Renjith G
26. September 2011 um 10:41 Uhr
-
Das Undurchsichtige Zeiger ist
xyzzy
, Jawohl. Der undurchsichtige Zeiger Typ istpmpi
und das Undurchsichtige Struktur iststruct pmpi
. Die wichtigsten verwenden davon hat mit der Kapselung zu tun, der Fähigkeit, Implementierungsdetails vor Benutzern zu verbergen. Ich werde die Antwort mit Details aktualisieren.– paxdiablo
26. September 2011 um 10:42 Uhr
-
Vielen Dank für die Hilfe. Vielen Dank für Ihre Zusammenarbeit.
– Renjith G
26. September 2011 um 10:50 Uhr
-
@VinothKumar, das einzige, was ein Benutzer mit dem undurchsichtigen Zeiger tun sollte, ist, ihn von Funktionen abzurufen oder an diese weiterzugeben tun wissen über seine Interna, genau wie
fopen
oderfclose
zumFILE *
. ObFILE
wirklich undurchsichtig ist, hängt davon ab, was Sie darin findenstdio.h
– Wenn es dort vollständig deklariert ist und nicht nur ein Typ ähnlich dem in dieser Antwort, können Benutzer auf die Interna zugreifen, sodass sie nur im gegenseitigen Einvernehmen undurchsichtig sind 🙂– paxdiablo
9. Januar 2015 um 5:49 Uhr
-
Beachten Sie, dass
typedef struct pmpi_s *pmpi;
ist nicht ideal: jemand kann davon ausgehen, dass zBvoid f(const pmpi val)
hat keine Nebenwirkungen auf seine Argumentation, die nicht wahr wäre.– Dmitri Grigorjew
1. September 2016 um 8:14 Uhr
Kaz
Undurchsichtige Zeiger werden in den Definitionen von Programmierschnittstellen (APIs) verwendet.
Typischerweise sind sie Zeiger auf unvollständige Strukturtypen, deklariert wie:
typedef struct widget *widget_handle_t;
Ihr Zweck besteht darin, dem Client-Programm eine Möglichkeit zu bieten, einen Verweis auf ein von der API verwaltetes Objekt zu halten, ohne etwas über die Implementierung dieses Objekts preiszugeben, außer seiner Adresse im Speicher (dem Zeiger selbst).
Der Client kann das Objekt herumreichen, es in seinen eigenen Datenstrukturen speichern und zwei solche Zeiger vergleichen, ob sie gleich oder unterschiedlich sind, aber er kann die Zeiger nicht dereferenzieren, um einen Blick auf das Objekt zu werfen.
Der Grund dafür besteht darin, zu verhindern, dass das Client-Programm von diesen Details abhängig wird, sodass die Implementierung aktualisiert werden kann, ohne Client-Programme neu kompilieren zu müssen.
Da die undurchsichtigen Zeiger typisiert sind, gibt es ein gutes Maß an Typsicherheit. Wenn wir haben:
typedef struct widget *widget_handle_t;
typedef struct gadget *gadget_handle_t;
int api_function(widget_handle_t, gadget_handle_t);
Wenn das Client-Programm die Reihenfolge der Argumente verwechselt, gibt es eine Diagnose vom Compiler, weil a struct gadget *
wird umgewandelt in a struct widget *
ohne Besetzung.
Das ist der Grund, warum wir definieren struct
Typen, die keine Mitglieder haben; jeder struct
-Deklaration mit einem anderen neuen Tag führt einen neuen Typ ein, der nicht mit zuvor deklarierten kompatibel ist struct
Typen.
Was bedeutet es für einen Klienten, abhängig zu werden? Angenommen, a widget_t
hat Breiten- und Höheneigenschaften. Wenn es nicht undurchsichtig ist und so aussieht:
typedef struct widget {
short width;
short height;
} widget_t;
dann kann der Client einfach dies tun, um die Breite und Höhe zu erhalten:
int widget_area = whandle->width * whandle->height;
während es unter dem undurchsichtigen Paradigma Zugriffsfunktionen verwenden müsste (die nicht inline sind):
// 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);
Beachten Sie, wie die widget
Autoren verwendeten die short
Typ, um Platz in der Struktur zu sparen, und die für den Client der nicht undurchsichtigen Schnittstelle verfügbar gemacht wurde. Angenommen, Widgets können jetzt Größen haben, die nicht hineinpassen short
und die Struktur muss sich ändern:
typedef struct widget {
int width;
int height;
} widget_t;
Der Client-Code muss jetzt neu kompiliert werden, um diese neue Definition aufzunehmen. Abhängig von den Tools und dem Bereitstellungsworkflow besteht möglicherweise sogar das Risiko, dass dies nicht erfolgt: Alter Clientcode versucht, die neue Bibliothek zu verwenden, und verhält sich falsch, indem er mit dem alten Layout auf die neue Struktur zugreift. Bei dynamischen Bibliotheken kann das leicht passieren. Die Bibliothek wird aktualisiert, die abhängigen Programme jedoch nicht.
Der Client, der die undurchsichtige Schnittstelle verwendet, arbeitet unverändert weiter und muss daher nicht neu kompiliert werden. Es ruft nur die neue Definition der Accessor-Funktionen auf. Diese befinden sich in der Widget-Bibliothek und rufen die neuen korrekt ab int
typisierte Werte aus der Struktur.
Beachten Sie, dass es in der Vergangenheit (und immer noch hier und da) auch eine glanzlose Praxis der Verwendung von gegeben hat void *
Typ als undurchsichtiger Handle-Typ:
typedef void *widget_handle_t;
typedef void *gadget_handle_t;
int api_function(widget_handle_t, gadget_handle_t);
Unter diesem Schema können Sie dies ohne Diagnose tun:
api_function("hello", stdout);
Die Microsoft Windows-API ist ein Beispiel für ein System, in dem Sie beides haben können. Standardmäßig sind verschiedene Handle-Typen wie HWND
(Fenstergriff) und HDC
(Gerätekontext) sind alle void *
. Es gibt also keine Typensicherheit; a HWND
übergeben werden könnte, wo a HDC
wird fälschlicherweise erwartet. Wenn du das tust:
#define STRICT
#include <windows.h>
dann werden diese Handles gegenseitig inkompatiblen Typen zugeordnet, um diese Fehler abzufangen.
Raul
Opak ist, wie der Name schon sagt, etwas, das wir nicht durchschauen können. Holz ist zB undurchsichtig. Ein undurchsichtiger Zeiger ist ein Zeiger, der auf eine Datenstruktur zeigt, deren Inhalt zum Zeitpunkt ihrer Definition nicht offengelegt ist.
Beispiel:
struct STest* pSTest;
Es ist sicher zuzuordnen NULL
zu einem undurchsichtigen Zeiger.
pSTest = NULL;
Ja, Sie dürfen: en.wikipedia.org/wiki/Opaque_pointer
– David Heffernan
26. September 2011 um 10:31 Uhr
Ich habe nichts von der Wiki-Seite verstanden.
– Renjith G
26. September 2011 um 10:39 Uhr
Hmmm; Diese Frage zu C wurde zu einem mit C++ gekennzeichneten dupliziert. Das ist nicht ideal. Es gibt genug Gemeinsamkeiten, dass es kein großes Problem ist, aber C++ hat Optionen und Features, die in C nicht verfügbar sind.
– Jonathan Leffler
9. Februar 2016 um 15:43 Uhr
Verwandte: Undurchsichtige C-Strukturen: Wie sollten sie deklariert werden?
– Gabriel Staples
19. November 2020 um 22:22 Uhr