jenz: Wie komisch ist das denn?

Hi,
ich bin gerade dabei, ein wenig c++ zu lernen, habe aber ein sehr komisches Problem bei der Funktionsübergabe eines long double. Am besten gebe ich einfach mal die source her, ich habe mit g++ kompiliert.

----------------------------------------------------
#include <iostream>
using namespace std;

long double summe(long double s, long double *f, int n)
{
        if(n>1)
        {
                summe(s+*f++, f, n-1);
        }
        else
        {
                cout << "Berechneter Wert ist " << s << "\n" << s << ", jetzt erfolgt die funktionsübergabe: \n";
                return s;
        }
}

long double summe(long double *f, int n)
{
        return summe(0, f, n);
}

int main(void)
{
        long double x[10];
        for(int i=0;i<10;i++)
        {
                cout << "Bitte Wert " << i+1 << " eingeben \n";
                cin >> x[i];
        }
        cout << "Summe aller Zahlen ist " << summe(x,10) << "\n";
}
---------------------------------------

Kann sich jemand erklären, warum ich im cout-Aufruf der summe-Funktion noch das richtige Ergebnis bekomme, sobald ich aber dieselbe Variable an main übergebe, immer totaler Mist rauskommt?

Schöne Grüße,
Jens

  1. Hi,

    zuerstmal solltest du einen geeigneten Titel angeben.

    #include <iostream>
    using namespace std;

    long double summe(long double s, long double *f, int n)
    {
            if(n>1)

    Du addierst einmal zu wenig. Es muss n>0 heißen.

    {
                    summe(s+*f++, f, n-1);

    Weißt du was du hier machst? Du solltest hier mindestens klammern verwenden, damit das etwas nachvollziehbar wird. Gerade als Anfänger solltest du solche Konstrukte vermeiden.
    Ich wüsste jetzt nicht auswendig was *f++ macht. Ob es erst dereferenziert (*) oder erst incrementiert (++).
    Aber das ist nicht der Fehler.

    }
            else
            {
                    cout << "Berechneter Wert ist " << s << "\n" << s << ", jetzt erfolgt die funktionsübergabe: \n";
                    return s;
            }
    }

    Der eigentliche Fehler ist, dass du eine long double Funktion hast, die nur ein long double zurück gibt, wenn die if-bedingung nicht erfüllt ist, anderenfalls gibt sie anscheinend 0 zurück.

    Wenn du den Schalter -Wall beim g++ verwendest bekommst du eine Warnung:
    "Warnung: Kontrollfluss erreicht Ende einer Nicht-void-Funktion"

    long double summe(long double *f, int n)
    {
            return summe(0, f, n);
    }

    int main(void)
    {
            long double x[10];
            for(int i=0;i<10;i++)
            {
                    cout << "Bitte Wert " << i+1 << " eingeben \n";
                    cin >> x[i];
            }
            cout << "Summe aller Zahlen ist " << summe(x,10) << "\n";

    Wegen der Wartungsfreundlichkeit könntest du statt 10 eine Konstante verwenden.

    }

    Kann sich jemand erklären, warum ich im cout-Aufruf der summe-Funktion noch das richtige Ergebnis bekomme, sobald ich aber dieselbe Variable an main übergebe, immer totaler Mist rauskommt?

    Das richtige Ergebnis war es nicht. Die letzte Zahl wird nicht dazugezählt, weil die if-Bedingung falsch ist.

    Außerdem solltest du dir nochmal klarmachen, wie eine Rekursion genau funktioniert. Die Ausgabe, die korrekt war erfolgte beim letzten (10.) Aufruf der Funktion Summe. Dieser wurde dann an die als vorletztes aufgerufene Funktion (9.) weitergegeben, dort aber nichtmehr verarbeitet.

    mfG,
    steckl

  2. Hello,

    long double summe(long double s, long double *f, int n)
    {
            if(n>1)
            {
                    summe(s+*f++, f, n-1);
            }
            else
            {
                    cout << "Berechneter Wert ist " << s << "\n" << s << ", jetzt erfolgt die funktionsübergabe: \n";

    //                 return s;

    }

    return s;

    }

    Also meiner Meinung nach gehört das "return s" nach unten
    C++ ist zwar recht merkwürdig gestrickt, aber warum sollte die Einstirgsstuf der Rekursion etwas zurückgeben, wenn Du es ihr nicht sagst?

    Dein Ergebnis steht nämlich in der globalen Variable s und

    cout << "Summe aller Zahlen ist " << summe(x,10) << "\n";
                                             -----------

    hat überhaupt keinen Rückgabewert, da "summe(0, f, n)" keinen hat.

    Das ist ein Standardproblem bei unstrukturierter Programmierung.
    Hättest Du Nassi-Shniderman beherzigt, dann wäre es Dir aufgefallen.

    Liebe Grüße aus Syburg bei Dortmund

    Tom vom Berg

    --
    Nur selber lernen macht schlau
    http://bergpost.annerschbarrich.de
    1. Hello,

      C++ ist zwar recht merkwürdig gestrickt, aber warum sollte die Einstirgsstuf der Rekursion etwas zurückgeben, wenn Du es ihr nicht sagst?

      sollte heißen:

      C++ ist zwar recht merkwürdig gestrickt, aber warum sollte die Einstiegsstufe der Rekursion etwas zurückgeben, wenn Du es ihr nicht sagst?

      Wenn es jetzt wieder nicht stimmt, ist FF schuld.

      Liebe Grüße aus Syburg bei Dortmund

      Tom vom Berg

      --
      Nur selber lernen macht schlau
      http://bergpost.annerschbarrich.de
    2. Hi,

      Hello,

      long double summe(long double s, long double *f, int n)
      {
              if(n>1)
              {
                      summe(s+*f++, f, n-1);
              }
              else
              {
                      cout << "Berechneter Wert ist " << s << "\n" << s << ", jetzt erfolgt die funktionsübergabe: \n";
      //                 return s;
              }
                return s;
      }

      Also meiner Meinung nach gehört das "return s" nach unten

      Nö. So bringt das gar nichts. Wenn n > 1 ist, würde zwar summe aufgerufen, der zurückgegebene Wert aber nach wie vor ignoriert, und es würde der Parameter s so zurückgegeben, wie er an die Funktion übergeben wurde.

      Es muß im Falle von n > 1 das Ergebnis des rekursiven Aufrufs zurückgegeben werden.

      cu,
      Andreas

      --
      Warum nennt sich Andreas hier MudGuard?
      O o ostern ...
      Fachfragen unaufgefordert per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.
      1. Hello,

        long double summe(long double s, long double *f, int n)
        {
                if(n>1)
                {
                        summe(s+*f++, f, n-1);
                }
                else
                {
                        cout << "Berechneter Wert ist " << s << "\n" << s << ", jetzt erfolgt die funktionsübergabe: \n";
        //                 return s;
                }
                  return s;
        }

        Also meiner Meinung nach gehört das "return s" nach unten

        Nö. So bringt das gar nichts. Wenn n > 1 ist, würde zwar summe aufgerufen, der zurückgegebene Wert aber nach wie vor ignoriert, und es würde der Parameter s so zurückgegeben, wie er an die Funktion übergeben wurde.

        Es muß im Falle von n > 1 das Ergebnis des rekursiven Aufrufs zurückgegeben werden.

        Und wo steht das Ergebnis?
        MMn in s!
        Also würde ein "return s" eine lokale Kopie des Referenzparemeters s erzeugen und den dann ins Stackframe zurückschreiben. Schreibt man nichts in Stackframe zurück, bleibt dder entsprechende Platz mit Zufallswert gefüllt.

        Dass das Programm ungeschickt gesschrieben ist, muss man nicht extra erwähnen.

        Die Rückgabe findet beim Rückweg sooft statt, wie Rekursionsstufen durchlaufen wurden. Entsprechend viele Stackframes wurden auch angelegt und müssen beim Rücklauf wieder ausfgelöst werden.
        Es wird mit "return s" jedes Mal derselbe Referenzparemter abgefragt und in 10 verschiedene Stackframes zurückgeschrieben.

        Liebe Grüße aus Syburg bei Dortmund

        Tom vom Berg

        --
        Nur selber lernen macht schlau
        http://bergpost.annerschbarrich.de
        1. Hi,

          Hello,

          long double summe(long double s, long double *f, int n)
          {
                  if(n>1)
                  {
                          summe(s+*f++, f, n-1);
                  }
                  else
                  {
                          cout << "Berechneter Wert ist " << s << "\n" << s << ", jetzt erfolgt die funktionsübergabe: \n";
          //                 return s;
                  }
                    return s;
          }

          Also meiner Meinung nach gehört das "return s" nach unten

          Nö. So bringt das gar nichts. Wenn n > 1 ist, würde zwar summe aufgerufen, der zurückgegebene Wert aber nach wie vor ignoriert, und es würde der Parameter s so zurückgegeben, wie er an die Funktion übergeben wurde.

          Es muß im Falle von n > 1 das Ergebnis des rekursiven Aufrufs zurückgegeben werden.

          Und wo steht das Ergebnis?
          MMn in s!

          Nur im Fall n <= 1.

          Im Fall n > 1 wird das Ergebnis des rekursiven Aufrufs nicht aufgefangen.

          cu,
          Andreas

          --
          Warum nennt sich Andreas hier MudGuard?
          O o ostern ...
          Fachfragen unaufgefordert per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.
  3. Hallo,

    Hi,
    ich bin gerade dabei, ein wenig c++ zu lernen, habe aber ein sehr komisches Problem bei der Funktionsübergabe eines long double. Am besten gebe ich einfach mal die source her, ich habe mit g++ kompiliert.


    #include <iostream>
    using namespace std;

    long double summe(long double s, long double *f, int n)
    {
            if(n>1)

    Hier muss - wie schon gesagt wurde - if(n>0) stehen.

    {
                    summe(s+*f++, f, n-1);

    Zunächst: Wenn das Programm hierhin kommt, wird die Funktion beendet,
    ohne einen return-Wert zurückzugeben.
    Weiterhin scheint das mit dem Zeiger-Inkrement nicht zu
    funktionieren, da ja an die tiefere Rekursionsebene
    immer wieder f übergeben wird. In meinem Testlauf wurde
    10 mal das erste Listenelement addiert.

    Funktionieren tut dagegen

    return summe(s+f[n-1], f, n-1);

    [...]

    Also im Ganzen nochmal meine geänderte Summenfunktion:

      
    long double summe(long double s, long double *f, int n)  
    {  
            if(n>0)  
            {  
                    return summe(s+f[n-1], f, n-1);  
            }  
            else  
            {  
                    cout << "Berechneter Wert ist " << s << "\n" << s << ", jetzt erfolgt die funktionsuebergabe: \n";  
                    return s;  
            }  
    }  
    
    

    Das ganze ist übrigens in Form einer rekursiven Funktion
    reichlich kompliziert und tendeziell ressourcenfressend.
    Einfacher wäre es z. B. so:

      
    long double summe2(long double *f, int n) {  
      long double s(0);  
      do { s+= f[n---1]; } while(n);  
      return s;  
    }  
    
    

    Wer will, kann s+= f[(n--)-1]; auch mit Klammerung schreiben ;-)

    MfG

    Andreas

  4. Danke an alle, jetzt funktioniert es! Wie dumm von mir.
    Ja, natürlich ist die Vorgehensweise ein wenig umständlich, aber ich wollte gleich mal das mit dem Überladen ausprobieren, und die Idee mit den "Hilfsfunktionen"(also in dem Fall die mit 3Variablen überladene summe(..) gefällt mir prinzipiell sehr gut.

    Das Zeigerinkrement funktioniert bei mir aber wunderbar, Andreas. Du hast wohl übersehen, das nicht das gleich f übergeben wird. Ich sollte wohl statt

    if(n>0)
    {
         summe(s+*f++, f, n-1);
    }

    lieber

    if(n>0)
    {
         summe(s+*f, ++f, n-1);
    }

    schreiben, das sieht einfach mehr nach dem aus, was getan werden soll ;-).

    Nassi-Shniderman werde ich mir gleich mal zu Gemüte führen.

    Herzlich,
    Jens

    1. Das Zeigerinkrement funktioniert bei mir aber wunderbar, Andreas. Du hast wohl übersehen, das nicht das gleich f übergeben wird. Ich sollte wohl statt

      if(n>0)
      {
           summe(s+*f++, f, n-1);
      }

      lieber

      if(n>0)
      {
           summe(s+*f, ++f, n-1);
      }

      schreiben, das sieht einfach mehr nach dem aus, was getan werden soll ;-).

      Hier fehlt jeweils immer noch das return.

      Bei mir funktioniert das mit der Pointerübergabe nicht wie
      beabsichtigt. Da dies ein lehrreicher Punkt ist, habe
      ich Dein Programm mal wie folgt umgeschrieben:

        
      #include <iostream>  
      using namespace std;  
        
      long double summe1(long double s, long double *f, int n)  
      {  
              if(n>0)  
              {  
                      return summe1(s+*f++, f, n-1);  
              }  
              else  
              {  
                      return s;  
              }  
      }  
      long double summe2(long double *f, long double s, int n)  
      {  
              if(n>0)  
              {  
                      return summe2(f, s+*f++, n-1);  
              }  
              else  
              {  
                      return s;  
              }  
      }  
        
      int main(void)  
      {  
              long double x[10];  
              for(int i=0;i<10;i++)  
              {  
                      cout << "Bitte Wert " << i+1 << " eingeben \n";  
                      cin >> x[i];  
              }  
              cout << "Summenfuntion 1\n"  << "Summe aller Zahlen ist " << summe1(0, x, 10)  << "\n\n";  
              cout << "Summenfunktion 2\n" << "Summe aller Zahlen ist " << summe2(x, 0, 10) << "\n\n";  
      }  
      
      

      Mit diesem Programm erhalte ich folgende
      Ausgabe auf der Konsole:

        
      ~$ g++ main.cxx  
      ~$ ./a.out  
      Bitte Wert 1 eingeben  
      1  
      Bitte Wert 2 eingeben  
      2  
      Bitte Wert 3 eingeben  
      3  
      Bitte Wert 4 eingeben  
      4  
      Bitte Wert 5 eingeben  
      5  
      Bitte Wert 6 eingeben  
      6  
      Bitte Wert 7 eingeben  
      7  
      Bitte Wert 8 eingeben  
      8  
      Bitte Wert 9 eingeben  
      9  
      Bitte Wert 10 eingeben  
      10  
      Summenfuntion 1  
      Summe aller Zahlen ist 10  
        
      Summenfunktion 2  
      Summe aller Zahlen ist 55  
      
      

      Der Unterschied kommt daher zustande, dass
      bei meiner g++ - Installation Funktionsparameter
      in der Reihenfolge von rechts nach links
      ausgewertet werden. Daher wird in summe1 immer
      erst der Zeiger f übergeben, bevor der
      Ausdruck *f++ ausgewertet wird.

      In summe2 habe ich die Reihenfolge
      der Parameter vertauscht. Ich meine, mich
      erinnern zu können, dass die Reihenfolge
      der Funktionsparameter-Auswertung in C nicht
      strikt definiert ist. Es könnte also sein, dass
      der Output bei Deinem Compiler genau
      umgekehrt ausfällt.

      MfG

      Andreas

      1. ...

        Der Unterschied kommt daher zustande, dass
        bei meiner g++ - Installation Funktionsparameter
        in der Reihenfolge von rechts nach links
        ausgewertet werden. Daher wird in summe1 immer
        erst der Zeiger f übergeben, bevor der
        Ausdruck *f++ ausgewertet wird.

        In summe2 habe ich die Reihenfolge
        der Parameter vertauscht. Ich meine, mich
        erinnern zu können, dass die Reihenfolge
        der Funktionsparameter-Auswertung in C nicht
        strikt definiert ist. Es könnte also sein, dass
        der Output bei Deinem Compiler genau
        umgekehrt ausfällt.

        ...

        Ein sehr guter Beitrag, herzlichen Dank nochmal! Dann sollte man wohl zugunsten der Portabilität aber weder Deine noch meine Variante wählen, sondern einfach kurz vorher eine neue Variable schreiben (wobei das bei größeren Inhalten zulasten der Performance geht, natürlich).

        Schöne Grüße,
        Jens

        1. Hi,

          ...

          Der Unterschied kommt daher zustande, dass
          bei meiner g++ - Installation Funktionsparameter
          in der Reihenfolge von rechts nach links
          ausgewertet werden. Daher wird in summe1 immer
          erst der Zeiger f übergeben, bevor der
          Ausdruck *f++ ausgewertet wird.

          Ein sehr guter Beitrag, herzlichen Dank nochmal!

          Ja, das war mir auch neu, dass es auch Compiler gibt, die die Argumente andersrum abarbeiten. Ich habs bis jetzt immer nur von rechts nach links gesehen. Außerdem verwende ich ja auch den g++ (Version 4.2).
          Finde ich ziemlich erstaunlich.
          Wäre interessant, ob dieses Verhalten wirklich so definiert ist, oder ob sich da ein Compiler einfach nicht an die Standards hält.

          Dann sollte man wohl zugunsten der Portabilität aber weder Deine noch meine Variante wählen, sondern einfach kurz vorher eine neue Variable schreiben (wobei das bei größeren Inhalten zulasten der Performance geht, natürlich).

          Oder einfach f+1 statt ++f verwenden, dann spart man sich auch die zusätzliche Variable.

          mfG,
          steckl

          1. hi!

            Ja, das war mir auch neu, dass es auch Compiler gibt, die die Argumente andersrum abarbeiten. Ich habs bis jetzt immer nur von rechts nach links gesehen. Außerdem verwende ich ja auch den g++ (Version 4.2).
            Finde ich ziemlich erstaunlich.
            Wäre interessant, ob dieses Verhalten wirklich so definiert ist, oder ob sich da ein Compiler einfach nicht an die Standards hält.

            Das duerfte dem C++-Standard ziemlich egal sein, welche Calling convention
            der Compiler verwendet. Das kann man bei einigen Compilern sogar von
            Funktion zu Funktion aendern.

            Siehe auch dazu http://en.wikipedia.org/wiki/X86_calling_conventions.

            bye, Frank!

            --
            Never argue with an idiot. He will lower you to his level and then
            beat you with experience.
        2. Dann sollte man wohl zugunsten der Portabilität aber weder Deine noch meine Variante wählen, sondern einfach kurz vorher eine neue Variable schreiben (wobei das bei größeren Inhalten zulasten der Performance geht, natürlich).

          Eben nochmal getestet: Mit Microsoft VC++ 6 kommt bei
          beiden Varianten das falsche Ergebnis 10 heraus (hm...).
          Anscheinend gibt es hinsichtlich der Interpretation
          der Auswerte-Reihenfolge von Funktionsparametern
          noch weiteren Spielraum.

          Warum muss es überhaupt eine rekursive Funktion sein? Wenn
          Du ein Array von 100000 Elementen summierst, werden
          100000 mal je drei Funktionsparameter, sowie die Rücksprungadresse
          auf dem Stack abgelegt, der einem bei dieser Gelegenheit
          leicht mal um die Ohren fliegen kann.

          Ein einfacher Dreizeiler mit einer Schleife
          (wie in meinem anderen Post bereits gezeigt)
          sollte es in jedem Fall tun.

          Falls es partout keine Schleife sondern
          eine rekursive Funktion sein muss (Aufgabenstellung?!),
          würde ich die Anzahl der Parameter möglichst gering halten,
          etwa so:

            
          long double summe3(long double *f, int n) {  
           if(n>1) return f[n-1]+summe3(f, n-1);  
           else return *f;  
          }  
          
          

          Diese Funktion sollte mit allen C++ - Compilern
          anstandslos funktionieren.

          MfG

          Andreas

          1. Eben nochmal getestet: Mit Microsoft VC++ 6 kommt bei
            beiden Varianten das falsche Ergebnis 10 heraus (hm...).
            Anscheinend gibt es hinsichtlich der Interpretation
            der Auswerte-Reihenfolge von Funktionsparametern
            noch weiteren Spielraum.

            g++ (GCC) 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)
            Das übrigens meine Version. Das dann schon 4.2 es gerade anders machen soll, ist auch seltsam. Irgendwelche Schalter hab ich nicht verwendet.

            Warum muss es überhaupt eine rekursive Funktion sein?

            http://forum.de.selfhtml.org/?t=176801&m=1163923 :-)
            Habe als Nebenfach Informatik und bis jetzt die Programmiersprachen-1 Vorlesung gehört, wo wir anhand Scheme, einer sehr beschränkten Sprache so einige strukturelle Möglichkeiten besprochen haben (auch eine Pseudo-OO programmiert haben, Scheme ist ja nicht nativ eine OOP).
            Dass das laufzeittechnisch erstmal nicht so günstig ist, ist schon klar, aber es hat eine gewisse Eleganz, finde ich, und ich bin ja noch am Üben.

            Wenn Du ein Array von 100000 Elementen summierst, werden
            100000 mal je drei Funktionsparameter, sowie die Rücksprungadresse
            auf dem Stack abgelegt, der einem bei dieser Gelegenheit
            leicht mal um die Ohren fliegen kann.

            Stack ist sowas wie der "Hafen", von dem aus der Funktionaufruf abgeschickt wird und damit der Platz, auf dem die Antwort wieder ankommen kann, richtig?

            Ein einfacher Dreizeiler mit einer Schleife
            (wie in meinem anderen Post bereits gezeigt)
            sollte es in jedem Fall tun.

            Falls es partout keine Schleife sondern
            eine rekursive Funktion sein muss (Aufgabenstellung?!),
            würde ich die Anzahl der Parameter möglichst gering halten,
            etwa so:

            long double summe3(long double *f, int n) {
            if(n>1) return f[n-1]+summe3(f, n-1);
            else return *f;
            }

              
            Aha, sehr gut.
            
            1. Hi,

              Eben nochmal getestet: Mit Microsoft VC++ 6 kommt bei
              beiden Varianten das falsche Ergebnis 10 heraus (hm...).
              Anscheinend gibt es hinsichtlich der Interpretation
              der Auswerte-Reihenfolge von Funktionsparametern
              noch weiteren Spielraum.

              g++ (GCC) 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)
              Das übrigens meine Version. Das dann schon 4.2 es gerade anders machen soll, ist auch seltsam. Irgendwelche Schalter hab ich nicht verwendet.

              Bei 4.2 ist es bei mir wie bei dir.
              Die genaue Version bei mir ist:
              gcc-Version 4.2.3 (Ubuntu 4.2.3-2ubuntu7)

              Falls es jemanden interessiert, hier sind ein paar Beispiele aus meiner Vorlesung, bei denen tatsächlich nicht definiert ist, wie sich der Compiler verhalten muss:
              i = 2;
              k = (i++) + i;         // k=4 oder k=5 ?
              s[i] = i++;            // s[i]=i oder s[i+1]=i ?
              s[i++] = i;            // s[i]=i oder s[i]=i+1 ?

              Stack ist sowas wie der "Hafen", von dem aus der Funktionaufruf abgeschickt wird und damit der Platz, auf dem die Antwort wieder ankommen kann, richtig?

              Der Stack ist ein Stapelspeicher, auf dem z.B. beim Aufruf einer Funktion Rücksprungadressen, Parameter und lokale Variablen abgespeichert werden.
              Genaueres unter Stack

              mfG,
              steckl

      2. Nachtrag:
        Mit g++ 4.1.2 (unter Debian Etch) kommt
        bei mir bei beiden Summenfunktionen jetzt das
        korrekte Ergebnis 55. G++ 4.1.2 arbeitet also
        die Funktionsparameter von links nach rechts durch.

        Bei g++ 3.3.5 (Debian Sarge), welches ich in
        obigem Posting hatte, funktionierte das
        anscheinend umgekehrt.

        C++ ist eine tolle Sprache ... ;)

        Viele Grüße

        Andreas

    2. Hi,

      lieber

      if(n>0)
      {
           summe(s+*f, ++f, n-1);
      }

      schreiben, das sieht einfach mehr nach dem aus, was getan werden soll ;-).

      Mir würde am besten
      summe(s+(*f),f+1,n-1)
      gefallen.

      mfG,
      steckl

      1. Mir würde am besten
        summe(s+(*f),f+1,n-1)
        gefallen.

        Hm, ich wusste noch gar nicht, dass das auch funktioniert. Dachte, diese typabhängige Zeigerinkrementierung wäre nur für In-/Dekrementierungsoperatoren gemacht, aber auch +x / -x wird seine Anwendung finden. Ich persönlich bleibe dann in diesem Fall trotzdem bei ++/--, das soll man ja auch bei gewöhnlichen Zählern in Schleifen etc. verwenden, zugunsten der Verständlichkeit des Codes.

        Welchen Knopf drückt Ihr eigentlich für den schönen bunten Code? Java, Perl?

        Gruß,
        Jens

        1. Hi,

          Mir würde am besten
          summe(s+(*f),f+1,n-1)
          gefallen.

          Hm, ich wusste noch gar nicht, dass das auch funktioniert. Dachte, diese typabhängige Zeigerinkrementierung wäre nur für In-/Dekrementierungsoperatoren gemacht, aber auch +x / -x wird seine Anwendung finden.

          Das ist ja auch keine Zeigerinkrementierung.
          Du übergibst ja auch nicht --n, sondern n-1; das ist genau das gleiche.
          Es wird nur die Adresse des nächsten Elements an die Funktion übergeben. In der rufenden Funktion bleibt der Zeiger unverändert.

          Außerdem kann man so auch auf Elemente weiter hinten im Feld zugreifen.

          Anschauungsbeispiel:

          f == &f[0] == f+0
          &f[5] == f+5
          f[0] == *(f+0)    // +0 kann natürlich auch weggelassen werden
          f[5] == *(f+5)
          //jetz wirds abstrakt und theoretisch (das wirst du hoffentlich nie so vorfinden), aber es geht noch weiter:
          == *(1+f) == 1[f]

          Die Schreibweise mit f+1 hat den Vorteil, dass der ursprüngliche Pointer erhalten bleibt, was auch das Problem, das du hattest mit der Abarbeitungsreihenfolge der Argumente, beseitigt.

          Das kannst du z.B. bei der dynamischen Speicherverwaltung verwenden, bei der du sonst die Anfangsadresse in einer Extra-Variablen speichern müsstest, damit du den Speicher später wieder freigeben kannst.

          Ich persönlich bleibe dann in diesem Fall trotzdem bei ++/--, das soll man ja auch bei gewöhnlichen Zählern in Schleifen etc. verwenden, zugunsten der Verständlichkeit des Codes.

          Naja, hier sehe ich keinen Sinn, warum du den Zeiger noch incrementieren solltest.

          Welchen Knopf drückt Ihr eigentlich für den schönen bunten Code? Java, Perl?

          Ich mach das immer manuell: (code lang=C++)bla bla bla (/code)
          nur mit eckigen statt runden klammern. Einen extra-Knopf gibt es dafür nicht, weil C++ hier vielleicht zu selten vorkommt und ausserdem in der Webprogrammierung ja nicht so häufig verwendet wird.

          mfG,
          steckl

          1. Anschauungsbeispiel:

            f == &f[0] == f+0
            &f[5] == f+5
            f[0] == *(f+0)    // +0 kann natürlich auch weggelassen werden
            f[5] == *(f+5)
            //jetz wirds abstrakt und theoretisch (das wirst du hoffentlich nie so vorfinden), aber es geht noch weiter:
            == *(1+f) == 1[f]

            Hm, coole Sache. Ich gehe mal davon aus, dass bei Dir auch 1[f]==f[1]? Ist das nur dadurch begründet, dass 1[f]=1+f <font color="red">!==!</font> f+1==f[1], um wenigstens die Kommutativität des "+"-Operators zu wahren, oder steckt noch eine andere teuflische Logik dahinter? :-)

            Die Schreibweise mit f+1 hat den Vorteil, dass der ursprüngliche Pointer erhalten bleibt, was auch das Problem, das du hattest mit der Abarbeitungsreihenfolge der Argumente, beseitigt.

            Okay, aber nur aufgrund dieser Tatsache will ich Dir jetzt Recht zugestehen :-). Ansonsten finde ich ++ einfach eleganter und korrekterer.

            Ich habe immer noch nicht ganz verstanden, wann man jetzt Variablen var durch "&var" und wann als "*var" deklarieren muss!? Mit google ist ohne Kenntnis der genaueren Suchsyntax ja nichts zu machen bei solchen Zeichen, finde leider nix.

            Ich mach das immer manuell: (code lang=C++)bla bla bla (/code)
            nur mit eckigen statt runden klammern. Einen extra-Knopf gibt es dafür nicht, weil C++ hier vielleicht zu selten vorkommt und ausserdem in der Webprogrammierung ja nicht so häufig verwendet wird.

            Sehr gut. Nur für den Fall, dass solche Themen hier im Sinne der Übersichtlichkeit des Forums nicht 100% erwünscht sind (bin hier trotzdem gerne, weil die Leute immer so schön diskutieren :), und Du ja C++mäßig sehr bewandert scheinst, wo würdest Du mich hinschicken?

            Gruss,
            Jens

            1. Hm, coole Sache. Ich gehe mal davon aus, dass bei Dir auch 1[f]==f[1]? Ist das nur dadurch begründet, dass 1[f]=1+f <font color="red">!==!</font> f+1==f[1], um wenigstens die Kommutativität des "+"-Operators zu wahren, oder steckt noch eine andere teuflische Logik dahinter? :-)

              Mist, das mit dem HTML-Knopf habe ich missverstanden. Und das bei DIESER Domain^^.

            2. Hi,

              Anschauungsbeispiel:

              f == &f[0] == f+0
              &f[5] == f+5
              f[0] == *(f+0)    // +0 kann natürlich auch weggelassen werden
              f[5] == *(f+5)
              //jetz wirds abstrakt und theoretisch (das wirst du hoffentlich nie so vorfinden), aber es geht noch weiter:
              == *(1+f) == 1[f]

              Hm, coole Sache. Ich gehe mal davon aus, dass bei Dir auch 1[f]==f[1]? Ist das nur dadurch begründet, dass 1[f]=1+f <font color="red">!==!</font> f+1==f[1], um wenigstens die Kommutativität des "+"-Operators zu wahren, oder steckt noch eine andere teuflische Logik dahinter? :-)

              Sorry, da hab ich die 1 mit der 5 vertauscht, also so muss es korrekt heißen:
              f[5] == *(f+5) == *(5+f) == 5[f]
              Aber ich kann mir aber keinen Fall vorstellen, wo es Sinn machen würde die letzten beiden Schreibweisen zu verwenden.
              Es funktioniert, damit die Rechengesetze alle eingehalten werden. "f+5" ist ja das gleiche wie "5+f". Und f[5] ist laut Definition das gleiche wie *(f+5). Und damit ist auch "*(5+f)" das gleiche wie 5[f].

              Die Schreibweise mit f+1 hat den Vorteil, dass der ursprüngliche Pointer erhalten bleibt, was auch das Problem, das du hattest mit der Abarbeitungsreihenfolge der Argumente, beseitigt.

              Okay, aber nur aufgrund dieser Tatsache will ich Dir jetzt Recht zugestehen :-). Ansonsten finde ich ++ einfach eleganter und korrekterer.

              Bis auf das Problem mit der Abarbeitungsreihenfolge ist es wohl hier echt ziemlich egal. Ich kann mir auch nicht vorstellen, dass eine der beiden Methoden viel Performanter ist als die andere.

              Ich habe immer noch nicht ganz verstanden, wann man jetzt Variablen var durch "&var" und wann als "*var" deklarieren muss!? Mit google ist ohne Kenntnis der genaueren Suchsyntax ja nichts zu machen bei solchen Zeichen, finde leider nix.

              Das ist ein äußerst wichtiges Thema in C/C++ und sollte daher eigentlich in jedem guten Tutorial/Buch ausführlich erklärt werden.

              Aber hier mal ein kurzer Erklärungsversuch:
              &var brauchst du bei der Deklaration (Anlegen von Variablen) nie, erst bei der Initialisierung (Wertzuweisung).

              int var1 = 4;     // legt eine normale Variable an und initialisiert sie mit 4  
              int* ptr = &var1; // legt einen int-Pointer (=Zeiger) an und initialisiert ihn mit der Adresse von var1  
              var1 = 2;         // setzt var1 auf 2  
              *ptr = 5;         // setzt die Variable, auf die ptr zeigt (in diesem Fall var1) auf 5  
              // hier hat var1 den Wert 5 (und nicht 2)
              

              Ich mach das immer manuell: (code lang=C++)bla bla bla (/code)
              nur mit eckigen statt runden klammern. Einen extra-Knopf gibt es dafür nicht, weil C++ hier vielleicht zu selten vorkommt und ausserdem in der Webprogrammierung ja nicht so häufig verwendet wird.

              Sehr gut. Nur für den Fall, dass solche Themen hier im Sinne der Übersichtlichkeit des Forums nicht 100% erwünscht sind (bin hier trotzdem gerne, weil die Leute immer so schön diskutieren :),

              Es sollte eigentlich niemanden stören, wenn du hier postest, solange du dich an die Forumsregeln hältst.

              und Du ja C++mäßig sehr bewandert scheinst, wo würdest Du mich hinschicken?

              Ich hab noch nie nach einem guten C++-Forum gesucht. Da ich Informatik studiere kann ich mich mit Problemen normalerweise an meine Kommilitonen oder Dozenten wenden. Wenn mal gerade keiner in der Nähe ist frage ich auch hier nach. Hier erhält man auch immer schnell Hilfe.
              Außerdem beherrsche ich C++ auch noch nicht so richtig, sondern nur die Grundlagen.

              mfG,
              steckl

              1. hi!

                Ich habe immer noch nicht ganz verstanden, wann man jetzt Variablen
                var durch "&var" und wann als "*var" deklarieren muss!? Mit google
                ist ohne Kenntnis der genaueren Suchsyntax ja nichts zu machen bei
                solchen Zeichen, finde leider nix.
                &var brauchst du bei der Deklaration (Anlegen von Variablen) nie, erst
                bei der Initialisierung (Wertzuweisung).

                Das stimmt so aber auch nicht. Wenn man bei der Deklaration & verwendet,
                erhaelt man eine Referenz. Referenzen sind sowas aehnliches wie Pointer; man
                kann damit einer Variable einen zweiten Namen geben, allerdings laesst sich
                die Referenz danach nicht mehr umbiegen und die Pointer-Zugriffssyntax
                faellt weg.

                  
                int a = 1;  
                int& b = a;  
                
                

                b ist jetzt eine Referenz auf a. Das heisst, auf den Wert, der dahinter
                steckt, kannst du durch beide Variablen zugreifen:

                  
                ++a;  
                ++b;  
                
                

                Sowohl a als auch b haben jetzt den Wert 3.

                Referenzen braucht man bei normalen Variablendeklarationen relativ selten.
                Wo sie aber sehr haeufig vorkommen, ist bei der Deklaration von Parametern
                fuer Funktionen. Das ist in jedem ordentlichen C++-Buch unter dem
                Unterschied von "call by reference" und "call by value" beschrieben.
                Verwendet man "call by value", wird ein Funktionsargument kopiert, bevor
                es an die Funktion uebergeben wird, bei "call by reference" erhaelt man
                eine Referenz auf das Original-Objekt. Letzteres ist in manchen Faellen
                deutlich performanter, gerade wenn man C++-Objekte als Parameter uebergibt.

                  
                #include <iostream>  
                #include <vector>  
                  
                using namespace std;  
                  
                template <class T>  
                void a(vector<T> vec) {  
                  cout << "a: " << vec.size() << " elements" << endl;  
                  vec.clear();  
                }  
                  
                template <class T>  
                void b(vector<T>& vec) {  
                  cout << "b: " << vec.size() << " elements" << endl;  
                  vec.clear();  
                }  
                  
                int main() {  
                  vector<int> vec;  
                  vec.push_back(1);  
                  vec.push_back(2);  
                  vec.push_back(3);  
                  
                  cout << "1: " << vec.size() << " elements" << endl;  
                  a(vec);  
                  cout << "2: " << vec.size() << " elements" << endl;  
                  b(vec);  
                  cout << "3: " << vec.size() << " elements" << endl;  
                }  
                
                

                Beim Aufruf von a() wird der Vektor kopiert, bevor er im Funktionsrumpf
                verwendet wird; vec ist dort also lediglich eine lokale Variable. Bei b()
                wird der Vektor als Referenz uebergeben, d.h. wenn du den Vektor innerhalb
                von b() veraenderst, wirkt sich das auf das Objekt aus, das du ausserhalb
                der Funktion (in main()) angelegt hast.

                bye, Frank!

                --
                Never argue with an idiot. He will lower you to his level and then
                beat you with experience.
                1. int a = 1;
                  int& b = a;

                  
                  >   
                  > b ist jetzt eine Referenz auf a. Das heisst, auf den Wert, der dahinter  
                  > steckt, kannst du durch beide Variablen zugreifen:  
                  >   
                  > ~~~c++
                    
                  
                  > ++a;  
                  > ++b;  
                  > 
                  
                  

                  Sowohl a als auch b haben jetzt den Wert 3.

                  Hi,
                  macht es eigentlich einen Unterschied, ob man

                    
                  int a = 1;  
                  int& b = a;  
                  oder  
                  int a = 1;  
                  int &b = a;  
                  
                  

                  schreibt, bzw. dasselbe mit Sternchen?

                  Gruss,
                  Benno

                  1. Hello,

                    macht es eigentlich einen Unterschied, ob man

                    int a = 1;
                    int& b = a;

                    oder

                    int a = 1;
                    int &b = a;

                    
                    > schreibt, bzw. dasselbe mit Sternchen?  
                      
                    Nein, das macht keinen Unterschied.  
                    Beides weist den Compiler an, keine neue Variable anzulegen, sondern nur einen neuen Namen.  
                    Der Name wird nachher ersetzt durch eine Speicherstelle, die die Adresse der Variablen enthält.  
                      
                    Du kannst es sogar so schreiben, was aber noch verwirrender aussieht.  
                      
                        int e = 3;  
                        int & f = e;  
                      
                        cout << "e: " << e << endl;  
                        cout << "f: " << f << endl;  
                      
                      
                      
                      
                    Liebe Grüße aus Syburg bei Dortmund  
                      
                    Tom vom Berg  
                    ![](http://selfhtml.bitworks.de/Virencheck.gif)  
                      
                    
                    -- 
                    Nur selber lernen macht schlau  
                    <http://bergpost.annerschbarrich.de>
                    
            3. Hello,

              Ich habe immer noch nicht ganz verstanden, wann man jetzt Variablen var durch "&var" und wann als "*var" deklarieren muss!? Mit google ist ohne Kenntnis der genaueren Suchsyntax ja nichts zu machen bei solchen Zeichen, finde leider nix.

              Name der        ->        Inhalt der Variablen          ->       vorauf der Inhalt
                  Variablen                                                        zeigen würde

              Blickrichtung:
                                               was steht wo?
                                                 & <- -> *

              Hinter dem Namen der Varibalen versteckt sich ein Zeiger auf deren Inhalt.
              Benutzt Du den Namen, bekommst Du also immer den Inhalt angezeigt.
              (Ausnahmen in Spezialfunktionen und Methoden mal beiseite gelassen)
              Den Namen verwaltet die Entwicklungsumgebung für Dich, der ist nachher
              verschwunden. Das Programm arbeitet also mit der Adresse der Variablen.
              Dies kann allerdings auch nochmals reloziiert werden beim Laden des Programms.
              Das bedeutet so ungefähr, dass die Variable vom Loader an einen Platz gelegt wird,
              den der Loader gut findet. Er trägt dann selbstverständlich den passenden Wert für
              die Adresse der Variable ein.

              Wenn Duneinen zweiten Namen für die Varibale haben willst, dann musst Du
              Dir deren Adresse (&) besorgen und so kannst Du auch von einer zweiten
              "Namenszelle" aus auf den Speicherplatz ezigen, wo der Wert steht.

              Wenn Du nun mit einer Variablen auf einen Wert zeigen willst, dann muss die
              Variable selber ein Zeiger sein, der auf die Adresse des tatsächlichen Wertes zeigt.

              Liebe Grüße aus Syburg bei Dortmund

              Tom vom Berg

              --
              Nur selber lernen macht schlau
              http://bergpost.annerschbarrich.de
  5. Hello,

    #include <iostream>
    using namespace std;

    long double summe(long double s, long double *f, int n)
    {
            if(n>1)
            {

    summe(s+*f, f++, n-1);
    //>                 summe(s+*f++, f, n-1);
                      cout << "if n_" << n << "=" << s << endl;

    }
            else
            {
                    cout << "Berechneter Wert ist " << s << "\n" << s << ", jetzt erfolgt die funktionsübergabe: \n";

    //>                 return s;

    }

    return s;

    }

    long double summe(long double *f, int n)
    {
            return summe(0, f, n);
    }

    int main(void)
    {
            long double x[10];
            for(int i=0;i<10;i++)
            {
                    cout << "Bitte Wert " << i+1 << " eingeben \n";
                    cin >> x[i];
            }
            cout << "Summe aller Zahlen ist " << summe(x,10) << "\n";
    }

    Schau Dir diese Version nochmal an.
    Dann sollte klar werden, was passiert.
    Nun wird zwar in jeder Instanz ein Erbebis zurückgegeben, das landet aber in seinem eignen Stackframe und wird von der nächsten Instanz gar nicht abgeholt.

    Entweder, Du entscheidest Dich dafür, die Summe als Referenz zu übergeben, oder du verpasst Der Funktion "summe(long double s, long double *f, int n)" einen Rückgabewert, was ich für besser hielte.

    Liebe Grüße aus Syburg bei Dortmund

    Tom vom Berg

    --
    Nur selber lernen macht schlau
    http://bergpost.annerschbarrich.de
    1. long double summe(long double s, long double *f, int n)
      {
              if(n>1)
              {
                        summe(s+*f, f++, n-1);
      //>                 summe(s+*f++, f, n-1);
                        cout << "if n_" << n << "=" << s << endl;
              }
              else
              {
                      cout << "Berechneter Wert ist " << s << "\n" << s << ", jetzt erfolgt die funktionsübergabe: \n";
      //>                 return s;
              }
                return s;
      }

      Damit wird die Funktion erstmalig mit s=0 aufgerufen,
      innerhalb der Funktion wird s nirgendwo verändert
      (falls doch, wo bitte?), und am Ende wird s=0 wieder
      zurückgegeben. Testest Du Deine
      Code-Beispiele eigentlich vor dem Posten?

      MfG

      Andreas

      1. Hello,

        Damit wird die Funktion erstmalig mit s=0 aufgerufen,
        innerhalb der Funktion wird s nirgendwo verändert

        Das habe ich auch nicht behauptet.

        (falls doch, wo bitte?), und am Ende wird s=0 wieder
        zurückgegeben. Testest Du Deine
        Code-Beispiele eigentlich vor dem Posten?

        Dieses ja.

        Und mMn nach erfüllt es seinen Zweck.

        Ich schrieb:
        "Schau Dir diese Version nochmal an. Dann sollte klar werden, was passiert."

        Liebe Grüße aus Syburg bei Dortmund

        Tom vom Berg

        --
        Nur selber lernen macht schlau
        http://bergpost.annerschbarrich.de
  6. Hello,

    habe Dir eine Lösung gebastelt, die funktioniert.

    //============================================================================
    // Name        : Rekursion.cpp
    //============================================================================

    #include <iostream>
    using namespace std;

    //-----------------------------------------------------------------------------
    template <typename T>
    T array_sum(T sum, T* array, int count )
    {
     if (count > 0)
     {
      sum += array[count-1] + array_sum<T>(sum, array, count-1);
    //  cout << "Summe " << sum << ", count: " << count << endl;  // zur Kontrolle
     }

    return sum;
    }

    //=============================================================================
    int main(void)
    {
            long double x[10];
            for(int i=0;i<10;i++)
            {
                    cout << "Bitte Wert " << i+1 << " eingeben \n";
                    cin >> x[i];
            }

    cout << "Summe aller Zahlen ist " << array_sum<long double>(0, x, sizeof(x)/sizeof(x[0])) << endl;

    }

    Es ist notwendig, die aktuelle Summe an die Funktion zu übergeben. Wenn die Funktion eine echte Funktion ist, also einen Rückgabewert hat (hier die Summe), dann reicht es, die aktuelle Summe als Kopie an die Funktion zu übergeben. Wenn Du die neue Summe allerdings nicht im Rückgabewert haben willst, dann musst Du sie als Referenz übergeben. Dann kannst Du nachher natürlich nicht einfach das Ergebnis an den Stream übergeben, oder Du musst den <<-Operator überladen, was bei komplexen Datentypen ja ohnehin notwendig wird.

    Liebe Grüße aus Syburg bei Dortmund

    Tom vom Berg

    --
    Nur selber lernen macht schlau
    http://bergpost.annerschbarrich.de