Forum: Mikrocontroller und Digitale Elektronik Wie kann ich ein zwei werte zurückgeben


von bjk (Gast)


Lesenswert?

Hallo Leute,
folgende Frage habe ich wie kann ich in einer Unterfunktion zwei Werte
zurückgeben unswar als double. Ich habe überlegt das ganze mit pointern
zu realisieren doch dann kann ich nur int werte zurückgeben kann mir
einer helfen unten habe ich den code aufgeschrieben.

int *mlMUlU32(unsigned int reg1,unsigned int reg2);
main()
   {
    double *produkt;
    unsigned reg2= 5;
    unsigned reg1= 2
    produkt =mlMulU32(reg1,reg2);
    printf("\n Die Ausgabe der Multiplikation ergibt Produkt= %u und
%u",produkt1[0],produkt1[1]);

   }

int *mlMlu32(unsigned int reg1,unsigned int reg2)
{
 double *array[2];
 unsignet int temparray[2];
 double reg3;
 // Beide Werte werden zu double gecastet
 reg1 = (double)reg1;
 reg2 = (double)reg2;

 reg3 = reg1*reg2;
 temparray[0] = reg2;
 temparray[1] = reg3;
 array[0] = temparray[0];
 array[1] = temparray[1];
 return array;


}

So funktioniert das ganze, nur das ich als ausgabe zwei integer Werte
kriege ich möchte aber double werte haben...die Ausgabe in
printf("..") habe ich hier als %u unsigned variable wenn ich diese in
%f ändere bekomme ich für beide werte Null. Kann mir einer weiter
Helfen?

Gruss
Bjk

von bjk (Gast)


Lesenswert?

sorry die Ausgabe müsste so aussehen

    printf("\n Die Ausgabe der Multiplikation ergibt Produkt= %u und
%u",produkt[0],produkt[1]);

von Khani (Gast)


Lesenswert?

Hallo bjk,

probiers mal mit

void blabla(int param1, int& param2)
{
   param1 = param1 + 1;
   param2 = param2 + 2;
}

int main(void)
{
   int p1 = 5, p2 = 5;
   cout << p1 << " " << p2 << endl;
   blabla(p1, p2);
   cout << p1 << " " << p2 << endl;
   return 0;
}

Jetzt wird ausgegeben:
2 5
2 6

Mit dem '&' im Funktionskopf von blabla(..) wird von dieser Variable
keine lokale Kopie angelegt, sondern mit dem Original gearbeitet. Mit
dem anderen Parameter läuft es wie gehabt.

Modifikationen in der Funktion haben nur auf param2, bzw. p2 eine
Wirkung im Hauptprogramm. p1 wird nicht verändert, obwohl param1
modifiziert wurde.

MfG, Khani

von bjk (Gast)


Lesenswert?

Hallo khani,

Danke für die Antwort,das sieht gut aus nur ist das nicht was ich
wollte das jetzt bei meinem Code reg2 in der UNterfunktion benutzt wird
ist nur zufall..folgendes:

ich habe zwei eingangsparameter als integer, diese wandle ich in double
um und berechne in der Funktion Zwei ganz neue Werte und will diese
zwei Werte als double zurückgeben.

Gruss
Bjk

von bjk (Gast)


Lesenswert?

kann mir den keiner helfen?

von Benjamin (Gast)


Lesenswert?

Also ich hab von C nicht so viel Ahnung, aber wenn ich mich richtig
erinnere ging das so in der Art:

void plus_eins(double* zahl1, double* zahl2)
{
    *zahl1 += 1
    *zahl2 += 1
}

int main()
{
    double zahl1 = 0, zahl2 = 0;
    plus_eins(&zahl1, &zahl2);

    return 0;
}

Nun müßte zahl1 und zahl2 meine ich gleich 1 sein.

Du übergibst der Funktion zwei Doubles, welche du vorher erstellt hast.
Die Funktion ändert diese Zahlen über Pointer auf sie nun direkt. Somit
kann man soeit ich weiß mehr als "Funktionsergebnisse" bekommen.

Bitte verbessert mich bei bedraf.

von bjk (Gast)


Lesenswert?

Hallo Benjamin,

Danke hat so funktioniert das bedeutet wohl das in C kein rückgabetyp
return definiert werden kann oder geht das auch anders?

gruss
Bjk

von bjk (Gast)


Lesenswert?

ach zu früh gefreut, dise Funktion geht nur wenn die Eingabeparameter
double sind, ich wollte aber zwei integer Werte als Eingabeparameter
haben, diese in double in der Funktion berechnen und als Double zwei
verschiedene Werte ausgeben als die Eingabeparameter. Der Vorschlag von
Benjamin aber verändert nur die Werte von zahl1 und zahl2..das sind
aber die eingangswerte folgend sollte der Inhalt der Funktion sein

void plus_eins(int* zahl1, int* zahl2)
{
    // Beide Zahlen in double umwandeln
    zahl1 = (double) zahl1;// weiss nicht ob das so geht
    zahl2 = (double) zahl2;
    double zahl3;
    zahl3 = zahl1*zahl2;
    zahl2 = zahl1/zahl2;

    // Jetzt als return wert (Hier ist die Funktion void also kein
returnwert möglich)
    return //beide werte (zahl3,zahl2) als double.

}

Ist das möglich oder nicht?

gruss
Bjk

von Khani (Gast)


Lesenswert?

Natürlich, auch wenn Du mir nicht geglaubt hast.

Schreib :

void blablubb(int zahl1, int zahl2, double& dzahl2, double& dzahl3)
{
  double dzahl1 = double(zahl1);
  dzahl2 = double(zahl2);
  dzahl3 = dzahl1*dzahl2;
  dzahl2 = dzahl1/dzahl2;
}

int main(void)
{
  ...
  blablubb(1, 2, x, y);
  ...
  return 0;
}

Das ergebnis steht in den double-Zahlen x und y. Ich empfehle Dir, mal
ein gutes Buch zu C/C++ in die Hand zu nehmen, weil es so scheint, als
wärst Du noch nicht sonderlich fit auf diesem Gebiet. Ist ein gut
gemeinter Tip und kein Zusammenstauchen. Mit einem Buch und ein bißchen
Übung hat mehr von den Antworten hier. Denn dann versteht man auch
wirklich, was die Leutchen so meinen.

MfG, Khani

von Stefan Kleinwort (Gast)


Lesenswert?

Schau mal in einem C-Tutorial oder Buch nach dem Unterschied zwischen:

call by value     und
call by reference


Stefan

von Lukas H (Gast)


Lesenswert?

Man könnte es auch mit einem struct machen...

von Khani (Gast)


Lesenswert?

@ Lukas :
Structs oder Klassen sind hier natürlich prinzipiell auch möglich, man
sollte sie jedoch nur verwenden, wenn man
a) sich keine Gedanken über die Verwaltung derselben im Speicher und
den damit verbundenen Speicher-/Performanceverbrauch machen muss
b) einen inhaltlichen Zusammenhang zwischen den Größen deutlich machen
will.

Auf einem Mikrocontroller ist die Methode mit dem "call by reference"
oder die Arbeit mit der Originalspeicherstelle (wie von mir
vorgeschlagen) besonders nett, da man nicht erst neue Speicherstellen
verwalten muss, sondern einfach mit den alten arbeiten kann. Ganz
nebenbei kann man sogar noch den Rückgabewert der Funktion für eine
Statusrückgabe der Funktion (Fehlschlag/Überlauf...) benutzen.

Auf dem PC (wo die Performance nach dem Moore'schen Gesetz eh völlig
egal ist) kann man natürlich der Einfachheit halber auch Structs nehmen
und hinterher eventuelle Probleme abfangen.

MfG, Khani

von Benjamin (Gast)


Lesenswert?

Soweit ich weiß gibt es in C nicht den Referenzoperator &. Dieser gehört
meines Wissens zu C++. Wenn du in C++ programmieren willst kannst auch
den benutzen. Sonst hast du in C nur die Möglichkeit über die Pointer.
Da bedeutet das "*" die Variable auf die der Pointer zeigt und beim
Funktionsaufruf (void foo(&zahl)) bedeutet das "&" die Adresse der
Variablen.
Das du Integer haben willst zum berechen dürfte das durch einen Cast
funktionieren.
Beispiel:

void foo(double* zahl)
{
   int i = (int)*zahl);
   int z = i/3;
   *zahl = (double)z;
}

int void main()
{
    double x = 6;
    foo(&x);
    x += 0.1
    Ausgaben(*x);  // Ausgabe() gibt z.B. denn Inhalt von *x
                   // auf den Bildschirm aus

    return 0;
}

*x müßte nun als 2.1 ausgegeben werden.

von Benjamin (Gast)


Lesenswert?

"int i = (int)*zahl);" müßte natürlich "int i = (int)*zahl;" heißen.
Also ohne die letzte Klammer. :)

von Benjamin (Gast)


Lesenswert?

Oh sorry. Haben deine Message nicht komplett gelesen. sorry.
Dein Beispiel, bjk, kannst du so ändern:

void plus_eins(double* zahl1, double* zahl2, double* zah3)
{
    zahl3 = (double) zahl1*zahl2;
    zahl2 = (int) zahl1/zahl2;
}

und der Aufruf:
plus_eins(&((double)eins), &((double) zwei), &((double)drei));

Hoffe das ist richtig und hilft dir.

von Jörg Wunsch (Gast)


Lesenswert?

@Khani:

> Structs oder Klassen sind hier natürlich prinzipiell auch möglich,
> man sollte sie jedoch nur verwenden, wenn man

> a) sich keine Gedanken über die Verwaltung derselben im Speicher und
> den damit verbundenen Speicher-/Performanceverbrauch machen muss

Davon abgesehen, daß structs und Klassen zwei sehr verschiedene Dinge
sind (da es Klassen nur in C++ gibt und sie dort einiges mehr
veranstalten können als structs in C), mußt Du mir das mal erklären.

Kann natürlich sein, daß ein bestimmter Compiler ineffizienten Code
erzeugt für die Rückgabe einer struct, aber das ist dann ein Problem
des Compilers, nicht der Methode.  Zumindest ein AVR-GCC erzeugt für
die Rückgaben einer struct fast identischen Code zu dem Fall, daß man
stattdessen einen struct * übergeben würde.  Außerdem habe ich im Ohr,
daß Atmel selbst (natürlich aus deren Sicht für den IAR, müßte aber
für GCC auch passen) die Benutzung von structs als effektive
Programmierung angepriesen hat (hat mal jemand von einem Seminar
berichtet, meiner Erinnerung nach in de.sci.electronics), da der
Compiler dann Offset-Index-Adressierung benutzen kann, die effektiver
ist als jedesmal die komplette Adresse laden zu müssen.

von Khani (Gast)


Lesenswert?

Hallo Jörg,

ich möchte dazu nur so viel sagen :

Der Unterschied zwischen Klassen und Structs ist lediglich die
Kapselung. Sonst gibt es keinen. (Außer daß sie vielleicht in
verschiedenen Programmiersprachen existieren.)

Falls Du noch eingehender über das Thema mit mir diskutieren möchtest,
kannst Du mir gerne eine Mail zum Thema schreiben, denn ich glaube
nicht, dass eine solche Diskussion hier im Forum a) komplett verstanden
wird und b) arg vielen Leuten was bringt.

MfG, Khani

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

"Zumindest ein AVR-GCC erzeugt für die Rückgaben einer struct fast
identischen Code zu dem Fall, daß man stattdessen einen struct *
übergeben würde."


Der war jetzt nicht ernst gemeint :-)


Anbei mal ein Beispiel dafür.
Die Funktion test1() mit struct als Returnwert belegt riesige 452% des
Code im Vergleich zur Pointer Version test2().

Und auch deren Aufrufer muß noch stolze 162% an Code erzeugen.


Das Problem ist nämlich, daß man in der Returnanweisung keine
mehrfachen Zuweisungen machen kann.
Man muß also erstmal eine lokale Instanz der Struktur anlegen und die
Returnanweisung muß daß dann umständlich kopieren.

Ebenso muß der Aufrufer erstmal umständlich einen Platz für eine 2.
Kopie anlegen, damit dort die Returnanweisung etwas reinschreiben kann,
was dann der Aufrufer wieder in das Original zurückkopiert.

Daher auch noch mindestens 3-facher RAM-Bedarf (Original + 2 Kopien).


Peter

von Khani (Gast)


Lesenswert?

Danke Peter, Khani

von Stefan Kleinwort (Gast)


Lesenswert?

Da muss ich Dir widersprechen, Khani:

Gerade solche Diskussionen finde ich hier am interessantesten. Die
heben sich wohltuend ab von den endlosen Entprell-Diskussionen ;-)
Also wenn Ihr das per Mail ausmacht, dann bitte auch cc an mich.

Ich persönlich denke, dass die Parameterübergabe mittels eines
Struct-Pointer eine der effektivsten Methoden ist. Was gcc macht, wenn
man eine struct call-by-value übergibt, habe ich noch nicht
ausprobiert.

Stefan

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Hier nochmal das Ganze mit Pointer auf die Struktur. Spart dann nochmal
ein kleines bischen Code (4 Worte).

"Ich persönlich denke, dass die Parameterübergabe mittels eines
Struct-Pointer eine der effektivsten Methoden ist."

Was hiermit bewiesen ist.

Wenn man die -> Schreibweise verstanden hat ist das auch kein Problem
mehr.


Peter

von MarkusS (Gast)


Lesenswert?

Hallo,

mittles Parameterübergabe durch Strukturen hat man auch den Vorteil das
man die Anazhl der Parameter relativ leicht Ändern kann.
Man braucht dann nur in der Stuktur den Parameter zu ändern und an der
Stelle an dem man mit dem Parameter arbeitet. Die Übergabeschnittstelle
bleibt dann immer gleich und man braucht nicht immer den
Funktionsprototyp und den Funktionskopf anzupassen.

Gruß

MarkusS

von Andreas Grimm (Gast)


Lesenswert?

Hi,

Man sollte sich bei der Auswahl der Methoden auch immer mit an der
Lesbarkeit der Quellen orientieren. Das heist, das man eine Structur
nur dann verwenden sollte wenn die einzelnen Werte auch logisch
zusammengehören. Ansonsten ist call by reference mit einem Errorcode
als Ergebnis die günstigere Variante.
Übrigens theoretisch giebt es klassen nur bei C++, aber einige
C-Compilier verstehen das Schlüsselwort class trotzdem. In C++ ist der
Unterschied das Elemente von Klassen standardmäßig private sind und bei
Strukturen public. Will man das jeweils andere erreichen oder die
Element preotected deklarieren muss man das explizit angeben. Da in der
OOP ein Grundprinzip die Datenkapselung ist verwendet man meist
Klassen.

Andreas


Andreas

von Jörg Wunsch (Gast)


Lesenswert?

@Peter:

Tja, Peter, wenn Du schon degenerierte Testfälle benutzt, dann wirf
bitte nicht mit Prozentzahlen herum, wenn der Unterschied am Ende ein
paar Bytes beträgt.

Man kann mit beliebig sinnlos konstruierten Testbeispielen natürlich
am Ende alles beweisen. ;-)  Einen double anzulegen, um ihn dann im
Aufrufer in int wandeln zu lassen (dem Gerufenen jedoch damit das
aufwendige Registerreten für double anzulasten), halte ich schon für
etwas schräg.

Dein Testbeispiel 2 vergleicht Äpfel mit Birnen (struct gegen call by
reference), das lasse ich daher gleich erstmal außen vor -- der
Fragesteller hatte explizit nach structs gefragt.  Ob dies nun für die
Datendarstellung sinnvoll ist oder nicht, mag er selbst entscheiden.
Wie andere schon schrieben, haben beide Ansätze ihr Für und Wider.
Meine Bemerkung, daß der AVR-GCC ein struct return wie eine Referenz
auf eine struct behandelt, bezog sich ausdrücklich nicht darauf, daß
man es auch ohne struct machen könnte.

Deine Testerei sieht dann für Dein double-Beispiel so aus
(-mmcu=atmega128, -Os):

/* function test1 size 72 (37) */
/* function caller1 size 76 (41) */
/* function test3 size 18 (17) */
/* function caller3 size 45 (26) */

also 148 Worte für die struct-Rückgabe gegen 53 Worte für die übergabe
des struct *.

Nun dreh' mal das double auf unsigned char (schließlich willst Du es
ja am Ende nur auf einen Port ausgeben), dann hast Du:

/* function test1 size 3 (2) */
/* function caller1 size 5 (4) */
/* function test3 size 6 (5) */
/* function caller3 size 27 (8) */

8 Worte für Lösung 1 gegen 33 Worte für Lösung 3, oder einen Overhead
von 400 % (:-) Deiner bevorzugten Variante.

So viel zur Aussage eines degenerierten Testbeispiels. ;-)

Fazit: es muß sich schon jeder für seine ganz konkrete Applikation
eine Rübe machen, welche der Methoden am effektivsten ist.

Btw: #include <avr/io.h>, nicht #include <io.h>.  <io.h> ist nicht von
irgendeinem Standard abgedeckt, daher steht er im Unterverzeichnis
avr.

@Khani:

> Der Unterschied zwischen Klassen und Structs ist lediglich die
> Kapselung. Sonst gibt es keinen.

In C++, weil dort auch eine struct letztlich eine Klasse ist, ggf. mit
allem Overhead, der daraus resultieren kann.

