Was ist ein undurchsichtiger Zeiger in C?

Lesezeit: 7 Minuten

Benutzeravatar von Renjith G
Renjith G

Darf ich die Verwendung und Logik hinter dem undurchsichtigen Zeigerkonzept in C kennen?

  • 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

Benutzeravatar von paxdiablo
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_salso 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 ist pmpi und das Undurchsichtige Struktur ist struct 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 oder fclose zum FILE *. Ob FILE wirklich undurchsichtig ist, hängt davon ab, was Sie darin finden stdio.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 zB void f(const pmpi val) hat keine Nebenwirkungen auf seine Argumentation, die nicht wahr wäre.

    – Dmitri Grigorjew

    1. September 2016 um 8:14 Uhr

Benutzeravatar von Kaz
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.

Benutzeravatar von Rahul
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; 

1420640cookie-checkWas ist ein undurchsichtiger Zeiger in C?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy