Funktion mit Wertrückgabe ?

Hallo,

kann eine Funktion wirklich nur ein Wert zurückgeben oder doch soviel man möchte?
Auch wenn man globale Variablen verwendet?

Wenn Du Pointer verwendest, kann eine Funktion auch mehrere Werte zurück"geben". Wobei das strenggenommen kein zurückgeben ist, sondern eher das Beschreiben von Speicherbereichen, die dann per Referenz wieder abgerufen werden können.

hi,

ich glaube, da hast Du was falsch verstanden.

man kann in einer funktion natürlich soviele globale variablen ändern, wie man will, und mit diesen veränderten variablen dann in anderen funktionen arbeiten.

der rückgabewert einer variablen ist etwas anderes:

byte summe(byte a, byte b) { return a+b; }

der rückgabewert dieser funktion ist die summe aus a und b, man fragt also mit

c = summe(4, 7);

ab, und c ist dann 11.

auf diese art ist dann natürlich nur ein rückgabewert möglich. mit pointern sollte dann wohl mehr gehen.

gruß stefan

Hallo,

ich verarbeite in 2 Funktionen mehrere globale Variablen. Zwischen den Funktionsaufrufen muß ich diese ändern und dann werden diese wieder in der nächsten verarbeitet. Deshalb brauche ich die Werte der globalen Variablen aus den Funktionen immer zurück. Das geht nur mit Pointern?

Wer hat denn das verzapft das man nur eine Variable zurückbekommt. :frowning:

Das geht demzufolge nicht so einfach.

int Kometenschweif_Links ()
   {
     analogWrite(LEDPOS_1, LED_H1);                                  
     analogWrite(LEDPOS_2, LED_H2);  
     analogWrite(LEDPOS_3, LED_H3);                
     analogWrite(LEDPOS_4, LED_H4);      
     analogWrite(LAST_LED, LOW);                     // letzte alte LED ausschalten
             
     LAST_LED = LEDPOS_4;                              // letzte Position merken
     LEDPOS_4 = LEDPOS_3;                              // LED Position nachrücken
     LEDPOS_3 = LEDPOS_2;
     LEDPOS_2 = LEDPOS_1;
     LEDPOS_1--;
          
     delay(3000);       
                            
     if (LEDPOS_1 < 2) { LEDPOS_1 = 9; }             // wenn letzte LED erreicht, auf Anfangsposition setzen
     
     return LEDPOS_1; LEDPOS_2; LEDPOS_3; LEDPOS_4; LAST_LED;
   }

verzapft ist gut ;) in welcher anderen sprache geht sowas?

mit pointern kannste alles verändern und nebenbei extrem viel blödsinn anstellen.

mehrere globale Variablen

wozu musst du die zurückgeben?

LAST_LED = LEDPOS_4; // letzte Position merken LEDPOS_4 = LEDPOS_3; // LED Position nachrücken LEDPOS_3 = LEDPOS_2; LEDPOS_2 = LEDPOS_1; LEDPOS_1--;

das ganze ist laut deiner aussage global und somit änderst du den inhalt jeder globalen variable in jeder deiner funktionen! da brauchst du keine rückgabe.

sobald du in deinem kometenschweif die werte für LEDPOS_3 = LEDPOS_2; etc geändert hast, kannst du nach dem aufruf für kometenschweif die neuen werte weiter benutzen.

Hallo,

oh Mann, ich Idiot. Jetzt wo Du das sagst, klar, wofür habe denn globale Variablen verwendet. Na dann brauche ich keine Rückgabewerte, logisch. ;)

Ich denke mal es hätte niemanden daran gehindert die Funktion so zu programmieren das man auch mehrere Rückgabewerte verwenden kann. Ich meine, übergeben kann man soviel man braucht an die Funktion. Nur beim rausrücken wird gespart. Das könnte vieles vereinfachen. Die Einschränkung verstehe ich nicht.

