Forum: Compiler & IDEs not completed typedef


von Sina A. (sinapse)


Lesenswert?

hallo,

in meiner stdio.h hab ich folgendes gesehen:
1
typedef struct __FILE FILE;

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
typedef lala mama;

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

von Karl H. (kbuchegg)


Lesenswert?

sina anargo schrieb:


>
1
> typedef lala mama;
2
>
>
> 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.
1
typedef struct lala_ lala;
2
3
struct lala_
4
{
5
  lala* next;
6
};
7
8
int main()
9
{
10
  lala a;
11
}

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

sina anargo schrieb:
> in meiner stdio.h hab ich folgendes gesehen:
>
>
1
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! 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
int fprintf (FILE*, const char*, ...);

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:
1
#define stdin  ((FILE*) 1)
2
#define stdout ((FILE*) 2)
3
#define stderr ((FILE*) 3)

von Rolf Magnus (Gast)


Lesenswert?

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.

von Sina A. (sinapse)


Lesenswert?

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...?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Le X. (lex_91)


Lesenswert?

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.

von Sina A. (sinapse)


Lesenswert?

http://www.keil.com/support/man/docs/gsac/gsac_retargetcortex.htm

siehe stelle mit
1
/* Add whatever you need here */

lg

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.