Hallo alle zusammen,
ich benötige mal wieder einwenig Hilfe von euch.
Ich möchte gern zwei Werte aus einer Funktion zurück geben. Ja ich weiß,
dass klappt so nicht. Daher hatte ich mir folgendes Überlegt. Ich nutze
ein Array.
Die Funktion sollte z.B. wie folgt aussehen:
1
uint8_tzweiwerte(uint8_tvorher,uint8_tzaehler)
2
{
3
uint8_tvorherzaehler[2],i;
4
vorherzaehler[1]=vorher+2;
5
zaehler++;
6
vorherzaehler[2]=zaehler;
7
returnvorherzaehler;
8
}
würde doch so gehen oder???
aber wie lese ich jetzt die zwei werte im Hauptprogramm aus?
1
#include .......
2
....
3
uint8_tzweiwerte(uint8_t,uint8_t);
4
....
5
intmain(void)
6
{
7
.....
8
jetzt=10;
9
zaehler_aktuell=0;
10
....
11
zweiwerte(jetzt,zaehler_aktuell);// übergibt den Wert 10 an vorher und den Wert 0 an zaehler
12
// RICHTIG????
13
14
jetzt=zweiwerte[1];
15
zaehler_aktuell=zweiwerte[2];
16
.....
17
}
jetzt müste doch in jetzt der Wert 12 stehen und in zaehler_aktuell der
Wert 1. richtig??? Oder habe ich mir das zueinfach gedacht?
Ich bedanke mich für euere Hilfe und verbleibe
mit freundlichen Grüßen
Balou Baer
Du musst im Hauptprogramm ein Array anlegen und die Adresse an die
Funktion übergeben. Dann kannst du in der Funktion auf das Array
zugreifen.
Näheres findest du in einem C-Buch unter Pointer, Adressen einer
Variable.... ;-).
Daniel R. schrieb:> Näheres findest du in einem C-Buch
Mist, ich wollte gerade noch schreiben:
Gleich wird jemand kommen und dir ein C-Buch vorschlagen. Recht wird er
haben.
:)
So könnte es gehen. Ist aber nur eine Variante von vielen.
Achtung: vorherzaehler in zweiwerte ist static,
d.h. es gibt nur einen Speicherbereich für die Ergebnisse,
auch wenn du es mehrmals aufrufst.
Achtung: Indizes in C beginnen mit 0, nicht mit 1.
Returnwerte einer Funktion bekommt man nur,
wenn man sie auch anfordert, sonst sind sie weg.
#define uint8_t unsigned char
uint8_t* zweiwerte (uint8_t vorher, uint8_t zaehler)
{
static uint8_t vorherzaehler[2];
vorherzaehler[0]=vorher + 2;
zaehler++;
vorherzaehler[1]=zaehler;
return vorherzaehler;
}
int main (void)
{
uint8_t jetzt = 10;
uint8_t zaehler_aktuell = 0;
uint8_t *zweireturnwerte;
zweireturnwerte = zweiwerte (jetzt,zaehler_aktuell);
jetzt = zweireturnwerte[0];
zaehler_aktuell = zweireturnwerte[1];
printf("jetzt=%d\n", jetzt);
printf("zaehler_aktuell=%d\n", zaehler_aktuell);
}
Andere Variante von zweiwerte (nicht getestet):
void zweiwerte (uint8_t vorher, uint8_t *zaehler, uint8_t *jetzt)
{
(*jetzt) = vorher + 2;
(*zaehler)++;
}
Aufruf in der Art (nicht getestet):
zweiwerte(jetzt, &zaehler_aktuell, &jetzt);
Balou Baer schrieb:> void zweiwerte (uint8_t *vorher, uint8_t *zaehler)> {>> &vorher=&vorher + 2;>> &zaehler++;>> }> #include .......> ....> void zweiwerte (uint8_t *vorher,uint8_t *zaehler);> ....> int main (void)> {> .....> jetzt = 10;> zaehler_aktuell = 0;> ....> zweiwerte (&jetzt,&zaehler_aktuell); // übergibt die ADRESSEN von beiden
Variablen an die Funktion
>> }
Gugst Du noch mal Funktionen ALGEMEIN und dann speziell Zeiger als
Funktionsparameter (call–by–reference).
Geht leider nicht:
Du gibst vorherzaehler mit deinem return zurück. Dieses Array wurde
allerdings IN der Funktion deklariert. Sie wird aufgerufen, Speicher für
das Array reserviert, Funktion wird ausgeführt und dann wird der
Speicher wieder freigegeben. Wenn du das Array zurückgibst, wurde das
Ding quasi "gelöscht". Bzw sein Speicher darf wieder mit was anderem
vollgeschrieben werden, was beim nächsten Funktionsaufruf mit großer
Wahrscheinlichkeit passieren wird.
Daher gibst du der Funktion Speicher zum Vollschreiben mit, der auch
nach Beenden der Funktion noch reserviert bleibt:
zweiwerte (jetzt,zaehler_aktuell, vorher); // übergibt den Wert 10 an vorher und den Wert 0 an zaehler
13
// RICHTIG????
14
15
jetzt = vorher[1];
16
zaehler_aktuell = vorher[2];
17
.....
18
}
jetzt = zweiwerte[1]; geht übrigens nicht. Du kannst
Pointer=zweiwerte(...)
schreiben und dann pointer[1]
Dann greift man übrigens auf das erste Element eines Arrays mit dem
Index null und nicht Index 1 zu. vorherzaehler[2] verreißt dir dein
Programm zur Laufzeit, weil du versuchst auf das dritte Element
zuzugreifen.
Die ganz feine englische Art wäre:
zweiwerte (&jetzt, &zaehler_aktuell); // übergibt den Wert 10 an vorher und den Wert 0 an zaehler
12
// RICHTIG????
13
.....
14
}
Du gibst deinem Unterprogramm zwei Zahlen, lässt das UP etwas damit
machen, holst dir die Ergebnise und kopierst sie dann im Hauptprogramm
in die Variablen, aus denen auch die zwei Variablen kommen.
C-like wäre, wenn du dem UP nicht sagst wie die Zahlen sind, sondern wo
sie im Speicher stehen. Dann kann dir dein UP die Arbeit mit dem Werte
kopieren abnehmen.
Die dritte Möglichkeit wäre übrigens die Rückgabewerte in ein Struct zu
packen. Ist aber alles Verschwendung von Zeit und Speicher.
Danke euch für die Antworten.
mir raucht der Kopf ich mach jetzt schicht ;).
Ich werde mir mal ein C-Buch morgen aus der Bücherei besorgen / leihen.
Gn8 euch allen
Mit freundlichen Grüßen
Balou Baer
Balou Baer schrieb:> Ich werde mir mal ein C-Buch morgen aus der Bücherei besorgen / leihen.
Das ist eine gute Entscheidung. In deinem Code oben ist nämlich so
ziemlich alles falsch, was man falsch machen kann, wenn nicht noch mehr
;-)
Das ist aber kein Problem, denn jeder von uns hat mal bei null
angefangen. Man muss das nur einsehen und darf nicht erwarten, dass man
ohne jegliche Literatur sofort komplizierte Dinge anstellen kann. Gerade
in C führt das oft ins Chaos.
Joschua C. schrieb:> C-like wäre, wenn du dem UP nicht sagst wie die Zahlen sind, sondern wo> sie im Speicher stehen. Dann kann dir dein UP die Arbeit mit dem Werte> kopieren abnehmen.
Wie geht das? Ok wahrscheinlich mit Pointern -> lese ich mich dann
morgen durch, wenn ich ein C Buch habe
also das wäre die Funktion wenn es so gegangen wäre wie ich gedacht habe
^^:
Ich benötige zur Flankenerkennung an einem PIN des ATMega32, einmal den
ursprüngliche Zustand des PIN´s, den wollte ich mit "vorher_zaehltaster"
übergeben ud den neuen Zustand wieder zurückgeben. Und ich wollte eine
Zahl hoch / runterzählen. Also die Funktion oben und dann eine weitere
Funktion die uint8_T y_zaehler_runter (uint8_t zaehler, uint8_t
vorher_zaehltaster) heist.
Yalu X. schrieb:> Man muss das nur einsehen und darf nicht erwarten, dass man> ohne jegliche Literatur sofort komplizierte Dinge anstellen kann.
Sag das Bitte mal unseren Lehreren!
Ich bin nur ein Elektroinstallateur der sich etwas auf der Abendschule
weiterbilden will und habe vor dieser Weiterbildung mit programmieren
nichts zutun gehabt.
Die Lehrer meinen aber, da sitzt ein voll ausgebildeter IT´ler.
Tom schrieb:> Wenn die zwei Werte irgendwie zusammengehören, darf man auch beide> in> ein ein struct stecken und dieses returnen:typedef struct> tCoordinate whereami(void)> {> tCoordinate position;> position.x = 1.23;> position.y = 3.45;> return position;> }
Leider absolut falsch, genauso wie vom TO.
> tCoordinate position;
position darf zwar innerhalb der Funktion benutzt werden. ist aber nach
dem Verlassen undefiniert!
> return position;
Der Wert der zurückgegeben wird, zeigt auf einen ungültigen Bereich,
der sich jederzeit ändern kann.
Typischer Anfängerfehler, der zu einem instabilen Programm führt, das
dann irgendwann abschmiert oder Unsinn macht.
Also entweder:
static tCoordinate position;
damit der Speicherbereich erhalten bleibt oder global anlegen.
@Balou Baer bei deinem Wissensstand, ist es besser kleinere
Testprogramme zu schreiben, zu testen und die dann in das größere zu
Übernehmen. Wenn du soviel Code schreibst und später erst wenn sehr viel
da steht mit der Fehlersuche beginnst, wirst du erschlagen von den
Fehlern.
Ausserdem beschäftige dich mal mit der Parameterübergabe, "by
reference" und "by value", das könnte Dein Problem eleganter Lösen.
Was ist das denn für ein Quatsch? Die struct wird by-value zurückgegeben
und dabei kopiert. Das ist absolut okay so.
Vielleicht verwechselst du das mit dem by-reference return in C++, wo
man eine Referenz auf eine Stack-Variable zurückgibt, was dann
tatsächlich zu Fehlern führt. In C gibt es aber gar keine Referenzen
(deshalb natürlich auch kein pass-by-reference!).
Mit Pointern kann man zwar den gleichen Fehler machen, dann muss man den
aber explizit beim return machen (mit einem "&") und das habe ich
eigentlich noch nie gesehen.
Balou Baer schrieb:> Antworten nein, weil das ein Beispiel ist und das eigentliche> Programm> noch lange nicht fertig ist, Kompilieren unmöglich.
Daran solltest du vielleicht auch arbeiten. Die einzelnen Funktionen
soweit kapseln, dass sie möglichst einzeln getestet werden können. Das
können ja Dummy-Werte sein, die du dir selber ausdenkst und dann im
Hauptprogramm übergibst. Da bietet es sich dann unter Umständen auch gut
an, vorher nachzudenken welche Ergebnisse man überhaupt erwartet.
Gruß
Dennis
Mal davon abgesehen, das man daß irgendwie hinbiegen kann, das eine
Funktion zwei Rückgabewerte liefert.(was natürlich auch wieder falsch
ist, da es doch nur ein wert bleibt) Führt das doch, das ganze Konzept
einer Funktion mit Rückgabewert ad absurdum!
Wenn dann sollte der TO das richtig lernen, darum geht's doch!
Also, Globale Variablen oder call by value. Alles andere ist
Schwachsinn!
Teo Derix schrieb:> Also, Globale Variablen oder call by value. Alles andere ist> Schwachsinn!
mit starken Ausdrücken sollte man sich zurückhalten, aber:
nicht alles andere, sonderne deine Aussage ist Schwachsinn
Teo Derix schrieb:> Wenn dann sollte der TO das richtig lernen, darum geht's doch!
Genau.
Und richtig ist in diesem Fall die Erkenntnis, dass die beiden
Funktionalitäten, die er da in eine gemeinsame Funktion stecken will,
nichts miteinander zu tun haben
* einen Tastendruck zu erkennen ist eine Funktionalität
* aufgrund eines Tastendrucks einen Zähler zu erhöhen oder erniedrigen,
ist eine andere Funktinoalität
In seinem Fall stellt sich die Frage nach den beiden Rückgabewerten also
überhaupt nicht, weil sein grundsätzliches Programmdesign in erster
Linie schon daneben ist.
Was natürlich nicht heißt, dass man die Techniken, wie man Werte in eine
Funktion hinein und wieder heraus kriegt, links liegen lassen darf.
Peter schrieb:> mit starken Ausdrücken sollte man sich zurückhalten,
Dazu sag ich nur :P
>aber:> nicht alles andere, sonderne deine Aussage ist Schwachsinn
Hole Worte, füll sie doch mal bitte. Will ja nicht dumm sterben.
@Gaestchen
Was du da beschreibst, steht in der ersten Ausgabe vom K&R und galt
damals schon nur für einige Compiler.
Seit C89 kann man komplette structs als Rückgabewert von Funktionen
nehmen.
Sogar mit Arrays drin.
structs sind keine Arrays.
@Balou Baer
Dein Array zaehler_vorhertaster ist eine automatische (lokale) Variable.
D.h. sie wird bei jedem Funktionsaufruf neu angelegt und beim verlassen
zerstört.
Alte Werte bleibe da nicht erhalten.
Daniel A. schrieb:> um mehrere rückgabewerte zu simulieren
Mit "Simulieren" hat das nichts zu tun, das nennt sich "call by
reference".
In Deinem Code sollte allerdings überprüft werden, ob die Pointer keine
Null-Pointer sind:
Teo Derix schrieb:> Mal davon abgesehen, das man daß irgendwie hinbiegen kann, das eine> Funktion zwei Rückgabewerte liefert.(was natürlich auch wieder falsch> ist, da es doch nur ein wert bleibt) Führt das doch, das ganze Konzept> einer Funktion mit Rückgabewert ad absurdum!>> Wenn dann sollte der TO das richtig lernen, darum geht's doch!> Also, Globale Variablen oder call by value. Alles andere ist> Schwachsinn!
Genau das Gegenteil ist der Fall. Globale Variablen und die hässlichen
"Output Parameter" untergraben das Konzept von Funktionen. Die
ursprüngliche Idee ist doch, dass eine Funktion funktioniert wie in der
Mathematik, also ohne Nebeneffekte. Es gibt auch Sprachen, die das
konsequent so umsetzen.
Dass das nicht immer sinnvoll ist, vor allem in C, ist klar, aber zu
behaupten es sei absurd Funktionen so zu benutzen ist ... absurd. Das
ist die beste Art, Funktionen zu benutzen: ohne Nebeneffekte, nur n
Inputs auf m Outputs und die Inputs sind die Parameter und die Outputs
die Rückgabewerte.
Teo Derix schrieb:>> nicht alles andere, sonderne deine Aussage ist Schwachsinn> Hole Worte, füll sie doch mal bitte. Will ja nicht dumm sterben.
OK, wenn Du das nicht kannst mach ich's halt selber :)
Die Aussage
Beitrag "Re: Rückgabe von ZWEI Werten aus einer Funktion"
ist definitiv Schwachsinn!
Natürlich gibt es genügend Gründe, warum globalen Variablen oder Call by
Value nicht wünschenswert ist!
Da bleiben halt nur Structs, Arrays oder Zeiger, als Rückgabewert. Ob
andere Krücken sinnvoll sind ???
PS: Wahr wohl zu sehr auf das Problem des TO fixiert :(
Karl Heinz schrieb:> Und richtig ist in diesem Fall die Erkenntnis, dass die beiden> Funktionalitäten, die er da in eine gemeinsame Funktion stecken will,> nichts miteinander zu tun haben> * einen Tastendruck zu erkennen ist eine Funktionalität> * aufgrund eines Tastendrucks einen Zähler zu erhöhen oder erniedrigen,> ist eine andere Funktinoalität
Das ist völlig richtig, löst aber das eigentliche Problem nicht.
Die ursprüngliche, "All-in-One"-Funktion liefert zwei Ergebnisse,
nämlich den letzten Tastenstatus (gedrückt oder nicht gedrückt) und den
Zählerstand.
Teilt man die Funktion in zwei Funktionen (Flankenerkennung und Zählen)
auf, hat die Flankenerkennung ebenfalls zwei Ergebnisse, nämlich den
letzten Tastenstatus und das Flankenereignis (Flanke erkannt oder
nicht erkannt).
Wie man es auch dreht und wendet, das grundsätzliche Problem bleibt
bestehen. Natürlich gibt es viele Lösungen dafür, aber leider keine, die
man guten Gewissens einem Anfänger als die richtige empfehlen könnte.
Die letztendlich getroffene Wahl hängt meist vom jeweiligen Kontext und
vom persönlichen Geschmack ab.
Die meisten neueren Programmiersprachen übernehmen deswegen zwei
Konzepte aus der funktionalen Programmierung, nämlich Tuples und
Pattern-Matching, die das Problem auf elegante Weise lösen.
Auch in der objektorientierten Programmierung gibt es bessere Lösungen
als in C: Da würde man eine Klasse Taste mit dem letzten Tastenstatus
als Membervariable anlegen. Auf diese Membervariable haben die einzelnen
Methoden der Klasse Zugriff, wodurch man die gleichen Möglichkeiten wie
mit globalen Variablen, aber mit eingeschränktem Scope, erhält.
Yalu X. schrieb:> Auch in der objektorientierten Programmierung gibt es bessere Lösungen> als in C: Da würde man eine Klasse Taste mit dem letzten Tastenstatus> als Membervariable anlegen. Auf diese Membervariable haben die einzelnen> Methoden der Klasse Zugriff, wodurch man die gleichen Möglichkeiten wie> mit globalen Variablen, aber mit eingeschränktem Scope, erhält.
Ach, und seit wann geht das in C nicht?
Hallo!
Na du merkst sicher, es geht hier um ein Thema in dem persönliche
Vorlieben eine große Rolle spielen. Meine Erfahrung in der Entwicklung
hat mir gezeigt, daß es wichtig ist Code lesbar zu halten, damit man
auch noch nach zwei Monaten weiß was man da gemacht hat. Deshalb gefällt
mir persönlich von den genannten Lösungen jene mit einer struct am
besten. Außerdem hat ein struct den Vorteil, daß du sie jederzeit
erweitern kannst und damit deinen Code modular hältst. Ob du die Instanz
deiner Variablen dann global oder irgendwo lokal hältst, ist nicht so
wichtig und hängt meiner Meinung stark vom Verwendungszweck ab. Hier
mein Beispiel zur Implementation mit struct, das sich in Atmel Studio
6.2 auch kompilieren lässt:
1
structZweiWerte{
2
uint8_tWert1;
3
uint8_tWert2;
4
};
5
6
voidMachWas(ZweiWerte*werte)
7
{
8
werte->Wert1++;
9
werte->Wert2++;
10
}
11
12
intmain(void)
13
{
14
ZweiWertewerte;
15
werte.Wert1=1;
16
werte.Wert2=2;
17
MachWas(&werte);
18
}
Dein Ansatz mit dem array kann natürlich auch funktionieren. Dabei musst
du jedoch darauf achten, daß du den reservierten Speicher auch wieder
frei gibst. Außerdem ist es wesentlich schwieriger aus dem Code die
Bedeutung jedes einzelnen Wertes raus zu lesen und auch die Erweiterung
wird sich in Folge schwierig und fehleranfällig gestalten. Eine mögliche
Implementierung:
Remo F. schrieb:> Eine mögliche Implementierung:
Bitte nicht nachmachen!
(Funktion reserviert Speicher, der außerhalb der Funktion freigegeben
werden muss...)
Schaulus Tiger schrieb:> Ach, und seit wann geht das in C nicht?
Ja, prinzipiell geht das auch in C. Ich vermute, du spielst darauf an,
der Funktion einen Zeiger auf eine Struktur mit den Membervariablen zu
übergeben, so wie es weiter oben schon von Remo F. vorgeschlagen wurde.
Die C++-Variante mit einem Objekt und Private-Membervariablen ist halt
etwas gediegener, weil sie die Membervariablen vor dem Anwender der
Funktionen versteckt und so weniger Freiraum für (unabsichtliches)
Schindluder lässt.
Servus!
Meinem Verständnis nach war die ursprüngliche Frage wie man aus einer
Funktion mehrere Werte zurück gibt. Es gibt in der Entwicklung immer
mehr als einen Lösungsweg für eine Problemstellung. Auch einzelne
globale Variablen sind möglich. Allerdings würde ich eher eine globale
struct machen, damit ich durch die Eingabehilfe der IDE auch immer einen
Überblick über diese hab.
Grüße!
Remo
Schaulus Tiger schrieb:> ich meinte sowas in dieser Art, keine Ahnung, ob "man" das so macht:
Ja, das macht man oft so, ich selber übrigens auch.
Das Programmmodul mit ein paar Funktionen und globalen Variablen hat
viele der Eigenschaften einer OOP-Klasse, von der genau ein Objekt
instanziiert wird. Durch "static" kann man Variablen oder Funktionen
sozusagen "private" machen, da sie außerhalb des Mopduls nicht sichtbar
sind.
Der Hauptnachteil dieser Methode besteht darin, dass es von der "Klasse"
nur eine einzige Instanz gibt. Wenn im konkreten Beispiel mit dem Taster
noch ein zweiter hinzukäme, würde diese Lösung nicht mehr funktionieren.
Remo F. schrieb:> Allerdings würde ich eher eine globale struct machen
Das ist dann aber kein "Rückgabewert" einer Funktion, sondern nichts
besseres als eine globale Variable.
Und so etwas ist in 99.95% aller Fälle Murks.