Doc_Arduino: Wer hat denn das verzapft das man nur eine Variable zurückbekommt. :(

Als sogenannten "Rückgabewert" kannst Du nur einen einzigen Wert zurückbekommen. Das ist der Wert, der vor der Funktionsdeklaration steht, also das "int" vor dem Funktionsnamen "summe":

int summe(int a, int b) { return a+b; }

Der return-Wert. Dieser Wert ist direkt Zuweisungskompatibel. Und so wie ein Gleichheitszeichen nur eine einzige Zuweisung macht, macht eine Funktion auch nur einen einzigen zuweisungsfähigen Rückgabewert:

int i=summe(2,4);

Trotzdem kann eine Funktion auch mehrere Werte "zurückgeben", und zwar wenn Du diese Werte auch vorher auch an die Funktion "übergeben" hast, und zwar als Parameter mit spezieller Aufrufkonvention.

Bei der Parameterübergabe an Funktionen gibt es zwei Möglichkeiten: 1. Call by Value ==> Parameter als Wert an die Funktion übergeben 2. Call by Reference ==> Parameter als Referenz an die Funktion übergeben

Die "by Value" übergebenen Parameter können Konstanten sein, oder Variablen, die nach der Rückkehr aus der Funktion unverändert sind.

Die "by Reference" übergebenen Parameter werden als Adressreferenz an die Funktion übergeben, und wenn die Funktion diese Parameter in der Funktion verändert und die Funktion kehrt zurück, dann haben diese Parameter ihren veränderten Wert!

Eventuell kennst Du die Parameterübergabe "by Reference" noch nicht.

Eine Parameterübergabe "by Reference" wird so deklariert, dass bei der Funktionsdeklaration ein "&"-Zeichen (Adressoperator) vor den so zu übergebenden Parameter geschrieben wird.

Ich habe mal ein Beispielprogramm mit zwei Funktionen gemacht, bei denen ein oder mehrere Parameter "by Reference" übergeben werden.

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}


void zumQuadrat(float &x){
   x = x*x; 
}


void testParamsByReference(int a, int b, int c, int &ret1, int &ret2, int &ret3)
{
  ret1=a+b+c;
  ret2=a*b*c;
  ret3=ret1+ret2;
}


void loop() {
  // put your main code here, to run repeatedly: 
  float x=1.5;  
  Serial.print("x= ");
  Serial.println(x);
  zumQuadrat(x);
  Serial.println("Zum Quadrat nehmen");
  Serial.print("x= ");
  Serial.println(x);
  Serial.println();

  int var1, var2, var3, var4, var5, var6;
  var1=4;
  var2=5;
  var3=6;
  testParamsByReference(var1, var2, var3, var4, var5, var6);
  Serial.print("Var1= ");
  Serial.println(var1);
  Serial.print("Var2= ");
  Serial.println(var2);
  Serial.print("Var3= ");
  Serial.println(var3);
  Serial.print("Summe: ");
  Serial.println(var4);
  Serial.print("Produkt: ");
  Serial.println(var5);
  Serial.print("Summe plus Produkt: ");
  Serial.println(var6);
  while (true==true);
}

Probier's aus: Eine Parameterliste einer Funktion kann praktisch beliebig lang sein, und darunter können beliebig viele "by Reference" Parameter sein, deren "Rückgabewert" Du nach dem Aufrufen der Funktion auswerten kannst.

Du kannst einen, mehrere oder alle Parameter "by Reference" an eine Funktion übergeben. Bei Bedarf einfach den "&"-Adressoperator mit in die Funktionsdeklaration des Parameters schreiben.

Ist es eventuell das, was Du gesucht hast?

Doc_Arduino: Hallo,

oh Mann, ich Idiot. Jetzt wo Du das sagst, klar, wofür habe denn globale Variablen verwendet. Na dann brauche ich keine Rückgabewerte, logisch. ;)

Ich denke mal es hätte niemanden daran gehindert die Funktion so zu programmieren das man auch mehrere Rückgabewerte verwenden kann. Ich meine, übergeben kann man soviel man braucht an die Funktion. Nur beim rausrücken wird gespart. Das könnte vieles vereinfachen. Die Einschränkung verstehe ich nicht.

Hallo,, wenn du noch was lernen möchtest, dann schau doch mal in google oder einen C++-Lehrbuch zu dem Thema "Referenzen" nach. damit kannst du die Verwendung von Pointern bei der Rückgabe von mehreren Werten vermeiden. Z.B. hier http://de.wikipedia.org/wiki/Referenzparameter unter dem Stichwort "Referenzparameter in Form von Referenzen".

Manche Leute sehen die Verwendung von globalen Variablen zur Parameterübergabe (oder überhaupt) als ganz schön "bah, bah" an ;) Man kann damit bei größeren Programmen nämlich ganz schnell den Überblick verlieren, wer wo etwas verändert.

Gruß Wolfgang

[Edit]Mist, da war jurs wohl schneller als ich und sogar noch ausführlicher

Hallo,

klingt erstmal für mich sehr kompliziert. :~ Werde mir das in Ruhe anschauen und mehrfach lesen müssen. Danke Euch.

Hier mein Kometenschweif, der erstmal soweit funktioniert. Verwendete Pins sind 2 bis 9. Das soll ein zusätzlicher Lichteffekt werden außen herum für meinen Würfel, wenn er würfelt bei Tastendruck.

// Arduino Mega 2560

// Geschwindigkeitssteigerung des Kometenschweifs bzw. abbremsen
int PAUSE [41] = {48,52,56,60,64,68,72,77,82,86,91,96,101,107,112,118,123,129,135,141,147,154,160,167,174,180,188,195,202,209,217,225,233,240,249,257,265,274,282,291,300 };
int SPEED = 40;       

int LEDPOS_1 = 5;                      // auf sicheren Anfangswert gesetzt für ersten Durchlauf
int LEDPOS_2 = 4;                      // auf sicheren Anfangswert gesetzt für ersten Durchlauf
int LEDPOS_3 = 3;                      // auf sicheren Anfangswert gesetzt für ersten Durchlauf
int LEDPOS_4 = 2;                      // erster Pin der LED Reihe
int LAST_LED = 9;                      // letzter Pin der LED Reihe

int LED_1, LED_2, LED_3, LED_4;        // zum Zwischen speichern der LED Position zum umdrehen

int LED_H1 = 255;        // 255, Helligkeiten der LEDs     
int LED_H2 = 96;         //  96
int LED_H3 = 32;	     //  32											
int LED_H4 = 8;           //   8

void setup()
{     
//   Serial.begin(9600);
 
  // set pins 2 through 9 as outputs:
  for (int thisPin =2; thisPin <= 9; thisPin++)
   { 
    pinMode(thisPin, OUTPUT); 
   }

//  Serial.println("\n[memCheck]"); 
//  Serial.println(freeRAM(), DEC);  
   
}

void loop()
{

  for (int i=0; i < 60; i++)
    { 
     Kometenschweif_Rechts ();
    }

  Schweif_nach_links_drehen ();
     
  for (int i=0; i < 47; i++)
    { 
     Kometenschweif_Links ();
    }

  Schweif_nach_rechts_drehen ();   

}

 


/* ***  Funktionen  *** */

void Kometenschweif_Rechts ()
{
     if (LEDPOS_1 > 9) { LEDPOS_1 = 2; }             // wenn "letzte" LED erreicht, auf Anfangsposition setzen
     
     analogWrite(LEDPOS_1, LED_H1);                  // 8, Schweifspitze ganz hell                                    
     analogWrite(LEDPOS_2, LED_H2);                  // 7, LED dahinter etwas dunkler   
     analogWrite(LEDPOS_3, LED_H3);                  // 6, nächste LED noch dunkler                  
     analogWrite(LEDPOS_4, LED_H4);                  // 5, letzte LED noch dunkler        
     analogWrite(LAST_LED, LOW);                     // letzte alte LED ausschalten
             
     LAST_LED = LEDPOS_4;                            // 5

     LEDPOS_4 = LEDPOS_3;                            // 6
     LEDPOS_3 = LEDPOS_2;                            // 7
     LEDPOS_2 = LEDPOS_1;                            // 8
     LEDPOS_1++;                                     // 9
 
     delay(PAUSE[SPEED]);                            // Kometenschweif beschleunigen
     SPEED--;
     if (SPEED < 1) { SPEED = 0; }                   // schnellsten Wert beibehalten
                            
}


void Kometenschweif_Links ()
{
     if (LEDPOS_1 < 2) { LEDPOS_1 = 9; }             // wenn "letzte" LED erreicht, auf Anfangsposition setzen

     analogWrite(LEDPOS_1, LED_H1);                  // Schweifspitze ganz hell                 
     analogWrite(LEDPOS_2, LED_H2);                  // LED dahinter etwas dunkler        
     analogWrite(LEDPOS_3, LED_H3);                  // nächste LED noch dunkler                      
     analogWrite(LEDPOS_4, LED_H4);                  // letzte LED noch dunkler            
     analogWrite(LAST_LED, LOW);                     // letzte alte LED ausschalten
             
     LAST_LED = LEDPOS_4;

     LEDPOS_4 = LEDPOS_3;
     LEDPOS_3 = LEDPOS_2;
     LEDPOS_2 = LEDPOS_1;
     LEDPOS_1--;
          
     delay(PAUSE[SPEED]);                            // Kometenschweif abbremsen
     SPEED++;
     if (SPEED > 40) { SPEED = 40; }                 // langsamsten Wert beibehalten
}
 

void Schweif_nach_links_drehen ()
{
     LED_1 = LAST_LED;             //5,  LED Positionen zwischen speichern
     LED_2 = LEDPOS_4;             //6
     LED_3 = LEDPOS_3;             //7
     LED_4 = LEDPOS_2;             //8

     LEDPOS_1 = LED_1;             //5,  LED Positionen umdrehen
     LEDPOS_2 = LED_2;             //6
     LEDPOS_3 = LED_3;             //7
     LEDPOS_4 = LED_4;             //8

     if (LEDPOS_4 >= 9) { LAST_LED = 2;}
       else { LAST_LED = LEDPOS_4 + 1; } 
}


void Schweif_nach_rechts_drehen ()
{
     LED_1 = LAST_LED;              //  LED Positionen zwischen speichern
     LED_2 = LEDPOS_4;             
     LED_3 = LEDPOS_3;        
     LED_4 = LEDPOS_2;          

     LEDPOS_1 = LED_1;              //  LED Positionen umdrehen
     LEDPOS_2 = LED_2;            
     LEDPOS_3 = LED_3;           
     LEDPOS_4 = LED_4;           

     if (LEDPOS_4 <= 2) { LAST_LED = 9;}
       else { LAST_LED = LEDPOS_4 - 1; } 
}


int freeRAM()                                     //  Funktion im WWW gefunden, zeigt freien Speicher an
  { 
  extern int __heap_start, *__brkval;  
  int v;  
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
  }

Mattes: verzapft ist gut ;) in welcher anderen sprache geht sowas?

Mehrere Rückgabewerte gehen z.B. schon immer in Python und seit einiger Zeit auch in .NET. Stichwort: Tupel Da kann man dann z.B. solche Konstrukte machen: a,b,c = mach_irgendwas() Ist eine nette Sache. Wenn man sich daran mal gewöhnt hat, vermisst man es bei anderen Sprachen schon etwas.

@jurs wow, Du hast den gordischen Knoten bezüglich Pointer in meinem Hirn zerschlagen! :D

Wenn Du jetzt noch eine ähnlich klare Erklärung für "dereference" hättest..... :blush:

MaFu:

Mattes: verzapft ist gut ;) in welcher anderen sprache geht sowas?

Mehrere Rückgabewerte gehen z.B. schon immer in Python und seit einiger Zeit auch in .NET. Stichwort: Tupel Da kann man dann z.B. solche Konstrukte machen: a,b,c = mach_irgendwas() Ist eine nette Sache. Wenn man sich daran mal gewöhnt hat, vermisst man es bei anderen Sprachen schon etwas.

Es ist zwar schön in Nachbars Garten zu schauen und sich zu freuen, was es da so alles gibt, aber man muß im eingenen Garten bleiben, sprich C /C++ benutzen das Arduino versteht. Zum Rückgabewert. Viele Funktionen geben im Fehlerfall -1 zurück.

Vieel Grüße Uwe

1) Wie schon erwähnt wurde kann C Referenzen in den Aufrufparametern verwenden. 2) Man kann sich ja beliebige Strukturen für die Rückgabeparameter definieren. Problem dabei ist nicht C sondern die schlecht gemachte Arduino IDE die meint Prototypen definieren zu müssen aber dabei typedefs nicht berücksichtigt. Ein Workaround ist TRICK17 http://blog.blinkenlight.net/2012/09/01/trick-17/, die andere Lösung die Verwendung von Namespaces, also so:

namespace example {
    typedef struct {
        int a;
        int b;
        int c;
    } triple;
        
