Felder und Funktionen

Von: , Frage gestellt am Mi, 13. Dez 2000

Hi!
Hab hier mehrere kleine Probleme. Vielleicht weisz ja jemand Rat...
1. Gibt es einen Operator, mit dem ich 2 Felder (gleicher Laenge) punktweise addieren kann?
2. Wie deklariere ich Funktionen, die Felder zurueckgeben sollen?
3. Welchen Befehl musz ich verwenden, um eine Funktion dazu zu bewegen, ein Feld zurueckzugeben?
return(x); // x ist ein float-Feld
funktioniert nicht so richtig...

Danke im Voraus! MfG,


Berni

19 Antworten zu dieser Frage

  1. Antwort von nach 2 Stunden hilfreich
    Re: Felder und Funktionen

    Hallo Berni! Hi!
    Hab hier mehrere kleine Probleme. Vielleicht weisz ja jemand
    Rat...
    1. Gibt es einen Operator, mit dem ich 2 Felder (gleicher
    Laenge) punktweise addieren kann?
    In C++ geht das mit valarrays:

    #include<valarray>

    using namespace std;

    main()
    {
    valarray a1(5), a2(5), a3(5); // Array mit 5 Elementen;

    for ( int i=0; i<5; i++ )
    {
    // a1 und a2 besetzen ...
    a1[i]=i;
    a2[i]=i*2;
    }

    a3 = a1 + a2;
    }

    ..in C braucht man da 'Handarbeit'. 2. Wie deklariere ich Funktionen, die Felder zurueckgeben
    sollen?
    3. Welchen Befehl musz ich verwenden, um eine Funktion dazu zu
    bewegen, ein Feld zurueckzugeben?
    return(x); // x ist ein float-Feld
    funktioniert nicht so richtig...
    In C++ ist die Sache mit 'vector' schnell erledigt:

    #include <vector>

    using namespace std;

    vector<int> g()
    {
    vector<int> v(5);

    // besetzen ...
    // [...]

    return v;
    }

    main()
    {
    vector v(5);

    v = g();

    }


    Sollen es aber 'echte' Arrays sein, kommt man AFAIK nicht darum herum, ein Array als (konstanten) Zeiger auf das erste Element zu betrachen, und diesen dann auch in der Funktion zurückzugeben ...

    int *h()
    {
    static int i[5] = { 1, 2, 3, 4, 5 };

    return &i[0]; // <- mein VC++ ist hier ziemlich pingelig ;-)
    }

    main ()
    {
    int *a;

    a = h();

    // Zugriff dann über a[0], a[1] etc ...
    }

    Zugegeben, die Sache ist zeimlich haarig, und bestimmt fangen auch verschiedene Compiler an verschiedenen Stellen zu meckern an (z.B. hat sich mein VC++ bei der Verwendung 'int a[5];'an der Stelle 'a = h();' die Arbeit eingestellt, was doch eigentlich des guten zu viel ist? Was sagen die Experten?).

    Außerdem muß der zurückgegebene Zeiger statisch sein, da es bei dem zurückgegebenen Array um ein temporäres Objekt handelt, welches zum Zeitpunkt der Zuweisung noch korrekt im Speicher verhanden sein sollte.

    Falls es aber in der Funktion einfach darum geht, irgendwie das Array zu 'vertüdeln', finde ich es übrigens geschickter einfach die Adresse(Referenz) des Arrays in die Funktion zu übergeben, um das Array dort direkt zu ändern und um sich diesen 'static-Quatsch' zu sparen ;-)

    z.B.

    void i(int *a)
    {
    a[2]=99;
    }

    main()
    {
    a[5]= { 1, 2, 3, 4, 5 };

    i(&a[0]);
    }

    HTH,
    Jo

    • Antwort von nach 8 Stunden hilfreich
      Re^2: Felder und Funktionen

      [...] 2. Wie deklariere ich Funktionen, die Felder zurueckgeben
      sollen?
      3. Welchen Befehl musz ich verwenden, um eine Funktion dazu zu
      bewegen, ein Feld zurueckzugeben?
      return(x); // x ist ein float-Feld
      funktioniert nicht so richtig...
      [...] Sollen es aber 'echte' Arrays sein, kommt man AFAIK nicht
      darum herum, ein Array als (konstanten) Zeiger auf das erste
      Element zu betrachen, und diesen dann auch in der Funktion
      zurückzugeben ...

      int *h()
      {
      static int i[5] = { 1, 2, 3, 4, 5 };

      return &i[0]; // <- mein VC++ ist hier ziemlich pingelig
      ;-)
      }

      main ()
      {
      int *a;

      a = h();

      // Zugriff dann über a[0], a[1] etc ...
      }

      Zugegeben, die Sache ist zeimlich haarig, und bestimmt fangen
      auch verschiedene Compiler an verschiedenen Stellen zu meckern
      an (z.B. hat sich mein VC++ bei der Verwendung 'int a[5];'an
      der Stelle 'a = h();' die Arbeit eingestellt, was doch
      eigentlich des guten zu viel ist? Was sagen die Experten?).

      Außerdem muß der zurückgegebene Zeiger statisch sein, da es
      bei dem zurückgegebenen Array um ein temporäres Objekt
      handelt, welches zum Zeitpunkt der Zuweisung noch korrekt im
      Speicher verhanden sein sollte.

      Falls es aber in der Funktion einfach darum geht, irgendwie
      das Array zu 'vertüdeln', finde ich es übrigens geschickter
      einfach die Adresse(Referenz) des Arrays in die Funktion zu
      übergeben, um das Array dort direkt zu ändern und um sich
      diesen 'static-Quatsch' zu sparen ;-)

      z.B.

      void i(int *a)
      {
      a[2]=99;
      }

      main()
      {
      a[5]= { 1, 2, 3, 4, 5 };

      i(&a[0]);
      }

      HTH,
      Jo
      Hi,

      sollte auf Verwendung der STL verzichtet werden, sollten
      die Arrays dynamisch in der Funktion allociert und nach
      der erfolgten Verabeitung in der aufrufenden Funktion
      frei gegeben werden.

      Von der Verwendung der statischen Variablen rate ich
      dringend ab. Damit erreicht man nämlich, daß die betreffende
      Funktion nicht mehr reentrant wird. D.h., wird die
      Funktion erneut aufgerufen, verändert sich der Wert der
      statischen Variablen mit an die Sicherheit grenzenden
      Wahrscheinlichkeit. Die vorher zurückgegebene Referenz
      behält aber scheinbar ihre Gültigkeit bei.

      Auch wenn man die Rückgabewerte stets nach dem Aufruf
      der Funktion in eine andere Variable kopiert, handelt man
      sich an der gleichen Stelle Probleme ein, spätestens wenn
      die Funktion in einer multithreaded Umgebung eingesetzt
      wird.

      Also, folgender Vorschlag:

      // C++
      int main(int argc, char** argv)
      {
      float* f = g();
      do_something_with_f(f);
      if (f)
      delete [] f;

      return 0;
      }

      float* g()
      {
      float* array = new float[10];
      do_work(array);
      return array;
      }

      // C
      int main(int argc, char** argv)
      {
      float* f = g();
      do_something_with_f(f);
      if (f)
      free(f);
      return 0;
      }

      float* g()
      {
      float* array = (float*) malloc(10);
      do_work(array);
      return array;
      }


      Grüße,
      Lydia

      • Antwort von nach 14 Stunden hilfreich
        Re^3: Felder und Funktionen

        Hi!
        Hi! [...]
        Von der Verwendung der statischen Variablen rate ich
        dringend ab. [...]
        ACK

        Gruß,
        Jo

        • Antwort von nach 2 Tagen hilfreich
          Re^4: Felder und Funktionen

          Hi! Von der Verwendung der statischen Variablen rate ich
          dringend ab. [...]
          Werds mir merken!

          Dank euch allen!

          Berni

    • Antwort von nach einem Tag hilfreich
      Re^2: Felder und Funktionen

      Hallo Johannes,

      hier noch nachträglich die Erklärung, warum Dein Compiler
      an dieser Stelle nicht mehr will. Zugegeben, die Sache ist zeimlich haarig, und bestimmt fangen
      auch verschiedene Compiler an verschiedenen Stellen zu meckern
      an (z.B. hat sich mein VC++ bei der Verwendung 'int a[5];'an
      der Stelle 'a = h();' die Arbeit eingestellt, was doch
      eigentlich des guten zu viel ist? Was sagen die Experten?).
      Hier würde jeder Compiler aussteigen, da das hier ein
      syntaktischer Fehler ist. Mit 'int a[5];' legst Du einen
      _statischen_ Speicherbereich an. Anschließend versuchst
      Du jedoch diesem _statischen_ Speicherbereich mit
      'a = h();' _dynamisch_ einen anderen Speicherbereich
      zuzuweisen. Das ist ein Widerspruch an sich.

      Es sollte also folgenderweise heißen:
      ...
      int* a = new int[5];
      do_something(a);
      delete [] a;
      a = g();
      do_something_else(a);
      delete [] a;
      ...


      Gruß,
      Lydia

    • Antwort von nach einem Tag hilfreich
      Re^2: Felder und Funktionen

      Hi Jo :)

      Du schreibst folgenden Code:

      int *h()
      {
      static int i[5] = { 1, 2, 3, 4, 5 };
      return &i[0]; // <- mein VC++ ist hier ziemlich pingelig ;-)
      }
      

      Damit ist h eine Funktion, die einen Zeiger auf int zurückgibt. Mit anderen Worten, der Rückgabewert ist eine Speicheradresse, deren Inhalt als int-Wert interpretiert werden soll. Zugegeben, die Sache ist zeimlich haarig, und bestimmt fangen
      auch verschiedene Compiler an verschiedenen Stellen zu meckern
      an (z.B. hat sich mein VC++ bei der Verwendung 'int a[5];'an
      der Stelle 'a = h();' die Arbeit eingestellt, was doch
      eigentlich des guten zu viel ist? Was sagen die Experten?).
      Doch, das ist völlig verständlich. Mit "int a[5]" definierst du ein Feld. Das kleine "a" wird vom Compiler direkt durch die Startadresse des Feldes ersetzt. Du versuchst nun, dieser Konstanten den Rückgabewert von h() zuzuweisen. Das kann nicht klappen. Das geht nur bei einem Zeiger. Ein solcher ist nämlich eine Variable, die eine Speicheradresse enthält. Merkst du den Unterschied? In C/C++ sind Feldnamen und Zeiger eben nicht dasselbe!!! Auch wenn dies in vielen Büchern fälschlicherweise so dargestellt wird!!!

      cu Stefan.

      • Antwort von nach einem Tag hilfreich
        Re^3: Bemerkung

        Hallo Stefan,


        :    return &i[0]; // <- mein VC++ ist hier ziemlich
        


        Bei meinem VC++ funktioniert auch return i; Doch, das ist völlig verständlich. Mit "int a[5]" definierst
        du ein Feld. Das kleine "a" wird vom Compiler direkt durch die
        Startadresse des Feldes ersetzt. Du versuchst nun, dieser
        Konstanten den Rückgabewert von h() zuzuweisen. Das kann nicht
        klappen.
        Stimmt. Merkst du den Unterschied? In C/C++ sind Feldnamen und Zeiger
        eben nicht dasselbe!!! Auch wenn dies in vielen Büchern
        fälschlicherweise so dargestellt wird!!!
        Eigendlich doch. Der Feldname wird synonym zur Addresse des Anfangselementes benutzt (wurde schon bei K&R so definiert). Bei Feldern, die zB. in main() wie "int a[5]" definiert werden, handelt es sich um statische Felder. Der Feldname repräsentiert dabei eine Zeigerkonstante. Also immer noch einen Zeiger, der nich verändert werden darf. Sonst wäre ja schließlich keine Zeigerarithmetik möglich, wie der []-Operator zeigt. Es gilt: a[i] <=> *(a+i).

        *(a+i) wäre natürlich schlechter Programmierstil.

        Bei dynamischen Feldern (Siehe Lydia-Artikel).

        Gruß Frank.

        • Antwort von nach einem Tag hilfreich
          Re^4: Bemerkung

          Hi Frank :) Bei Feldern, die zB. in main() wie "int a[5]" definiert
          werden, handelt es sich um statische Felder. Der Feldname
          repräsentiert dabei eine Zeigerkonstante. Also immer
          noch einen Zeiger, der nich verändert werden darf.
          Ein Feldname wird während des Compilierens automatisch durch die Startadresse des Feldes ersetzt. Ein Zeiger hingegen ist eine Variable, deren Inhalt eine Adresse ist! Feldnamen und Zeiger sind also definitiv nicht dasselbe!!! Sonst wäre ja schließlich keine Zeigerarithmetik möglich,
          wie der []-Operator zeigt. Es gilt: a[i] <=> *(a+i).
          Das ist genau das, was in fast allen C/C++ Büchern das Problem ist. Dadurch wird suggeriert, dass Zeiger und Feldnamen dasselbe wären. Probier doch mal bitte in 2 Dateien eines Projektes folgende globale Definition/Deklaration:

          Datei 1:
          int a[10];

          Datei 2:
          extern int *a;

          Ich garantiere dir, dass jeder Compiler dabei aussteigt. In Datei 1 ist "a" ein Synonym für eine Speicheradresse. In Datei 2 ist "a" eine Variable, deren Inhalt eine Speicheradresse ist. Beide werden vom Compiler völlig unterschiedlich behandelt. Damit dürfte dann auch die angebliche Gleichheit von Feldnamen und Zeigern widerlegt sein :) *(a+i) wäre natürlich schlechter Programmierstil.
          Wieso? Es ist doch völlig egal, wie du das schreibst:

          a[5] = *(a+5) = *(5+a) = 5[a]

          Ach, ich liebe C/C++. Die Sprache ist so was von logisch :)

          cu Stefan.

          • Antwort von nach 2 Tagen hilfreich
            Re^5: Bemerkung

            Hi Stefan,

            das Mißverständnis, was in fast allen C/C++ - Büchern auftritt, ist wohl, daß man nicht streng zwischen den Datentyp Zeiger und Adresse unterscheiden kann.
            Ich benutze die Definition, daß ein Zeiger ein Datentyp ist (also eine Adresse, die auf einen bestimmten Typ zeigt).
            Variablen, die diesen Datentyp enthalten nenne ich Zeigervariablen. Ein Feldname wird während des Compilierens automatisch durch
            die Startadresse des Feldes ersetzt.
            Deshalb ist es ja auch eine Zeigerkonstante und keine Zeigervariable. Man könnte z.B. durch #define z ((int*) 0xffff0000) eine solche Art Zeigerkonstante definieren. Probier doch mal bitte in 2 Dateien eines
            Projektes folgende globale Definition/Deklaration:

            Datei 1:
            int a[10];

            Datei 2:
            extern int *a;

            Damit dürfte dann auch die angebliche Gleichheit
            von Feldnamen und Zeigern widerlegt sein :)
            Das zeigt nach obiger Definition, daß Konstanten und Variablen nicht dasselbe sind. *(a+i) wäre natürlich schlechter Programmierstil.
            Wieso? Es ist doch völlig egal, wie du das schreibst:
            a[5] = *(a+5) = *(5+a) = 5[a]
            Natürlich ist es egal. Wenn man aber in einem Entwicklungsteam arbeitet, wobei die Sourcen offengelegt werden, müssen gewisse Regeln (zB ungarische Notation u.a.) eingehalten werden, um nicht geteert und gefedert zu werden. Ach, ich liebe C/C++. Die Sprache ist so was von logisch :)
            dito

            Gruß Frank



Keine passende Antwort gefunden? Jetzt eigene Frage stellen!