Forum: Compiler & IDEs Handles oder Zeiger?


von Masl (Gast)


Lesenswert?

Hallo,
weis nicht ob das hier oder im PC Forum besser aufgehoben ist. Aber 
eigentlich eher eine Frage zu C, also => gcc.

Ich schreibe gerade ein Modul dass eine bestimmte Datenstruktur und 
verschiedene Operationen auf diese anbietet. Organisiert sind die Daten 
in einer Struktur.

Durch eine Init Funktion sollen mehrere "Instanzen" dieser Struktur 
angelegt werden können.

Die Schnittstellen des Moduls werden durch das Einbinden der Header 
Datei dem Programm bekannt gemacht.
Der "Benutzer" soll beim Aufrufen einer Funktion immer die Instanz der 
Datenstruktur übergeben auf die operiert wird. Am liebsten wär mir das 
über ein Handle. Wenn der Benutzer nämlich anstelle eines Handles die 
struct an sich sieht (und einen Zeiger auf diese übergibt) könnte er 
ohne Umweg über die Schnittstellen direkt an den Membern der struct 
rummanipulieren.

Aber bei einem Zugriff per Handle muss ich ja intern in meinem Modul 
irgendwie eine Liste führen, in der die Strukturen abgelegt sind.
Da setzts grad aus bei mir.

Soll ich mein struct um zwei Member erweitern um eine doppelt verkettete 
Liste zu verwalten? Aber das geht ja wieder mit Overhead für die 
Listenverwaltung einher.

Denkbar wäre noch ein Array mit Zeigern auf die structs. Aber ich weiß 
nicht wie leicht oder schwer es ist, ein Array dynamisch zu vergrößern 
oder zu verkleinern.

Wärend ich das schreibe merke ich übrigens, dass mein Vorhaben ein 
Paradebeispiel für OOP wäre...

Wisst ihr eine Lösung, das möglichst elegant hinzukriegen?

Zur Not muss ich die struct eben doch nach aussen hin bekannt geben, 
aber das halte ich wie gesagt für unschön.

von Klaus W. (mfgkw)


Lesenswert?

Da hast du zwei Möglichkeiten (mit Untervarianten natürlich):

1. Du vergibst als Handles irgendwelche IDs, z.B. fortlaufende Nummern, 
und merkst dir in einer Tabelle, welche Id zu welcher struct (bzw. 
Zeiger darauf) gehört.

2. Du redest dem Anwender gegenüber von einem Handle, zeigst ihm aber 
nur eine unsigned int passender Größe (so daß ein Zeiger reinpasst) oder 
wahlweise void*, und nur du selber weisst, daß sich dahinter tatsächlich 
die Adresse der zugehörigen struct verbirgt.
Der Anwender gibt dir also seine uint32_t/uint64_t oder void*, und du 
castest das dann jeweils in einen Zeiger auf die struct.

1. ist rechenzeitintensiver, weil anhand bsearch() oder einer 
Hashtabelle o.ä. jeweils von der ID auf die Adresse übersetzt werden 
muß.
Am schnellsten ist vermutlich, wenn deine Handles bei 0 starten und 
tatsächlich ein Eintrag in ein Feld sind; darin finden sich dann die 
Adressen der tatsächlichen structs. Dann geht die Übersetzung durch 
einen Feldzugriff und damit für die meisten Fälle wohl schnell genug.
Falls das Feld mit malloc() beschafft wird, kann es mit realloc() 
verkleinert oder vergrößert werden.
2. ist insofern etwas unelegant, als man bewusst casten muß. Bei void* 
ahnt der Anwender natürlich, daß sich die Adresse dahinter verbirgt, 
auch wenn sie ihn nichts angeht, und bei einer ganzzahligen Zahl musst 
du dafür sorgen, daß auf jedem verwendeten System ein Handle groß genug 
ist für eine Adresse, und das Wandeln von Adresse zu Zahl und zurück 
gilt nicht als elegant per se.
Dafür ist diese Methode am schnellsten, weil faktisch nichts gerechnet 
werden muß, um vom Handle zur struct zu kommen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Wenn du magst kannst du das komplett so kapseln, daß Zugriffe auf die 
Daten in main.c zu einem Syntaxfehler führen:
 
1
// data.h
2
struct data_s;
3
4
typedef struct data_s *data_t;
5
6
extern data_t data_create (void);
7
extern void data_init (data_t);
8
9
// main.c
10
11
void func (void)
12
{
13
    data_t data;
14
15
    data = data_create ();
16
    data_init (data);
17
}
18
19
// data.c
20
21
struct data_s
22
{
23
    int a, b;
24
};
25
26
#include <stdlib.h>
27
28
data_t data_create (void)
29
{
30
    return (data_t) malloc (sizeof (struct data_s));
31
}
32
33
void data_init (data_t d)
34
{
35
    d->a = 0;
36
    d->b = 0;
37
}
 
data.h stellt die Interfaces für main.c zu Verfügung. data_t is ein 
Zeiger auf einen incomplete Type der erst in data.c zu einem 
vollständigen Typen gemacht wird.  Erst dadurch kann in data.c via 
data_t auf die Komponenten von data_s zugegriffen werden.

von Hermann K. (r2d2)


Lesenswert?

Wie wär' es mit dieser Idee: Im Header-File nur ein
struct abc;

und dann alle Funktionen Pointer auf diesen Typ akzeptieren bzw. 
zurückgeben lassen.

So kann kein User auf die Elemente der Struktur zugreifen, aber trotzdem 
sind keine dummen Casts nötig.

EDIT: Da war wohl jemand schneller und ausführlicher als ich.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.