moin, irgendwie bin ich immer und immerwieder vor dem Problem, dass ich gerne eine Funktion in C hätte, mit der ich mehrere Rückgabeparameter auf einmal zurückgeben kann. Beispiel: Ein Array nach dem kleinsten Wert durchsuchen und dessen Position im Array ebenfalls mit rausgeben. Eine Bastellösung wäre, die beiden Werte jeweils als ein High- und Low-Byte zu betrachten und das zusammengesetzte Wort zu returnen... Aber wie gesagt, dass wäre eine Bastellösung, die mir gerade so in den Sinn kommt und die irgendwie suboptimal ist, weil man immer noch eine zusätzliche Funktion brauch, um das wieder auseinander zu frickeln... Wie löst ihr am elegantesten eine solche Aufgabenstellung? mfg
Wie von den Vorpostern schon extrem kurz umschrieben: Das Stichwort heißt "call by reference". Mehrere Parameter lassen sich nicht über den eigentlichen Rückgabewert der Funktion zurückgeben. Stattdessen übergibst Du der Funktion beim Aufruf für die Rückgabeparameter einen oder mehrere Pointer auf beschreibbare Variablen, in denen die eigentliche Werte-Rückgabe erfolgt. Du übergibst also im Funktionsaufruf für die Rückgabeparameter der Funktion einen Pointer auf die zu beschreibende Variable. Diese kann z.B. auch ein Array oder Struct sein, womit man mit nur einem Pointer mehrere Parameter zurückgeben kann. Innerhalb der Funktion dereferenzierst Du den übergebenen Pointer, um die verschiedenen Rückgabewerte zu speichern. Die Funktion schreibt also ihre Rückgabewerte direkt in das durch den Pointer adressierte Array oder Struct. Über dieselbe Struktur kann man der Funktion natürlich auch Parameter übergeben, Array oder Struct müssen beim Funktionsaufruf ja nicht undefiniert sein. ;-) So ist z.B. eine Funktion machbar, die eine 3 x 3 Matrix als Array (per Pointer darauf!) übergeben bekommt und diese Matrix, falls möglich, invertiert. Die Inverse Matrix wird dann wieder in dem Übergabe-Array abgelegt und der normale Rückgabewert der Funktion wird als Fehlercode genutzt, um zu signalisieren, daß die Matrix nicht invertierbar ist. Gruß, Thorsten
Für Deine Funktion könnte das etwa so aussehen:
1 | // Definition der Funktion
|
2 | void search_minimum(uchar_t *p_minval, uchar_t *p_minidx, uchar_t arr) |
3 | {
|
4 | // lokale Variablen
|
5 | uint_t searchidx; |
6 | ...
|
7 | |
8 | // Minimum-Suche
|
9 | ...
|
10 | |
11 | // Parameterrückgabe
|
12 | *p_minval = arr[searchidx]; |
13 | *p_minidx = sarchidx; |
14 | }
|
15 | |
16 | |
17 | |
18 | // Aufruf der Funktion
|
19 | uchar_t min_value; |
20 | uchar_t min_index; |
21 | uchar_t array[ARRAY_LEN]; |
22 | |
23 | search_minimum(&min_value, &min_index, array); |
Gruß, Thorsten
nachtrag: in der Fkt. sollte der searchindex natürlich auf vom Typ uchar_t sein...
Ja, Pointer ist die Lösung. Hier einige Grundlagen zu Pointern in C: http://et-tutorials.de/3368/pointer-in-c/
Da merkt man, dass die Jungs die C erfunden haben, eher einen mathematischen Hintergrund hatten als dass sie Assembler-Hacker waren. Letztere haetten ganz selbstverstaendlich Funktionen mit mehreren Resultaten vorgesehen.
Marwin schrieb: > Da merkt man, dass die Jungs die C erfunden haben, eher einen > mathematischen Hintergrund hatten als dass sie Assembler-Hacker waren. > Letztere haetten ganz selbstverstaendlich Funktionen mit mehreren > Resultaten vorgesehen. Merkwürdig nur dass praktisch in allen Programmiersprachen Funktionen nur einen Rückgabewert haben. Das hat doch mit den "Jungs" von C nichts zu tun. Die Anlehnung an die mathematische Funktion ist durchaus sinnvoll. gruß cyblord
Marwin schrieb: > Da merkt man, dass die Jungs die C erfunden haben, eher einen > mathematischen Hintergrund hatten als dass sie Assembler-Hacker waren. > Letztere haetten ganz selbstverstaendlich Funktionen mit mehreren > Resultaten vorgesehen. In Assembler kann ich nicht mal 1 Wert zurückgeben, sondern muss von vorn herein sagen: ich erwarte den Wert an dieser Adresse (und sei es auch der Stack).
Alternativ kann man die Funktion einen Pointer auf ein Array zurückgeben lassen das 2 Einträge hat die dann Minimum und Position ausgeben. Was die anderen nicht "beachtet" haben ist, dass deine Funktion noch irgendwoher wissen muss wie lang dein Array ist, brauchst also einen Zusätzlichen eingabeparameter. Florian
Timmo H. schrieb: > Struct? Genau, am besten mit Typedef kombiniert:
1 | typedef struct // neuen Datentyp mit der Struktur der Rückgabe definieren |
2 | {
|
3 | int index; |
4 | float wert; |
5 | }
|
6 | RETURNSTRUCT; |
7 | |
8 | RETURNSTRUCT funktion(void); |
Servus Michael
Marwin schrieb: > Da merkt man, dass die Jungs die C erfunden haben, eher einen > mathematischen Hintergrund hatten als dass sie Assembler-Hacker waren. Andersherum wird ein Schuh daraus: Gerade diejenigen Sprachen, die etwas mathematisch angehaucht sind (prominentestes Beispiel: Haskell), erlauben die Rückgabe von Tupeln. Die Zerlegung des Tupels in einzelne Komponenten kann dank Pattern- Matching im gleichen Aufwasch erfolgen. Beispiel:
1 | sumprod x y = (x+y, x*y) -- Funktion mit zwei Ergebnissen |
2 | |
3 | (s, p) = sumprod 3 4 -- Anwendung ergibt s=7 und p=12 |
In vielen anderen Sprachen (Python, Ruby, Lua, Groovy ...) geht das ganz ähnlich. Bei den C-Entwicklern hingegen stand nicht die reine Mathematik sondern der reale Prozessor im Vordergrund. Eines der ursprünglichen Konzepte bestand darin, Funktionswerte in einem (immer gleichen) Prozessorregis- ter oder allenfalls einem Registerpaar (bei double) zurückzugeben. Die Rückgabe mehrerer Werte (sei es als getrennte Werte oder zusammengefasst in eine Struktur oder ein Array) war deswegen nicht vorgesehen. Struktu- ren als Funktionswerte kamen erst später (mit ANSI-C), Arrays sind immer noch nicht möglich. Das Beispiel von oben ist in heutigem C zwar in ähnlicher Form möglich, sieht aber recht holprig aus:
1 | // Hilfsstruktur
|
2 | |
3 | struct SumProd { |
4 | int sum, prd; |
5 | };
|
6 | |
7 | // Funktion mit zwei Ergebnissen
|
8 | |
9 | struct SumProd sumprod(int x, int y) { |
10 | struct SumProd sp; |
11 | sp.sum = x + y; |
12 | sp.prd = x * y; |
13 | return sp; |
14 | }
|
15 | |
16 | // Anwendung ergibt s=7 und p=12
|
17 | |
18 | struct SumProd sp; |
19 | int s, p; |
20 | |
21 | sp = sumprod(3, 4); |
22 | s = sp.sum; |
23 | p = sp.prd; |
Deswegen bedient man sich auch heute noch meist der klassischen Methode mit den Zeigerargumenten:
1 | // Funktion mit zwei Ergebnissen
|
2 | |
3 | void sumprod(int x, int y, int *sum, int *prd) { |
4 | *sum = x + y; |
5 | *prd = x * y; |
6 | }
|
7 | |
8 | // Anwendung ergibt s=7 und p=12
|
9 | int s, p; |
10 | |
11 | sumprod(3, 4, &s, &p); |
>Ein Array nach dem kleinsten Wert durchsuchen und dessen Position im >Array ebenfalls mit rausgeben. Der "richtige" Returnwert ist dann die Position und sonst nix. Auf die Rückgabe des Wertes kann man verzichten, denn um den zu wissen muss man ja nur "a[i]" schreiben (wenn i die Position ist und a das Array).
Vuvuzelatus schrieb: >>Ein Array nach dem kleinsten Wert durchsuchen und dessen Position im >>Array ebenfalls mit rausgeben. > > Der "richtige" Returnwert ist dann die Position und sonst nix. Auf die > Rückgabe des Wertes kann man verzichten, denn um den zu wissen muss man > ja nur "a[i]" schreiben (wenn i die Position ist und a das Array). Das war auch mein erster Gedanke...
Lothar Miller schrieb: > In Assembler kann ich nicht mal 1 Wert zurückgeben, sondern muss... Das ist Quatsch. Man kann in Assembler so ziemlich alles zurückgeben. Das Ganze ist lediglich eine Frage der vereinbarten Implementierung. So kann man bei Arm nachlesen, daß Funktionen dort im Prinzip sehr wohl mehrere Variablen zurückgeben können, weil es bei den dafür vorgesehenen Registern möglich ist. In Assembler wäre das nutzbar. ABER: Es macht niemand davon Gebrauch, weil sowas in den einschlägigen Programmiersprachen nicht vorgesehen ist. nochwas: Gert schrieb: > Ja, Pointer ist die Lösung. Jaja, eine zwar benutzbare, aber schlechte Lösung. Anstatt mit Pointern um sich zu werfen, sollte man lieber über seine Lösungswege nachdenken und sich einen besseren Code ausdenken, der weitgehend ohne sowas auskommt. Pointer als Resultate sind grottenschlecht, denn worauf sollen sie zeigen? Auf Variablen auf dem Stack etwa? W.S.
Naja Pointer zurückgeben meinte er wohl nicht, sondern vielmehr einen Pointer auf ein Array oder Struct übergeben und dort direkt reinschreiben. Also call by reference. Das ist natürlich performanter als ein Struct zurückzugeben, da man sich so die ganze Rumkopiererei spart. Hängt halt immer davon ab wie knapp die Ressourcen sind wie man das implementiert. Ich verwende beides, aber wenn ich mehrere Werte zurückgeben will, arbeite ich meist mit Pointer auf struct und schreibe direkt rein, anstatt ein Struct zurückzugeben. Wenn ich z.B. überlege wenn ich meine "Einstellungen" die im EEPROM liegen auslese und vielleicht 30 Werte umfassen, dann würde ich nicht im Traum darauf kommen das ganze per return by value zu realisieren, zumindest nicht auf einem µC
W.S. schrieb: > Gert schrieb: >> Ja, Pointer ist die Lösung. > > Jaja, eine zwar benutzbare, aber schlechte Lösung. Anstatt mit Pointern > um sich zu werfen, sollte man lieber über seine Lösungswege nachdenken > und sich einen besseren Code ausdenken, der weitgehend ohne sowas > auskommt. Schwachsinn. > Pointer als Resultate sind grottenschlecht, denn worauf sollen > sie zeigen? Auf Variablen auf dem Stack etwa? Warum denn nicht? Schau mal als Beispiel in die Windows API, die ist voll mit sowas. Mal zufallsmäßig was rausgepickt: http://msdn.microsoft.com/en-us/library/windows/desktop/aa363260(v=vs.85).aspx
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.