In C ist eine struct als solches nicht schwergewichtig.  Sie kann
keine Methoden haben, landet niemals in einer virtuellen
Methodentabelle, ruft keine Konstruktoren beim Anlegen oder
Destrukturen am Ende des Scopes etc. pp.  Insofern kann ich der
generellen Aussage ,,Wer structs im Mikrocontroller benutzt, bläht
seinen Code ja automatisch künstlich auf.'', die da von Dir
rüberkam,
nicht folgen.

Man kann sicher auch in C++ mit structs oder Klassen vernünftig und
speichersparsam umgehen (schließlich gibt es auch Bemühungen um eine
Standardisierung eines Subsets von C++ für embedded applications),
aber es gehört schon ein wenig Kenntnis des Verhaltens verschiedener
Sprachelemente dazu -- sonst kein ein vergessenes &-Zeichen großen
Schaden anrichten.

von Khani (Gast)


Lesenswert?

Mensch Jörg,

weniger aufregen - länger leben.

Ich kenne 1. den Unterschied zwischen C und C++ und 2. auch die beiden
Sprachen recht genau (auch bezgl. dem was hinten rauskommt).

Also : Du hast Deine Meinung, ich habe meine und ich akzeptiere Deine
Einstellung, auch wenn ich nicht so verfahre.

MfG, Khani

P.S.: Immer diese Glaubenskriege ;-)

von Peter D. (peda)


Lesenswert?

@Jörg,

ich hatte deshalb "double" genommen, weil das ja oben so gefordert
wurde.

Und die Zuweisung zu den Ports habe ich nur zur Sicherheit gemacht,
damit mir nicht der ganze Code wegoptimiert wird.

Das es bei "char" nun ganz anders aussieht ist wirklich verblüffend.


Peter

von Jörg Wunsch (Gast)


Lesenswert?

Ich hatte mich mit ,,degenerierter Testfall'' auch nur darauf
bezogen,
daß Deine Prozentzahlen letztlich nur daraus resultierten, daß die
eine Lösung im Vergleich zur anderen in dieser zusammengestrichenen
Version vor allem ihren Aufwand im umständlichen Retten von sackweise
Registern verplempert hat, gar nicht so sehr im eigentlichen Code.

Wenn man nun statt des konstruierten Testfalles ein real world example
nimmt, ist das Retten der Register sehr wahrscheinlich ohnehin schon
nötig, damit dürften die Unterschiede zwischen beiden Verfahren in der
Praxis minimal sein.

Ich würde übrigens selbst auch nicht auf die Idee kommen, eine struct
zurückzugeben (dazu kenne ich halt auch noch die Zeiten von K&R C zu
gut ;-), ich war auf die meiner Meinung nach doch recht elegante
Variante der struct-Rückgabe (die ja letztlich ein call by reference
ist) nur gestoßen, als ich mir letztens mal angesehen habe, wie der
AVR-GCC das denn überhaupt abhandelt.  Hatte mich interessiert, da
Atmel ursprünglich ein ELF-ABI veröffentlichen wollte und mich um eine
Zuarbeit dazu gebeten hat.  (Allerdings sind die Unterschiede zwischen
den verschiedenen Compilern bezüglich ihrer Strategie zu groß, so daß
ein ABI keinen Sinn hat.  Naja, ist zumindest auch ein Ergebnis.)

von Peter D. (peda)


Lesenswert?

Ich bevorzuge in der Regel die Variante 2 wegen Schreibfaulheit.
Die Variante 1 hatte ich hier das erste mal ausprobiert.

Es gibt allerdings noch eine 4. Variante, nämlich globale Variablen.

Das wird auch öfter gemacht, z.B. gibt es oft eine globale Variable
"Error", in die dann verschiedene Funktionen ihr Mißlingen
reinodern.

Es bleibt dann dem Programmierer überlassen, wie spät oder früh er das
Mißlingen einer oder mehrerer Funktionen prüfen will. Durch das Odern
bleiben die Fehler ja bis zum Test der Variable erhalten.


Peter

von fale20 (Gast)


Lesenswert?

In C kannst du nur immer ein Wert aus einer Funktion zurückgeben, egal
welcher Dateityp. Das einfachste ist, das du die Variablen die du
zurückgegen willst global, sprich vor deiner main funktion deklarierst.
Dann kannst du dir das ganze Spielchen mit Pointer und Structuren
sparen.

von Jörg Wunsch (Gast)


Lesenswert?

Och nee, nicht wieder alles von vorn...

Es sind alle Möglichkeiten doch schon diskutiert worden.  Globale
Variablen sind eine davon, aber bei weitem nicht die einzige und auch
nicht immer ,,die einfachste''.

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
Noch kein Account? Hier anmelden.