Zitat von
roadracer
Gibt es einen Unterschied zwischen Header für Objektdateien und Header für Bibliotheken? Wegen Linken, etc. (Siehe Frage 2)
Wie sommerfee schon geschrieben hat, gibt es im ersten Moment keinen Unterschied. Allerdings ist die Sache mit den Symbol Direktiven nicht nur auf den Microsoft Compiler beschränkt, auch wenn man es dort am leichtesten als Problem bekommt.
Generell kennen die meisten Compiler heutzutage sogenannten Symbolsichtbarkeit, d.h. Symbole wie Funktionsnamen können vom Compiler so in der Bibliothek abgelegt werden, dass sie "unsichtbar" werden.
In diesem Fall sind sie zwar für anderen Code aus der Bibliothek selbst vorhanden, aber nicht für Programmcode der die Bibliothek benutzt.
Oft kommt man erstmal ohne aus, z.B. weil man nur statische Bibliotheken erzeugt, oder die entsprechende Compileroption aus ist.
Wenn man mal so ein Macro hat, ist es aber auch nicht sehr viel Aufwand, es entsprechend anzubringen.
Kommen in den Header auch die Präpozessor-Derektieven, wie z.B. #define und #include, der Quelldateien? Denn Objektdateien werden erst später gelinkt, und damit ja auch die Header/Bibliotheken.
Ich bin mir da nicht ganz sicher wie Sommerfee das gemeint hat, aber es gibt da unterschiedliche Strategien.
Es kann Sinn machen, alle Sachen einer Bibliothek in einem Header zu haben (bzw. einen Header, der alle anderen inkludiert).
Das ist sehr bequem, setzt aber praktisch eine Buildchain mit Unterstützung für Precompiled Headers vorraus (weil sonst für jede Quelldatei das ganze Ding durch gearbeitet werden müsste).
Eine andere Strategie ist, nur die Sachen zu inkludieren, die absolut notwendig für den Compiler sind, nicht unbedingt für die Benutzung.
Beispiel:
nehmen wir an man hat eine Structure
Code:
struct Foo {
int bar;
};
und in einem anderen Header eine Funktion, die diese Structure benutzt
Code:
int foo_bar( Foo *foo );
Der Compiler muss nun dafür nur über Foo Bescheid wissen, er braucht zu diesem Zeitpunkt noch kein Details, weil hier aussreicht, dass er weiß es ist ein Pointer.
D.h. es ist nicht falsch den Header für Foo zu inkludieren, man kann es aber auch mit einer sogenannten Forwarddeclaration machen
Code:
struct Foo;
int foo_bar( Foo *foo );
Um foo_bar() zu benutzen, muss man dann beide Header inkludieren.
Das ist weniger bequem, kann aber dann helfen, wenn viele solcher Abhängigkeiten in einem Header benötigt würden und man aber nicht alle Funktionen aufrufen möchte. D.h. man kann dann selektiv die Abhänigigkeiten dort inkludieren, wo man sie auch wirklicht braucht.
Kommen struct und union Definitionen (oder heißt es doch Deklarationen?) auch in den Header?
Generell ja, es sei denn, es sind "private" Datenstrukturen.
Das wird besonders bei Bibliotheken gern gemacht, um Implementationsdetails aus der API raus zuhalten und später die Möglichkeit zu haben diese zu ändern ohne dass davon abhängige Software neu gebaut oder sogar verändert werden muss.
Beispiel:
nehmen wir an deine Bibliothek hat Operationen auf Rechtecken. Mit einer öffentlichen Struct sieht das vielleicht so aus
Code:
struct Rect {
int x;
int y;
int width;
int height;
};
int area_size( Rect* rect );
Jemand kann also eine Instanz von Rect z.B. so befüllen
Code:
rect->x = 5;
rect->y = 10;
Zu einem späteren Zeitpunkt würdest du gerne Rect so ändern, dass es in Paar von gegenüberliegenden Eckpunkten ist.
Code:
struct Rect {
int x1;
int y1;
int x2;
int y2;
};
Das sind zwar wieder vier int, aber die Namen und die Bedeutung haben sich geändert.
Bei einer privaten oder "opaque" (also nicht durchsichtig) Structure sieht das in etwa so aus
Code:
struct Rect;
Rect *create_empty_rect();
void rect_set_x( iRect *rect, int x );
void rect_set_width( Rect *rect, int w );
Im Falle der ersten Variante sehen die Implementierung der beiden Funktionen so aus
Code:
void rect_set_x( Rect *rect, int x )
{
rect->x = x;
}
void rect_set_width( Rect *rect, int w )
{
rect->width = w;
}
Im Falle der zweiten Variante z.B. so:
Code:
void rect_set_x( Rect *rect, int x )
{
rect->x1 = x;
}
void rect_set_width( Rect *rect, int w )
{
rect->x2 = rect->x1 + w;
}
Es macht die Benutzung komplizierter, aber behält mehr Spielraum für die Implementierung.
Bei einfachen Strukturen wird man das meistens eher nicht machen, aber wenn man sich Sachen erweiterbar halten will, kann das manchmal die einzige Lösung sein.
Ciao,
_
Lesezeichen