<?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[Prefix in rekursiven Funktionen]]></title><description><![CDATA[<p>Guten Tag,</p>
<p>ich habe eine kleines Verständnisproblem und würde mich freuen, wenn mir jemand vielleicht kurz Zeit nimmt und mir auf die Sprünge hilft.</p>
<p>Soweit ich weiß gibt es ja die Prefix (--n) und Postfix (n--) möglichkeiten um variablen um den Wert 1 zu verringern. Nach meinem Verständnis liegt der Unterschied darin, dass bei einem Funktionsaufruf mit dem Prefix die Zahl erst um eins verringert wird und dann die Funktion aufgerufen wird, bei Postfix verwendung jedoch erst die Funktion mit dem ursprünglichen Wert aufgerufen wird und die Variable danach um 1 verringert.</p>
<p>Nun zum eigentlichen Problem:</p>
<p>um die Fakultät zu berechnen habe ich folgende rekursive Funktion geschrieben</p>
<pre><code>int fac(int n)
{
    int f = 1;
    if(n&gt;1)
    {
        f = n*fac(--n);
    }
    return f;
}
</code></pre>
<p>nach meinem bisherigen Verständnis sollte diese Schreibweise äquivalent zu</p>
<pre><code>int fac(int n)
{
    int f = 1;
    if(n&gt;1)
    {
        f = n*fac(n-1);
    }
    return f;
}
</code></pre>
<p>sein, ist es jedoch nicht. Die erste Variante liefert für fac(5) als Ergebnis die Fakultät von 4, die zweite Variante hingegen jedoch das richtige Ergebnis.</p>
<p>Und nun was mich völlig aus dem Konzept bringt:</p>
<pre><code>int fac(int n)
{
    int f = 1;
    if(n&gt;1)
    {
        f = n*f*fac(--n);
    }
    return f;
}
</code></pre>
<p>f wird bei jedem Funktionsaufruf auf 1 initialisiert, es sollte sich also am Ergebnis nichts ändern... dem ist aber nicht so. Beim Aufruf der letzten Funktion wird wieder das richtige Ergebnis fac(5)=120 geliefert.</p>
<p>Könnte mir jemand bitte erklären was da abgeht?? <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="😃"
    /></p>
<p>Würde mich sehr über Antworten freuen.</p>
<p>Viele Grüße,<br />
Martin</p>
]]></description><link>https://www.c-plusplus.net/forum/topic/333094/prefix-in-rekursiven-funktionen</link><generator>RSS for Node</generator><lastBuildDate>Mon, 27 Apr 2026 05:34:30 GMT</lastBuildDate><atom:link href="https://www.c-plusplus.net/forum/topic/333094.rss" rel="self" type="application/rss+xml"/><pubDate>Thu, 11 Jun 2015 14:25:47 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Prefix in rekursiven Funktionen on Thu, 11 Jun 2015 14:25:47 GMT]]></title><description><![CDATA[<p>Guten Tag,</p>
<p>ich habe eine kleines Verständnisproblem und würde mich freuen, wenn mir jemand vielleicht kurz Zeit nimmt und mir auf die Sprünge hilft.</p>
<p>Soweit ich weiß gibt es ja die Prefix (--n) und Postfix (n--) möglichkeiten um variablen um den Wert 1 zu verringern. Nach meinem Verständnis liegt der Unterschied darin, dass bei einem Funktionsaufruf mit dem Prefix die Zahl erst um eins verringert wird und dann die Funktion aufgerufen wird, bei Postfix verwendung jedoch erst die Funktion mit dem ursprünglichen Wert aufgerufen wird und die Variable danach um 1 verringert.</p>
<p>Nun zum eigentlichen Problem:</p>
<p>um die Fakultät zu berechnen habe ich folgende rekursive Funktion geschrieben</p>
<pre><code>int fac(int n)
{
    int f = 1;
    if(n&gt;1)
    {
        f = n*fac(--n);
    }
    return f;
}
</code></pre>
<p>nach meinem bisherigen Verständnis sollte diese Schreibweise äquivalent zu</p>
<pre><code>int fac(int n)
{
    int f = 1;
    if(n&gt;1)
    {
        f = n*fac(n-1);
    }
    return f;
}
</code></pre>
<p>sein, ist es jedoch nicht. Die erste Variante liefert für fac(5) als Ergebnis die Fakultät von 4, die zweite Variante hingegen jedoch das richtige Ergebnis.</p>
<p>Und nun was mich völlig aus dem Konzept bringt:</p>
<pre><code>int fac(int n)
{
    int f = 1;
    if(n&gt;1)
    {
        f = n*f*fac(--n);
    }
    return f;
}
</code></pre>
<p>f wird bei jedem Funktionsaufruf auf 1 initialisiert, es sollte sich also am Ergebnis nichts ändern... dem ist aber nicht so. Beim Aufruf der letzten Funktion wird wieder das richtige Ergebnis fac(5)=120 geliefert.</p>
<p>Könnte mir jemand bitte erklären was da abgeht?? <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="😃"
    /></p>
