Unterprogramm: Übergabe und Rückgabeparameter

Hi,
mal wieder eine Anfängerfrage:
Ich möchte einem Unterprogramm einen String übergeben und 2 Integerwerte zurückbekommen.
Das Unterprogramm soll aus dem String die beiden Werte extrahieren und als INT zurückgeben.
Kann ich da alle 3 Übergabevariablen in die Deklaration reinschreiben?
Also so:

void chkstring ( String readstring, int ret_val1, int ret_val2){
ret_val1 = irgendwas (readstring);
ret_val2 = was anderes (readstring);
}

(Das irgendwas/was anderes ist nur Platzhalter für meine Funktionen.
Ich frage deshalb, weil es nicht klappt. Mag jetzt sein, daß mein Code in der Sub noch buggy ist.
Ich würde nur gerne wissen, ob ich auf der richtigen "Spur" bin.

gruß/hk007

Nein, so funktioniert es nicht.
Der Grund hat einen Namen: "Call bei Value".
Beim Aufruf Deiner Funktion werden nicht die Variablen selbst, sondern deren aktuelle Werte zu Zeitpunkt des Aufrufs übergeben. Damit änderst Du zwar innerhalb Deiner Funktion die Werte aber nicht die Variablen selbst.

Schau mal hier rein: http://arduino.cc/forum/index.php/topic,128651.0.html
Da hab ich witzigerweise genau Dein Problem beschrieben und als Beispiel gelöst :slight_smile:

Hi,

Muss mal vorab erklären, daß ich aus der SPS-Welt komme, und da kann man Funktionen mit IN/IN-OUT/OUT Variablen definieren.
Ok. Hab das jetzt so verstanden, daß ich eine OUT-Variable nur dann beschreiben kann, wenn ich beim Aufruf den Pointer verwende.
Das würde dann für mein Beispiel so aussehen???

void chkstring ( String readstring, int *ret_val1, int *ret_val2){
*ret_val1 = irgendwas (readstring);
*ret_val2 = was anderes (readstring);
}

Nur noch eins:
Kann ich das Unterprogramm dann auch mehrmals mit verschiedener "Beschaltung" aufrufen?
Also so:

chkstring(stringin1, intval1a, intval1b):
chkstring(stringin2, intval2a, intval2b);

Kann ich das Unterprogramm dann auch mehrmals mit verschiedener "Beschaltung" aufrufen?

ja natürlich.
Das machst Du ja dauernd zb mit digitalRead() ecc.

Nur würd ich das nicht Unterprogramm sondern Funktion nennen und die "Beschaltung" Übergabeparameter.

Die Funktionen, die Arduino kennt, sind nicht anders als eine Funktion die Du im Sketch selbst definierst und dann benutzt. Die anderen Funktionen sind in den verschiedenen Bibliothen definiert die automatisch includiert werden oder die Du includierst.

Grüße Uwe

uwefed:
Nur würd ich das nicht Unterprogramm sondern Funktion nennen und die "Beschaltung" Übergabeparameter.

OK, :smiley:

ich war mir nur nicht sicher, ob er dann auch immer die Werte die in der Funktion mit "*ret_val1" berechnet auch dem von aussen zugewiesenen Übergabeparametern "intval1a" etc. zuweist.

Also ich würde für sowas noch KEINE Pointer nehmen sondern Referenzen.

Beispiel mit Pointern:

void move_a_to_b(int a, int *b) {
    *b = a;
}

loop()  {
    int b;
    move_a_to_b(1, &b);
}

Und jetzt (besser) mit Referenzen:

void move_a_to_b(int a, int &b) {
    b = a;
}

loop()  {
    int b;
    move_a_to_b(1, b);
}

Mit Referenzen hat man nicht den ganzen Code unnötig mit "*" vollgepflastert.

Pointer <-> Referenz

jetzt wird es mir zu hoch.......
Vor allem, weil du in der Funktion das b benutzt und aussen auch noch mal.

Zum Unterschied Pointer <-> Referenzen ...Da muss ich mich wohl mal etwas einlesen...

Das übersteigt meinen Horizot.
Uwe

Hi,

ich hab jetzt mal versucht das Ganze umzusetzen
Noch mal zu meiner Aufgabenstellung: ich bekomme über Ethernet einen String, in dem eine Uhrzeit enthalten ist. Die liegt irgendwo im String und sieht z.B. so aus:

23%3A59

Also 23:59 das %3A stellt den Doppelpunkt dar.
Jetzt will ich zwei Integer Variablen, in denen die Stunden und die Minuten enthalten sind.
Ich wollte eine Funktion dafür, da die Abfrage einige Male vorkommt, der String immer ähnlich aufgebaut ist, aber die Resultate in verschiedene Variablen geschrieben werden sollen.
Mein aktuelles Programm sieht so aus:

void chkstring ( String instring, int *ret_hour, int *ret_minute) {
             String substring =  (instring.substring(instring.indexOf("%3A")-2,instring.indexOf("%3A")));  
             char valueArray[substring.length() +1];              
             substring.toCharArray(valueArray, sizeof(valueArray));
             *ret_hour = atoi(valueArray);          
          
             String substring2 = (instring.substring(instring.indexOf("%3A")+3,instring.indexOf("%3A")+5));          
             char valueArray2[substring2.length() +1];              
             substring2.toCharArray(valueArray2, sizeof(valueArray2));
             *ret_minute = atoi(valueArray2); 
}
void loop() {
             int hour1, hour2, minute1, minute2;
             chkstring(readString1, &hour1, &minute1);
             chkstring(readString2, &hour2, &minute2);
}

Kompilieren klappt, aber die Werte sind immer "0".

Wahrscheinlich hab ich das Ganze eh wieder zu kompliziert aufgebaut, oder???

uwefed:
Das übersteigt meinen Horizot.

Referenzen sind kein Hexenwerk, aber wer viel C gemacht hat, wird durch diese C++ Erweiterung erstmal verwirrt, das ging mit auch so. Daher hab ich das bewußt erstmal nicht als Lösung angeboten :slight_smile:
Udo hat aber recht, es ist deutlich eleganter.

Im Prinzip wird aus dem üblichen "Call by Value", bei dem immer die Werte der Parameter auf den Stack gepackt werden wenn eine Funktion aufgerufen wird (default) ein "Call by reference" gemacht. Der Compiler sorgt also dafür das automatisch die Zeiger der Variablen an die Funktion übergeben werden (quasi eine Referenz auf die Werte). Man spart sich dadurch das "verpointern" bei der Übergabe und das ständige dereferenzieren der Pointer beim Rechnen. Das macht den Code übersichtlicher und weniger fehleranfällig. Wenn man statt "*a++" nämlich nur "a++" schreibt, das "a" aber ein Pointer ist, wird der Compiler nicht meckern, trotzdem kommt Murks raus :slight_smile:

void pointer_swap( int* a, int* b) {
 int temp = *b;
 *b = *a;
 *a = temp;
}

int referenz_swap( int &a, int &b) {
 int temp = b;
 b = a;
 a = temp;

}

void setup() {
 Serial.begin(9600);
  
 int a = 1;
 int b = 2;

 Serial.print(a);
 Serial.print(" - ");
 Serial.println(b);
 

 pointer_swap(&a,&b);

 Serial.print(a);
 Serial.print(" - ");
 Serial.println(b);

 referenz_swap(a,b);

 Serial.print(a);
 Serial.print(" - ");
 Serial.println(b);

}

void loop() {
  // put your main code here, to run repeatedly: 
  
}

Beider Funktionen machen das Gleiche, nur ist die Verwendung von referenz_swap() deutlich übersichtlicher weil man a und b direkt verwenden kann.

Hi,

jetzt hab ichs kapiert. Und läuft auch bei mir.
Danke für die geduldigen Erklärungen.

Ich stell mal meinen Code rein, falls es mal jemand brauchen kann.
Das Extrahieren der Integer hab ich auch etwas einfacher gestaltet.
Ich hoffe der charAt() ist ok, den hab ich vorhin irgendwo aufgeschnappt. Aber so spar ich mir das wandeln ich ein charArray.

void chkstring ( String instring, int &ret_hour, int &ret_minute) {
             int DP = (instring.indexOf("%3A"));   //Doppelpunkt suchen
             ret_hour = ((int(instring.charAt(DP-2))-48)*10) + (int(instring.charAt(DP-1))-48); // die 2 Zeichen vor dem Doppelpunkt in Zahl wandeln
             ret_minute = ((int(instring.charAt(DP+3))-48)*10) + (int(instring.charAt(DP+4))-48); // die 2 Zeichen nach dem Doppelpunkt in Zahl wandeln            
}
//Aufruf:
           chkstring (readString, hour, minute);

Vor allem, weil du in der Funktion das b benutzt und aussen auch noch mal.

Das nennt man "verschatten" von Variablen. Die Funktion hat einen lokalen Namensraum der den globalen Namensraum überdeckt. D.h. das b in der Funktion ist der Name des Parameters, das b außen ist der Name der Variablen. Aus Sicht des Compilers hätte ich auch

void move_a_to_b(int c, int &d) {
    d = c;
}

loop()  {
    int b;
    move_a_to_b(1, b);
}

schreiben können. Und wenn wir schon dabei sind, gefühlt sauberer wäre natürlich

void move_a_to_b(const int a, int &b) {
    b = a;
}

loop()  {
    int b;
    move_a_to_b(1, b);
}

obwohl das aus Sicht des Compilers in diesem Fall keinen großen Unterschied macht. Man könnte fast sagen, eigentlich gar keinen.