<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Kleine Linkerfrage]]></title><description><![CDATA[<p>Hallo,</p>
<p>kurz 2 Linkerfragen:</p>
<p>1. Wenn ich 2 UE a.cpp und b.cpp habe und es in b.cpp eine Funktion bFunc() gibt, die aus a.cpp die Funktion aFunc() aufruft, dann wird ja vom Compiler sowaas erzeugt: CALL XXXXX und der Linker trägt dann für XXXXX die Adresse von aFunc() ein.<br />
Aber wie ist das, wenn ich in b.cpp in der Funktion bFunc() eine andere Funktion aus b.cpp aufrufe, z.b. AnotherBFunc(). Generiert der Compiler dann gleich beim Übersetzen von b.cpp Code mit der Adresse oder wieder nur CALL XXXXX? (Ich tippe auf Letzteres)</p>
<p>2. Geht ein C++ Compiler genau einmal über eine UE? Oder mehrere Male wenn nötig (Ich tippe auf ersteres)</p>
<p>Danke!</p>
]]></description><link>https://www.c-plusplus.net/forum/topic/229902/kleine-linkerfrage</link><generator>RSS for Node</generator><lastBuildDate>Sun, 27 Sep 2026 11:08:54 GMT</lastBuildDate><atom:link href="https://www.c-plusplus.net/forum/topic/229902.rss" rel="self" type="application/rss+xml"/><pubDate>Wed, 17 Dec 2008 22:28:57 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Kleine Linkerfrage on Wed, 17 Dec 2008 22:28:57 GMT]]></title><description><![CDATA[<p>Hallo,</p>
<p>kurz 2 Linkerfragen:</p>
<p>1. Wenn ich 2 UE a.cpp und b.cpp habe und es in b.cpp eine Funktion bFunc() gibt, die aus a.cpp die Funktion aFunc() aufruft, dann wird ja vom Compiler sowaas erzeugt: CALL XXXXX und der Linker trägt dann für XXXXX die Adresse von aFunc() ein.<br />
Aber wie ist das, wenn ich in b.cpp in der Funktion bFunc() eine andere Funktion aus b.cpp aufrufe, z.b. AnotherBFunc(). Generiert der Compiler dann gleich beim Übersetzen von b.cpp Code mit der Adresse oder wieder nur CALL XXXXX? (Ich tippe auf Letzteres)</p>
<p>2. Geht ein C++ Compiler genau einmal über eine UE? Oder mehrere Male wenn nötig (Ich tippe auf ersteres)</p>
<p>Danke!</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1632169</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1632169</guid><dc:creator><![CDATA[ALinkerToThePast]]></dc:creator><pubDate>Wed, 17 Dec 2008 22:28:57 GMT</pubDate></item><item><title><![CDATA[Reply to Kleine Linkerfrage on Thu, 18 Dec 2008 01:19:00 GMT]]></title><description><![CDATA[<p>Ich schätze es wird üblich sein dass Adressen erst beim Linken erzeugt werden. Schonmal deswegen weil einige Plattformen mit absoluten Adressen arbeiten, und absolute Adressen können erst erzeugt werden wenn alle Object-Files/Libraries/... bekannt sind.<br />
Und auch weil einige Implementierungen es ermöglichen unbenutzte Funktionen nicht &quot;mitzulinken&quot; - also quasi wegzulassen. Durch das Weglassen dieser Funktionen würden sich auch relative Adressen innerhalb eines Object-Files verschieben -&gt; d.h. auch die finalen relativen Adressen könnten in dem Fall erst beim Linken erzeugt werden.</p>
<p>Was das &quot;single pass&quot; vs. &quot;multi pass&quot; angeht: ich würde sagen ein klassischer &quot;single pass&quot; Compiler für C++ ist unmöglich.</p>
<ol>
<li>
<p>Es gibt den Präprozessor. Ob dieser Teil des &quot;Compilers&quot; ist ist Ansichtssache. Vom Standard vorgeschrieben ist er, Teil des gesamten Übersetzungsvorganges muss er daher immer sein, also denke ich sollten wir ihn dazurechnen. Den Präprozessor sollte man natürlich &quot;streamlinen&quot; können, d.h. dieser allein macht noch keinen 2. Pass notwändig.</p>
</li>
<li>
<p>Es gibt Templates, und Templates viele Dinge machen die von Template-Parametern abhängig sind. Das &quot;Instanzieren&quot; von Templates ist etwas was in dem was ich mir unter einem klassischen &quot;single pass&quot; Compiler vorstelle nicht möglich ist. Natürlich muss der Compiler um ein Template zu instanzieren nicht 1:1 den Source-Code nochmal durchackern, aber gewisse Datenstrukturen die er beim parsen des Template-Codes erzeugt hat muss er dazu zumindest durchackern. Natürlich muss ein Compiler das auch wenn irgendwo ein Funktionsaufruf steht -- dazu muss er ja wissen welche Funktionen mit einem bestimmten Namen etc. es gibt. Beim Instanzieren von Templates muss aber wesentlich mehr passiere: es muss wirklich &quot;Code&quot; (in irgendeiner Form) interpretiert werden. Das sieht man auch schon daran was man mit Templates alles anfangen kann (z.B. diverse Berechnungen &quot;statisch&quot;, also beim Compilieren, durchzuführen, und nur das fertige Ergebnis &quot;ins Programm zu compilieren&quot;, oder auch die Auswahl eines bestimmten Typs von solchen Berechnungen abhängig zu machen).<br />
Weiters kann man in C++ Code auf viele Dinge zugreifen die &quot;weiter unten im Code stehen&quot;, auch ohne forward-declarations. Ein Beispiel sind Member von Klassen, auf die man in inline implementierten Funktionen zugreift. Sowas:</p>
</li>
</ol>
<pre><code class="language-cpp">int a;
int b;
int c;

class bar
{
public:
   void test()
   {
       a += char(0); // bar::a, einfache integer addition
       b += char(0); // bar::b, std::string::operator += (char)
       c += char(0); // globales ::c, einfache integer addition
       // ...
   }

private:
   int a;
   std::string b;
};
</code></pre>
<p>Weiters optimieren viele Compiler, und für Optimierungen ist es oft nötig den selben Code mehrfach anzuschauen. Beispielsweise gibt es Compiler die Funktionen inline erweitern können, und zwar auch an Stellen im Code wo der Compiler die Implementierung der Funktion &quot;noch garnicht gesehen hat&quot;. Ein klassischer &quot;single pass&quot; Compiler kann auch das nicht.</p>
<p>Die Antwort auf deine Frage hängt also davon ab was du unter &quot;einmal drübergehen&quot; verstehst. Wenn du meinst dass der Compiler nur 1x genau den Text durchgeht, 1:1 so wie er in den Source-Files drinsteht, dann gibt es vermutlich viele Compiler die das so machen. (Diese Compiler &quot;parsen&quot; den Source-Code indem sie genau 1x über den Quelltext drübergehen, erzeugen sich dabei interne Datenstrukturen, und arbeiten dann im Weiteren nur mit diesen internen Datenstrukturen. Sozusagen.)</p>
<p>Wenn du damit allerdings meinst, dass, nachdem der Compiler das letzte Zeichen einer Funktion oder Klasse &quot;verarbeitet&quot; hat, der komplette &quot;Code&quot; für diese Funktion oder Klasse feststeht, dann ganz klar nein.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1632206</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1632206</guid><dc:creator><![CDATA[hustbaer]]></dc:creator><pubDate>Thu, 18 Dec 2008 01:19:00 GMT</pubDate></item><item><title><![CDATA[Reply to Kleine Linkerfrage on Fri, 19 Dec 2008 17:05:37 GMT]]></title><description><![CDATA[<p>Hm, könnte dazu vielleicht noch jemand was sagen? Am besten irgend ein Low-Level Freak? <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f603.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--grinning_face_with_big_eyes"
      title=":D"
      alt="😃"
    /><br />
Ich würde noch immer gerne genau wissen, ob der Compiler selber gleich die Adresse einer Funktion Foo einträgt, wenn Foo in der selben UE steht, also sowas:</p>
<pre><code class="language-cpp">// a.cpp
void bar();

void foo() {
   bar();
}

void bar() { }
</code></pre>
<p>Erzeugt dann der Compiler beim Übersetzen von a.cpp gleich ein call AdresseVonBar oder nur ein call XXXXX und der Linker ersetzt dann XXXXX durch die Adresse von bar?</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1633007</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1633007</guid><dc:creator><![CDATA[ALinkerToThePast]]></dc:creator><pubDate>Fri, 19 Dec 2008 17:05:37 GMT</pubDate></item><item><title><![CDATA[Reply to Kleine Linkerfrage on Fri, 19 Dec 2008 17:22:37 GMT]]></title><description><![CDATA[<p>Deine Frage kann man nicht allgemein beantworten. Es gibt nicht &quot;den&quot; Compiler und &quot;den&quot; Linker.<br />
Wenn du genau sagst welcher Compiler und welcher Linker kann man dir vielleicht sogar die &quot;genaue&quot; Antwort geben die du dir wünscht.</p>
<p>---</p>
<p>In deinem Beispile kann ich dir sagen dass z.B. MSVC (mit Optimierungen an) keines von beiden machen wird: er wird den Aufruf von bar() gleich ganz weglassen -- wozu eine Funktion aufrufen die *nichts* macht?</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1633015</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1633015</guid><dc:creator><![CDATA[hustbaer]]></dc:creator><pubDate>Fri, 19 Dec 2008 17:22:37 GMT</pubDate></item><item><title><![CDATA[Reply to Kleine Linkerfrage on Fri, 19 Dec 2008 17:43:41 GMT]]></title><description><![CDATA[<p>Das Beispiel geht natürlich davon aus, dass die funktion nicht rausgekürzt wird. Es geht doch nur um das Aufrufbeispiel und da hab ich keine Lust eine endlos lange Funktion zu posten.<br />
Nehmen wir als Compiler einfach den MSVC. Wie wird der das machen?</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1633022</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1633022</guid><dc:creator><![CDATA[ALinkerToThePast]]></dc:creator><pubDate>Fri, 19 Dec 2008 17:43:41 GMT</pubDate></item><item><title><![CDATA[Reply to Kleine Linkerfrage on Fri, 19 Dec 2008 17:45:23 GMT]]></title><description><![CDATA[<p>Ich denke mal, dass das hier interessant sein könnte:</p>
<p><a href="http://msdn.microsoft.com/en-us/magazine/cc301698.aspx" rel="nofollow">http://msdn.microsoft.com/en-us/magazine/cc301698.aspx</a></p>
]]></description><link>https://www.c-plusplus.net/forum/post/1633025</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1633025</guid><dc:creator><![CDATA[drakon]]></dc:creator><pubDate>Fri, 19 Dec 2008 17:45:23 GMT</pubDate></item><item><title><![CDATA[Reply to Kleine Linkerfrage on Sat, 20 Dec 2008 05:10:51 GMT]]></title><description><![CDATA[<p>MSVC wird, in diesem minimalistichen Beispiel, auch wenn in &quot;bar&quot; viel drinsteht, den Call inlinen, weil er sieht dass bar nur 1x aufgerufen wird, und das Inlining sich deshalb auf jeden Fall auszahlt. (ausgenommen &quot;bar&quot; wäre extremst riesig, oder enthält Konstrukte die der Compiler nicht inlinen kann)</p>
<p>Jetzt wirst du wieder fragen &quot;ja, aber was wenn MSVC die Funktion NICHT inlinen kann?&quot;. Dann werde ich antworten: das kommt u.U. auch auf die Compiler-Einstellungen drauf an. <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f642.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--slightly_smiling_face"
      title=":)"
      alt="🙂"
    /></p>
<p>Wieso willst du das überhaupt so genau wissen? (würde mich nur interessieren - bestimmter Grund oder einfach Neugier)</p>
<p>Dass theoretisch beides möglich ist solltest du aus meinen Antworten ja bereits rausgelesen haben.</p>
<p>Davon abgesehen...</p>
<p>Ich habe jetzt nachgesehen: x86 Code verwendet bei direkten Sprüngen normalerweise relative Adressen, d.h. der Compiler *könnte* gleich die fertige (relative) Adresse reinschreiben. Ich hatte das anders in Erinnerung - da sieht man mal wieder wie man sich täuschen kann <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f642.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--slightly_smiling_face"
      title=":)"
      alt="🙂"
    /><br />
(x64 verwendet vermutlich ich auch relative Adressen)</p>
<p>Was ich aber sicher weiss: mit Function-Level-Linking (Compiler Option) *muss* der Linker die Adresse nochmal anpassen, da auch die relative Adresse einer Funktion in der selben Übersetzungseinheit sich beim Linken ändern kann.</p>
<p>Function-Level-Linking: <a href="http://msdn.microsoft.com/en-us/library/xsa71f43(VS.80).aspx" rel="nofollow">http://msdn.microsoft.com/en-us/library/xsa71f43(VS.80).aspx</a></p>
<p>Und ich bin mir ziemlich sicher dass auch ohne Function-Level-Linking der Linker die Adresse nachträglich anpassen wird. Garantieren kann ich es allerdings nicht.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1633184</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1633184</guid><dc:creator><![CDATA[hustbaer]]></dc:creator><pubDate>Sat, 20 Dec 2008 05:10:51 GMT</pubDate></item><item><title><![CDATA[Reply to Kleine Linkerfrage on Sun, 21 Dec 2008 00:56:41 GMT]]></title><description><![CDATA[<p><a class="plugin-mentions-user plugin-mentions-a" href="https://www.c-plusplus.net/forum/uid/18422">@drakon</a>: Danke für den Link. Er ist zwar sehr interessant, beantwortet jedoch nicht meine Frage (in dem Artikel gehts nur um die Optimierung von Calls, Threadlocal data und Inlining bei Benutzen von LTCO)</p>
<p>hustbaer schrieb:</p>
<blockquote>
<p>Wieso willst du das überhaupt so genau wissen? (würde mich nur interessieren - bestimmter Grund oder einfach Neugier)</p>
</blockquote>
<p>Hauptsächlich aus reiner Neugier.</p>
<p>Aber die 2. Frage entstand, weil ich mich gefragt habe wieso ich überhaupt Prototypen brauche, wenn ich eine Funktion aufrufe? In Java oder C# brauch ich die ja auch nicht. In C++ liefert das ja schon einen Compilefehler:</p>
<pre><code class="language-cpp">void bar() { foo(); }

void foo() { }
</code></pre>
<p>Das kann ich mir eigentlich nur dadurch erklären, dass der C++ Compiler (MSVC) nur einmal über die UE geht. Würde er z.B. einfach 2mal drübergehen, dann hätte er ja beim 2. Durchlauf die Definition von foo() und könnte bar() übersetzen.</p>
<p>Was du mit relativen Adressen bei Sprüngen meinst, ist mir nicht klar. Die Windows PE Dateien benutzen bei Calls und JMPs alle absolute Adressen (die zur Not reallokiert werden, wenn die BaseImageAdresse != AdresseImVirtSpeicherAnDieDasExecutableGeladen wird)</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1633446</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1633446</guid><dc:creator><![CDATA[ALinkerToThePast]]></dc:creator><pubDate>Sun, 21 Dec 2008 00:56:41 GMT</pubDate></item><item><title><![CDATA[Reply to Kleine Linkerfrage on Sun, 21 Dec 2008 05:04:02 GMT]]></title><description><![CDATA[<p>ALinkerToThePast schrieb:</p>
<blockquote>
<p>Das kann ich mir eigentlich nur dadurch erklären, dass der C++ Compiler (MSVC) nur einmal über die UE geht. Würde er z.B. einfach 2mal drübergehen, dann hätte er ja beim 2. Durchlauf die Definition von foo() und könnte bar() übersetzen.</p>
</blockquote>
<p>Das hat eher historische Gründe, und weniger damit zu tun, was der Compiler tun könnte und nicht tun könnte. Viele Dinge, die heutige C++ Compiler tun könnten, sind laut Standard &quot;falsch&quot;, und werden daher von Compilern auch als Fehler behandelt. Dass der Compiler Funktionen, die in der selben UE definiert werden, auch ohne Prototyp &quot;verstehen&quot; kann, sieht man ja auch daran, dass es mit implizit inline implementierten Memberfunktionen funktioniert.</p>
<blockquote>
<p>Was du mit relativen Adressen bei Sprüngen meinst, ist mir nicht klar. Die Windows PE Dateien benutzen bei Calls und JMPs alle absolute Adressen (die zur Not reallokiert werden, wenn die BaseImageAdresse != AdresseImVirtSpeicherAnDieDasExecutableGeladen wird)</p>
</blockquote>
<p>Mit relativen Sprüngen meine ich relative Sprünge <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f615.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--confused_face"
      title=":confused:"
      alt="😕"
    /> . Das PE Format kann beim &quot;re-basen&quot; absolute Adressen anpassen, das heisst aber nicht dass jeder Sprung/Call absolut adressiert sein muss. Im Gegenteil, alles was relativ adressiert werden kann wird normalerweise auch relativ adressiert. Schonmal deswegen, weil der Code für einen relativen Sprung kleiner ist, und auch weil so der Relocation Table nicht unnötig aufgeblasen wird. Guck dir einfach mal an, was MSVC für Code generiert, dann wirst du sehen, dass an vielen Stellen relative Adressen verwendet werden. (Du brauchst z.B. bloss mal im Debugger den disassemblierten Code anzeigen lassen, da lässt sich leicht erkennen was relativ und was absolut adressert wird)</p>
<p>Relativ bedeutet in dem Fall dass nur ein Offset zur Adresse an der der Sprung-Befehl selbst steht gespeichert wird - also quasi &quot;springe N Bytes nach vorne/nach hinten&quot;. Wenn sich das Ziel im gleichen &quot;modul&quot; befindet muss hier nie etwas angepasst werden, da unter Windows immer nur Module als ganzes &quot;re-based&quot; werden.</p>
<p>Das ganze hat aber nicht wirklich etwas mit single-pass vs. multi-pass zu tun, oder mit Prototypen vs. Type-Libraries bzw. Assemblies mit Type-Information (wie sie von C# oder Java verwendet werden) zu tun.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1633460</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1633460</guid><dc:creator><![CDATA[hustbaer]]></dc:creator><pubDate>Sun, 21 Dec 2008 05:04:02 GMT</pubDate></item></channel></rss>