    triple test(const int x, const int y, const int z) {
        triple value;
        value.a = x;
        value.b = y;
        value.c = z;
        return value;
    }
    
    triple test2(const int x, const int y, const int z) {
        return triple {x, y, z};
    }
}


void setup() {
    using namespace example;
    
    const triple result = test(1,2,3);
    
    const int x = result.a;
    const int y = result.b;
    const int z = result.c;
}

void loop() {}

test() und test2() haben das gleiche Verhalten sind aber einmal ausführlich und einmal kurz hingeschrieben.

Stattdessen globale Variablen zu verwenden ist nicht wirklich Stand der Technik.

Hallo Udo,

dankefür das kurze Tutorial. Das mit NAmespaces war mir völligeu im Arduino-Chargon, kannte ich bisher nurvon DotNet.
Verhalten sich die Namespaces genau wie in z.B. C#, dass man ohne die Using-Direktive z.B. “example.result.a” schreiben könnte? Und wie verhält es sich mit der Speichernutzung?

Fast jede moderne Sprache hat so ein Konzept. Manche mehr manche weniger ;) Namespaces wirken sich bei kompilierten Sprachen eher wenig bis gar nicht auf den Speicherverbrauch aus. Was die Verwendung in C++ angeht guckst Du hier: http://www.cplusplus.com/doc/tutorial/namespaces/

Super... Vielen Dank! 8)

Hallo,

liest sich nicht schlecht. Ich bleibe erstmal bei meinen globalen Variablen. Sonst verstehe ich mein eigenes Programm später nicht mehr. :D Nachdem ich Tage lang an dem Schweif gefummelt habe. Ich werde später darauf zurückkommen.

@jurs auch wenn der Thread schon alt ist hat dein Post hier ewiges suchen beendet. Wenn ich referenziere, spart mir dass dann auch variablen Speicher? (wenn ich sie öfter mit unterschiedlichen Variablen aufrufe) Und ist das korrekt das korrekt das Arrys standartmäßig referenziert werden?

Und ist das korrekt das korrekt das Arrys standartmäßig referenziert werden?

Arrays sind in C/C++ keine Objekte. Eine Array Variable zerfällt in einen Zeiger auf das erste Element. Bei Arrays wird also immer nur der Zeiger übergeben. Das ist auch der Grund weshalb man keine Arrays als Rückgabewert verwenden kann, bzw. keinen lokalen Arrays zurückgeben kann.

Hmmm, da ich eher aus der Delphi-Ecke komme bin ich kein C++ Experte - aber solch trickreiche Typen-Rückgaben wie in #12 von Udo Klein erinnern mich stark an die “type x = record … end;” aus Pascal. Daher habe ich heute morgen ein wenig experimentiert und festgestellt, dass die Arduino-IDE in dieser Hinsicht wohl um einiges verbessert worden ist. Solch Typen-Rückgaben wie beschrieben funktionieren zumindes ab der Version 1.6.7 auch ohne “namespace”. Sogar so gut, dass man in der “struct” auch Arrays unterbringen kann:

// Typendeklaration 
struct TArray
{
  byte arr[3];
};

// Beliebige Funktion mit Rückgabetyp "TArray"
TArray MyFunc( byte x )
{
  // Locale Var. vom Typ "TArray"
  TArray locVar;

  // Zuweisungen gemäß Input "x"
  locVar.arr[0] = 10 +x;
  locVar.arr[1] = 10 -x;
  locVar.arr[2] = 10 *x;

  // Rückgabe des Arrays (der Struktur)
  return locVar;
}

// Variable vom Typ "TArray"
TArray TestVar;
 
void setup() {
  Serial.begin(9600);
  
  TestVar = MyFunc(5);
  Serial.println( TestVar.arr[0] );  
  Serial.println( TestVar.arr[1] );  
  Serial.println( TestVar.arr[2] );  
}

void loop() {
}

Damit ist man sicherlich nicht mehr abwärtskompatibel. Und ob es ökonomisch in Bezug auf Speicherverwaltung ist kann ich nicht sagen. Wohl eher nicht. Dennoch funktioniert obiger Beispiel-Code bei mir ohne irgendwelche Probleme oder Warnungen und bietet Anreiz, damit weiter zu experimentieren.