<p>Würde mich sehr über Antworten freuen.</p>
<p>Viele Grüße,<br />
Martin</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2456432</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2456432</guid><dc:creator><![CDATA[Mattin]]></dc:creator><pubDate>Thu, 11 Jun 2015 14:25:47 GMT</pubDate></item><item><title><![CDATA[Reply to Prefix in rekursiven Funktionen on Thu, 11 Jun 2015 14:32:14 GMT]]></title><description><![CDATA[<p>Beim ersten Code ändert das</p>
<pre><code class="language-cpp">n*fac(--n);
</code></pre>
<p>beide 'n'. Also auch das n*fac. Aber das willst du eigentlich gar nicht verändern hier.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2456433</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2456433</guid><dc:creator><![CDATA[KN4CK3R]]></dc:creator><pubDate>Thu, 11 Jun 2015 14:32:14 GMT</pubDate></item><item><title><![CDATA[Reply to Prefix in rekursiven Funktionen on Thu, 11 Jun 2015 14:50:34 GMT]]></title><description><![CDATA[<p>Ok, Soweit so gut... aber warum bewirkt nun eine Multiplikation mit dem auf 1 initialisierten f eine Änderung?</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2456437</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2456437</guid><dc:creator><![CDATA[Mattin]]></dc:creator><pubDate>Thu, 11 Jun 2015 14:50:34 GMT</pubDate></item><item><title><![CDATA[Reply to Prefix in rekursiven Funktionen on Thu, 11 Jun 2015 15:00:41 GMT]]></title><description><![CDATA[<p>Es ist nicht definiert, in welcher Reihenfolge die Operationen ausgewertet werden, d.h. ob zunächst des n vorne betrachtet und dann --n für den Funktionsaufruf ausgewertet wird oder anders herum. Durch die Multiplikation mit 1 ändert der Compiler die Reihenfolge =&gt; anderes (jetzt &quot;richtiges&quot;) Ergebnis.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2456440</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2456440</guid><dc:creator><![CDATA[manni66]]></dc:creator><pubDate>Thu, 11 Jun 2015 15:00:41 GMT</pubDate></item><item><title><![CDATA[Reply to Prefix in rekursiven Funktionen on Thu, 11 Jun 2015 15:43:09 GMT]]></title><description><![CDATA[<p>manni66 schrieb:</p>
<blockquote>
<p>Durch die Multiplikation mit 1 ändert der Compiler die Reihenfolge =&gt; anderes (jetzt &quot;richtiges&quot;) Ergebnis.</p>
</blockquote>
<p>Und das auch rein Zufällig. Ich habe hier gerade mal VS 2013 getestet und da ändert das multiplizeren mit f nichts und man kriegt immer noch das falsche Ergebniss.</p>
<p>Hier kann man mehr nachlesen: <a href="http://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points" rel="nofollow">http://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points</a></p>
]]></description><link>https://www.c-plusplus.net/forum/post/2456444</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2456444</guid><dc:creator><![CDATA[sebi707]]></dc:creator><pubDate>Thu, 11 Jun 2015 15:43:09 GMT</pubDate></item><item><title><![CDATA[Reply to Prefix in rekursiven Funktionen on Thu, 11 Jun 2015 16:02:23 GMT]]></title><description><![CDATA[<pre><code>f = n*fac(--n);
</code></pre>
<p>Das Verhalten dieses Programmes ist undefiniert (1.9/15). Zwischen der Modifizierung des Objektes n in</p>
<pre><code class="language-cpp">--n
</code></pre>
<p>und dem Lesen des Zustandes des Objektes n im Teilausdruck</p>
<pre><code class="language-cpp">n
</code></pre>
<p><em>besteht keine Reihenfolge</em> der Ausführung. Das Programm darf also legitimerweise die Festplatte formatieren.</p>
<p>Vergleiche mit</p>
<pre><code class="language-cpp">int&amp; prefix_dec(int&amp; n) { return --n; }
int fac(int n)
{
    int f = 1;
    if(n&gt;1)
    {
        f = n*fac(prefix_dec(n));
    }
    return f;
}
</code></pre>
<p>Wiederum ist es so, dass der Multiplikationsoperator keine Reihenfolge bei der Auswertung der beiden Operanden induziert. Allerdings findet hier die Modifikation des Objektes n innerhalb der Funktion prefix_dec statt.<br />
Die <em>speziellere Regel</em> zu Funktionsaufrufen besagt u.a., dass<br />
1. jeder Nebeneffekt der Auswertung einer Funktion einerseits erst nach Betreten jener Funktion stattfindet und bei Verlassen der Funktion abgeschlossen ist, und<br />
2. jder Nebeneffekt der Auswertung der Funktionsargumente bei Betreten der Funktion abgeschlossen ist (hier nicht relevant), und<br />
3. jeder andere Teil der Auswertung des Ausdruckes, der den Funktionsaufruf enthält, in <em>unbestimmter Weise entweder vor oder nach</em> dem Funktionsaufruf stattfindet (es sei denn eine speziellere Regel gibt eine bestimmte Reihenfolge vor).</p>
<p>Der wesentliche Unterschied zum ursprünglichen Fall ist der, dass eine Reihenfolge der Auswertung der kritischen Operationen (Modifikation von n und Zugruff auf den Wert von n) besteht. Der Compiler ist also gezwungen, Code zu erzeugen, der bei jedem Auftreten entweder den linken oder aber den rechten Operanden zuerst auswertet. <em>Welche</em> Wahl der Compiler trifft bleibt <em>unbestimmt</em>. Konsistenz wird dabei nicht verlangt, selbst der mehrfache Aufruf mit denselben Werten gerantiert also nicht gleiche Ergebnisse (diese Konsistenz wird nur in Hinblick auf mehrfache Aufrufe des gesamten Programmes verlangt, 1.9/5). Ein</p>
<pre><code class="language-cpp">assert( fac(5) == fac(5) );
</code></pre>
<p>darf fehlschlagen (reale Compiler werden in der Regel in konsistenter Weise reagieren, die Sprache schreibt das aber nicht vor - praktisch müsste man für diesen Effekt kräftig nachhelfen, z.B. indem eine inline Funktion in verschiedenen ÜEs verwendet wird und diese ÜEs mit unterschiedliche Optimierungsoptionen compiliert werden).</p>
<p>Beide Varianten sind folglich böse.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2456445</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2456445</guid><dc:creator><![CDATA[camper]]></dc:creator><pubDate>Thu, 11 Jun 2015 16:02:23 GMT</pubDate></item><item><title><![CDATA[Reply to Prefix in rekursiven Funktionen on Thu, 11 Jun 2015 17:18:57 GMT]]></title><description><![CDATA[<p>Was soll jetzt genau der Unterschied sein? Du meinst bei der Variante ohne Funktion können sich Lesen und Schreiben überlappen, sodass ich beispielsweise auf einer 32 Bit Architektur mit 64 Bit ints nur die &quot;halbe&quot; Subtraktion beim lesen von n sehe?</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2456447</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2456447</guid><dc:creator><![CDATA[sebi707]]></dc:creator><pubDate>Thu, 11 Jun 2015 17:18:57 GMT</pubDate></item><item><title><![CDATA[Reply to Prefix in rekursiven Funktionen on Thu, 11 Jun 2015 17:39:39 GMT]]></title><description><![CDATA[<p>sebi707 schrieb:</p>
<blockquote>
<p>Was soll jetzt genau der Unterschied sein? Du meinst bei der Variante ohne Funktion können sich Lesen und Schreiben überlappen, sodass ich beispielsweise auf einer 32 Bit Architektur mit 64 Bit ints nur die &quot;halbe&quot; Subtraktion beim lesen von n sehe?</p>
</blockquote>
<p>Inwiefern ist es überhaupt sinnvoll so eine Frage zu stellen? Das Verhalten ist undefiniert, und damit kann prinzipiell alles passieren. Welchen Effekt das in der Praxis genau hat, hängt völlig von verschiedenen Dingen ab.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2456448</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2456448</guid><dc:creator><![CDATA[Columbo]]></dc:creator><pubDate>Thu, 11 Jun 2015 17:39:39 GMT</pubDate></item><item><title><![CDATA[Reply to Prefix in rekursiven Funktionen on Thu, 11 Jun 2015 18:07:14 GMT]]></title><description><![CDATA[<p>Ich habe einfach versucht campers ziemlich lange Antwort zu verstehen. Es gibt ja auch sonst Dinge die in C++ undefiniert sind aber nicht dazu führen, dass meine Festplatte formatiert wird. Zum Beispiel</p>
<pre><code>int c = ++a * ++b;
</code></pre>
<p>Da ist auch nicht definiert ob zuerst a oder b incrementiert wird, aber trotzdem sind am Ende alle Werte klar definiert.</p>
<p>So habe ich auch campers Ausführungen verstanden:<br />
- Ohne Funktion: Alles kann passieren, bis zum Formatieren der Festplatte.<br />
- Mit Funktion: Der Compiler muss zumindest eins nach dem anderen ausführen. Damit kriege ich zwar ein irgendwie zufälliges Ergebnis aber es stürzt nicht ab und das Ergebniss muss eines der beiden Fälle (oder 2^N Fälle bei der Rekursion) ergeben.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2456451</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2456451</guid><dc:creator><![CDATA[sebi707]]></dc:creator><pubDate>Thu, 11 Jun 2015 18:07:14 GMT</pubDate></item><item><title><![CDATA[Reply to Prefix in rekursiven Funktionen on Thu, 11 Jun 2015 19:05:21 GMT]]></title><description><![CDATA[<blockquote>
<p>Es gibt ja auch sonst Dinge die in C++ undefiniert sind aber nicht dazu führen, dass meine Festplatte formatiert wird.</p>
</blockquote>
<p>Wenn du nicht sowieso dabei bist, eine Operation auszuführen die mit entsprechenden Argumenten deartiges tun könnte: Es ist wahrscheinlicher, dass Volkard in Stöckelschuhen und Latex vor deinem Haus auftaucht. Warum also nicht das als ein Beispiel nehmen? Volkard hat sicherlich nichts dagegen? (Also wie eine Schlampe gekleidet durch die Nachbarschaft zu rennen, meine ich. Nicht Subjekt eines dummen Witzes zu sein, das würde niemals jemand wagen.) <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f576.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--sunglasses"
      title=":sunglasses:"
      alt="🕶"
    /></p>
<blockquote>
<p>Da ist auch nicht definiert ob zuerst a oder b incrementiert wird</p>
</blockquote>
<p>Nicht <em>spezifiziert</em>. Kann auch beides gleichzeitig passieren.</p>
<blockquote>
<p>aber trotzdem sind am Ende alle Werte klar definiert.</p>
</blockquote>
<p>Alle möglichen.</p>
<blockquote>
<p>Alles kann passieren, bis zum Formatieren der Festplatte.</p>
</blockquote>
<p>Laut Standard - der aber keine Festplatten kennt. <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="😕"
    /> Und wenn ich nach x86 gehe, würde eine race condition oder so per se nichts schädliches anrichten.</p>
<p>---</p>
<p>Der Unterschied ist hier vorzufinden:</p>
<blockquote>
<p>If <em>A</em> is not sequenced before <em>B</em> and <em>B</em> is not sequenced before <em>A</em>, then <em>A</em> and <em>B</em> are unsequenced. [ <em>Note</em>: <strong>The execution of unsequenced evaluations can overlap.</strong> — <em>end note</em> ]<br />
Evaluations <em>A</em> and <em>B</em> are indeterminately sequenced when either <em>A</em> is sequenced before <em>B</em> or <em>B</em> is sequenced before <em>A</em>, but it is unspecified which. [ <em>Note</em>: <strong>Indeterminately sequenced evaluations cannot overlap, but either could be executed first.</strong> — <em>end note</em> ]</p>
</blockquote>
<p>I.e. im Fall <code>n + n++</code> ist es sogar möglich dass, wie am Anfang des zitierten Paragraphen auch impliziert*, beide Ausdrücke gleichzeitig ausgewertet werden. Das würde zu einer race condition führen. Ich nehme an dass, da die Implementierungen nicht dazu verdonnert werden sollten, derartiges zu behandeln oder ihre Optimierung anzupassen, man es als undefiniert abgestempelt hat.</p>
<p>(Muss es aber nicht sein, ein relativ neues Paper von Sutter und Stroustrup beschäftigt sich damit die Auswertungsreihenfolge festzulegen.)</p>
<p>Wie auch immer, für Funktionen ändert sich die Lage wie folgt:</p>
<p>§1.9/15 schrieb:</p>
<blockquote>
<p>Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function.</p>
</blockquote>
<p>I.e., die Ausführung des Inkrements innerhalb der Funktion ist indeterminately sequenced im Vergleich zum Zugriff auf den Wert der Variable. Wir haben also lediglich unspezifiziertes Verhalten, das in keinster weise konsistent sein braucht.</p>
<p>* Impliziert wird, dass für den <code>+</code> -Operator keine Ausnahme bezüglich</p>
<blockquote>
<p>Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced. [ <em>Note</em>: In an expression that is evaluated more than once during the execution of a program, unsequenced and indeterminately sequenced evaluations of its subexpressions need not be performed consistently in different evaluations. — <em>end note</em> ]</p>
</blockquote>
<p>besteht.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2456452</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2456452</guid><dc:creator><![CDATA[Columbo]]></dc:creator><pubDate>Thu, 11 Jun 2015 19:05:21 GMT</pubDate></item></channel></rss>