hallo,
in meiner stdio.h hab ich folgendes gesehen:
1
typedefstruct__FILEFILE;
ohne dass struct __FILE jemals fertig deklariert wurde. Das kann man
als Benutzer der lib später selber machen.
Was ist jetzt meine Frage? Immer wenn ich denke, ich kenne C nun recht
gut, kommt doch immer was neues. Daher meine Frage: Unter welche Regel
fällt das... könnt ihr mir eine Stelle zeigen, wo diese Eigenschaft von
C erklärt wird?
Ich hab doch glatt dann damit herumgespielt und
1
typedeflalamama;
hier geht das nicht, wenn lala nicht vorher deklariert wurde... woran
liegt das... oder anders gefragt, wann darf man solche nicht fertigen
deklarationen verwenden?
dankö schonmal fürs lesen
>> hier geht das nicht,
klar.
Der Unterschied ist, dass im FILE Beispiel erst mal klar ist, dass es
sich grundsätzlich um einen struct handelt.
Hier, in diesem Beispiel ist erst mal überhaupt nichts klar.
> liegt das... oder anders gefragt, wann darf man solche nicht fertigen> deklarationen verwenden?
Auf der einen Seite will man natürlich eine gewisse Felxibilität haben.
Auf der anderen Seite soll aber auch nicht jeder Tippfehler so lange
nicht mitgezogen werden, dass die irgendwann fällige Fehlermeldung
keinen irgendwie erkennbaren Bezug mehr zum eigentlichen Problem hat.
Bei einem typedef soll das was definieret wird, wenigstens ungefähr so
aussehen, als ob es richtig sein könnte. D.h. es sollte wenigstens wie
eine Variablendeklaration aussehen. Wenn der Name einer struct nicht
bekannt ist, dann ist sie eben an dieser Stelle noch undefiniert. Aber
zumindest ist klar, dass es sich erst mal um eine struct handelt, selbst
wenn der genau Aufbau der Struct zu diesem Zeitpunkt noch unbekannt ist.
Das ist auch gut so, damit man in rekursiven, verpointerten
Datenstukturen den typedef als 'forward typedef' vorziehen kann.
>> ohne dass struct __FILE jemals fertig deklariert wurde. Das kann man> als Benutzer der lib später selber machen.
Nein, das darfst du auf garkeinen Fall selber machen! Und es ist auch
nicht notwendig.
Es handelt sich dabei um einen "incomplete type". Von einem solchen
bekommt man nir die Adresse (etwa bei fopen) und darf diese vergleichen
(gegen andere FILE*), kopieren und weitergeben, etwa als erstes
Funktionsargument von fprintf:
1
intfprintf(FILE*,constchar*,...);
FILE wird innerhalb der Implementierung der Bibliothek, hier der libc,
zu einem vollständigen Typen gemacht auf den dann auch — innerhalb der
libc — zugegriffen werden kann, d.h. dort kann man FILE* dereferenzieren
und die Komponenten von FILE lesen und schreiben.
Wenn du FILE selbst zu einem complete Type machst, stehen die Chancen
sehr gut, daß du nicht die richtige Definition erwischst und
Änderungen an FILE machst, die inkompatibel zur Definition der libc
sind.
Incomplete Types werden dort eingesetzt, wo es dem Anwender genügt,
einen "Handle" auf das Objekt zu haben. Sämtliche Zugriffe auf das Ding
sind in der Bibliothek verborgen und nicht öffentlich.
Nicht unüblich ist auch, daß hinter speziellen Streams kein
FILE-Objekt vorhanden ist, sondern es sich nur um spezielle Adressen
handelt wie zum Beispiel:
Johann L. schrieb:>>typedef struct __FILE FILE;>>> ohne dass struct __FILE jemals fertig deklariert wurde. Das kann man>> als Benutzer der lib später selber machen.>> Nein, das darfst du auf garkeinen Fall selber machen!
Was man auch schon daran sieht, daß der Name mit zwei Unterstrichen
gefolgt von einem Großbuchstaben beginnt. Solche Namen sind für den
Compiler resierviert. Von denen hat man die Finger zu lassen.
gleich zwei anwendungsfelder für "incompleted type". jetzt da ich weiss
wie die dinger heissen hab ich auch was im netz dazu gefunden.
danke
> Nein, das darfst du auf garkeinen Fall selber machen!
die manuals zu meinen libs erlauben das explizit. wird wohl
herstellerspezifisch sein...?
sina anargo schrieb:> wird wohl herstellerspezifisch sein...?
Ja. Wenn es so dokumentiert ist, darfst (und sollst) du das
natürlich tun.
Generell erlaubt es C, dass man einen Strukturtypen auch unvollständig
deklarieren kann. Damit macht man dem Compiler lediglich den Namen
dieser Struktur bekannt, und das Einzige, was man damit hernach (solange
er nicht vollständig deklariert worden ist) anstellen kann ist, einen
Zeiger darauf zu bilden. Den kann man hin und her reichen, aber
natürlich nicht dereferenzieren. Für viele Fälle genügt das bei
einer Bibliothek ja auch: man hat eine Funktion, die einen Zeiger auf
eine Struktur zurückgibt, den merkt man sich im Programm, und später
gibt es Bibliotheksfunktionen, denen man diesen Zeiger wieder
rüberreicht. Worauf er wirklich zeigt, ist dabei der Applikation
völlig wurscht (oder tofu, für die Vegetarier :).
Im Prinzip ist das nichts anderes, als wenn man gleich einen "void *"
herumreichen würde, allerdings kann der Compiler so zumindest noch
warnen, falls man nicht den Zeiger auf die gewünschte Struktur
übergeben will.
Dass man das Ganze dann auch noch in einen typedef verpacken kann, ist
dabei eher ein Nebeneffekt.
Jörg Wunsch schrieb:> sina anargo schrieb:>> wird wohl herstellerspezifisch sein...?>> Ja. Wenn es so dokumentiert ist, darfst (und sollst) du das> natürlich tun.
Der Anwendungsfall würd mich aber doch interessieren.
Die Lib ist ja vermutlich bereits gegen einen kompletten Typen
kompiliert worden, der dem User allerdings im Header der Lib nur als
Forward Deklaration bekannt gemacht wird.
Nun kommt der User, vervollständigt den Typen und kann dadurch die
Zeiger dereferenzieren.
Aber was soll denn dabei bestenfalls rauskommen?
Letzendlich weiß der User ja nicht, wie die Datenstruktur der Lib
aussieht und kann mit seiner eigenen Typendefinition munter zwischen
irgendwelchen Membern rumklauben.