Go Down

Topic: Funktion mit Wertrückgabe ? (Read 2706 times) previous topic - next topic

Doc_Arduino

Hallo,

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

markbee

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.
XBee blog: http://lookmanowire.blogspot.com/

Eisebaer

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
nimm abblockkondensatoren!  100nF.  glaub mir, nimm abblockkondensatoren!

Doc_Arduino

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.   :(

Das geht demzufolge nicht so einfach.

Code: [Select]

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;
   }
Tschau
Doc Arduino

Mattes

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

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

Quote
mehrere globale Variablen


wozu musst du die zurückgeben?

Quote
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.

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.

Tschau
Doc Arduino

jurs


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.

Code: [Select]

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?

voithian

#7
Mar 15, 2013, 09:03 pm Last Edit: Mar 15, 2013, 09:05 pm by voithian Reason: 1

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

Doc_Arduino

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.

Code: [Select]

// 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);
  }

Tschau
Doc Arduino

MaFu

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.
_______
Manfred

spaceball

@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.....
:smiley-red:

uwefed


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

Udo Klein

#12
Mar 16, 2013, 12:14 pm Last Edit: Mar 16, 2013, 12:26 pm by Udo Klein Reason: 1
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:

Code: [Select]

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.
Check out my experiments http://blog.blinkenlight.net

Marcus W

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?

Udo Klein

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/
Check out my experiments http://blog.blinkenlight.net

Go Up