manchmal bin ich wirklich vergesslich, leichte Tendenz zu Alzheimer ;-( aber gottseidank gibt es mikrocontroller.net und dessen Suchfunktion. Die Sache über die ich heute wieder mal beim praktischen Programmieren gestolpert war, war die Verwendung des C Schlüsselworts "extern". Hab hier eine gute Erläuterung von Karl-Heinz Buchegger zu dem Thema gefunden, allerdings nicht unbedingt kurz gefasst. Vielleicht muss man manche Dinge manchmal auch ausführlich erklären. Wollte nur mal fragen, ob meine Kurzfassung von dem was ich glaube verstanden zu haben, korrekt ist: "extern" ist dafür da, um im Zweifelsfall eine Deklaration eindeutig als solche zu kennzeichnen und von einer Definition zu unterscheiden. Also mit extern stellt man sicher, dem Compiler nur zu sagen dass es die Variable oder Funktion irgendwo im Projekt gibt. Nicht mehr und nicht weniger. Der Punkt der mir nicht klar ist: wenn etwas in einer Header-Datei steht, ist es dann per se eine Deklaration - also kann man das "extern" in header Dateien getrost immer weg lassen?
Micha schrieb: > Also mit extern stellt man sicher, dem Compiler nur zu sagen dass es die > Variable oder Funktion irgendwo im Projekt gibt. Nicht mehr und nicht > weniger. Korrekt. > Der Punkt der mir nicht klar ist: wenn etwas in einer Header-Datei > steht, ist es dann per se eine Deklaration - also kann man das "extern" > in header Dateien getrost immer weg lassen? Header-Dateien werden in keiner Weise vom Compiler speziell behandelt. Wenn in einem File ein #include "header.h" steht, dann wird vom Präprozessor nichts weiter gemacht, als dieses include-Statement durch den im Header enthaltenen Code zu ersetzen. Also verhält sich "extern" in einem Header exakt genauso, wie es sich verhalten würde, wenn es in jedem c-File stünde, das diesen Header einbindet.
Danke! Das war genau das Stück Gewissheit das mir noch gefehlt hat! Die Erklärung macht Sinn.
Am besten schaust du dir den Prozess Quelldatei -> ausführbare Datei an, vor allem was der Linker da macht. Mit extern machst du dem Compiler klar, dass sich die Variable in einer anderen Quellcodedatei befindet. Dann ist es nämlich die Aufgabe des Linkers nach dem Kompilieren die Adresse der Variable einzutragen.
Oh mir fällt gerade auf, dass das nicht ganz stimmt, denn der Linker muss das immer machen auch ohne "extern".
TriHexagon schrieb: > Oh mir fällt gerade auf, dass das nicht ganz stimmt, denn der Linker > muss das immer machen auch ohne "extern". Nein, bei Objekten, die innerhalb der gleichen Übersetzungseinheit definiert sind, macht das der Compiler; bei Funktionen oder Variablen, die als "static" deklariert sind, gibt es sogar kein korrespondierendes Symbol in der Symboltabelle, das der Linker auflösen könnte.
Ach so, dann werden die globalen Variablen gar nicht immer zentral an einer Stelle angelegt. Wieder was gelernt.
TriHexagon schrieb: > Ach so, dann werden die globalen Variablen gar nicht immer zentral an > einer Stelle angelegt. Was hat das mit meiner Aussage zu tun?
Rufus Τ. Firefly schrieb: > Nein, bei Objekten, die innerhalb der gleichen Übersetzungseinheit > definiert sind, macht das der Compiler; Nö, wie sollte das denn funktionieren? Rufus Τ. Firefly schrieb: > bei Funktionen oder Variablen, > die als "static" deklariert sind, gibt es sogar kein korrespondierendes > Symbol in der Symboltabelle, das der Linker auflösen könnte Doch, gibt es. Eine Ausnahme sind allenfalls Funktionen, die mit einem relativen call erreicht werden können.
Stefan Ernst schrieb: > Rufus Τ. Firefly schrieb: >> Nein, bei Objekten, die innerhalb der gleichen Übersetzungseinheit >> definiert sind, macht das der Compiler; > > Nö, wie sollte das denn funktionieren? Wie sollte es denn sonst funktionieren? > Rufus Τ. Firefly schrieb: >> bei Funktionen oder Variablen, >> die als "static" deklariert sind, gibt es sogar kein korrespondierendes >> Symbol in der Symboltabelle, das der Linker auflösen könnte > > Doch, gibt es. Nein. Wozu auch? static bedeutet, daß der Name in anderen Übersetzungseinheiten nicht bekannt sein soll, und das geht am einfachsten, indem man kein Linkersymbol dafür generiert. > Eine Ausnahme sind allenfalls Funktionen, die mit einem relativen call > erreicht werden können. Was sollte das denn für einen Unterschied machen?
Rolf Magnus schrieb: > Stefan Ernst schrieb: >> Rufus Τ. Firefly schrieb: >>> Nein, bei Objekten, die innerhalb der gleichen Übersetzungseinheit >>> definiert sind, macht das der Compiler; >> >> Nö, wie sollte das denn funktionieren? > > Wie sollte es denn sonst funktionieren? Dir ist schon klar, dass es darum geht, wer die Adressen vergibt, oder? Das macht der Linker, nicht der Compiler. Rolf Magnus schrieb: >> Rufus Τ. Firefly schrieb: >>> bei Funktionen oder Variablen, >>> die als "static" deklariert sind, gibt es sogar kein korrespondierendes >>> Symbol in der Symboltabelle, das der Linker auflösen könnte >> >> Doch, gibt es. > > Nein. Wozu auch? Wozu? Damit der Linker für das Symbol eine Adresse vergeben und bei allen Referenzen innerhalb des Moduls eintragen kann. Rolf Magnus schrieb: > static bedeutet, daß der Name in anderen > Übersetzungseinheiten nicht bekannt sein soll Und? Was meinst du, warum es auch bei Linker-Symbolen eine Unterscheidung zwischen lokal und global gibt? Diese Symbole werden einfach direkt nach abarbeiten des aktuellen Moduls verworfen. Rolf Magnus schrieb: >> Eine Ausnahme sind allenfalls Funktionen, die mit einem relativen call >> erreicht werden können. > > Was sollte das denn für einen Unterschied machen? Das ist der einzige Fall, wo der Compiler bei statischen Objekten komplett ohne Mithilfe des Linkers auskommen kann.
Natürlich muß der Linker/Lokator auch statischen Variablen Speicher zuordnen. Der Compiler kann das nicht machen — es gibt eben noch keine spukhafte Fernwirkung zwischen C-Modulen, die dem COmpiler z.B. sagen könnte, daß ein anderes Modul die Adresse 0x12345678 bereits für eine (statische) Variable verwendet. Das einzige, was der Compiler machen kann, ist bestimmte variablen wegzuoptimieren, etwa
1 | static const int i = 3; |
Dafür braucht i.d.R. kein Speicher angelegt zu werden. Ein Fall, wo Speicher dafür gebraucht wird, ist wenn die Adresse von i das Modul verlässt.
:
Bearbeitet durch User
Ok gut dann werden die Adressen doch hauptsächlich vom Linker vergeben. Bei meinem zweiten Beitrag sind mir die Linkerscripts und die .data/.bss Sections wieder eingefallen. Daraus schloss ich, dass Variablen ob initialisiert oder nicht initialisiert in einem gemeinsamen Speicherbereich angelegt werden und dass kann dann eigentlich nur der Linker (die letzte Instanz). Der Kompiler schaut sich ja nur jede Quellcodedatei einzeln an, nicht aber alle gemeinsam.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.