Eine Fkt. - mehrere Returnwerte

Gast #2770310
Lesenswert?

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
Gast #2770366
Lesenswert?

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
Gast #2770391
Lesenswert?

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
Gast #2770560
Lesenswert?

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.
#2770567
Lesenswert?

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
Moderator (Firma: Titel) Persönliche Seite #2770573
Lesenswert?

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).
#2770578
Lesenswert?

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
Moderator #2770727
Lesenswert?

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);
Gast #2770751
Lesenswert?

>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).
Persönliche Seite #2770753
Lesenswert?

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...
Gast #2770910
Lesenswert?

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.
#2770931
Lesenswert?

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
Persönliche Seite #2770938
Lesenswert?

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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren