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.
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.
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.
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
staticconstinti=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.
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.