Hallo,
bin an einem Menü mit Pointern dran, dass switchen im Menü funktioniert
schon sehr gut. Was bei mir noch nicht klappt ist, dass er die
Funktionen nicht aufruft.
pgm_read_word(&test_m[state].funktion) liefert dir bestenfalls
einen Zeiger auf eine Funktion, ruft sie aber sicher nicht auf.
Dazu fehlt noch der cast auf den richtigen Typ (Zeiger auf
Funktion, die <parameterliste> bekommt und <Rückgabetyp> liefert)
sowie die runden Kalmmern, die ein Funktionsaufruf braucht.
Außer bei dem letzten Eintrag fehlen übrigens die Zeiger auf
die Funktionen, und überhaupt ist der Quelltext doof zu lesen.
Mies formatiert, unvollständig, nicht kompilierbar.
Na und?
Die Bruchstücke dort funktionieren für sich alleine auch nicht.
Was soll mir der Link sagen?
Du musst dich schon mit C beschäftigen, wenn du mit
Funktionszeigern arbeiten willst, und nicht nur irgendwelche
Fragmente zusammenkopieren.
Klaus Wachtler schrieb:> Mies formatiert, unvollständig, nicht kompilierbar.
Mies formatiert?
unvollständig -> ja ist richtig, gut erkannt
nicht kompilierbar -> richtig, da er ja nicht vollständig ist
hat schon einen Grund warum ich nicht alles gepostet habe!
Klaus Wachtler schrieb:> Was soll mir der Link sagen?Klaus Wachtler schrieb:> ORIGINAL-Quelltext
Wenn in den paar Zeilen von dir schon 2 Semikolon fehlen, von
verwendeten Variablen keine Definition oder Initialisierung zu
sehen ist, und als Fehlerbeschreibung nur ein "geht nicht",
dann brauche ich doch gar nicht groß Fehler zu suchen.
Wenn es zu groß ist zum Posten, dann liegt es an dir, das
Ganze auf ein kleines übersichtliches Beispiel zu
reduzieren, zu dem man dann Fragen beantworten kann.
Abgesehen davon, daß meine Aussage nach wie vor steht:
in der letzten gezeigten Zeile hast du nur ein Word,
das erst gecastet werden muß und dann mit Klammern erst
einen Funktionsaufruf gibt.
Wenn dann auch noch ein Semikolon dahinter spendiert wird...
Tip:
So etwas kann man am PC wesentlich einfacher erst mal üben.
Wenn man dann die Zeiger auf Funktionen verstanden hat,
packt man es in einen MC, in den man nicht so gut reinschauen
kann zw. Debuggen.
Das spart dir und uns viel Zeit.
Es gibt hier etliche Leute, die gerne helfen. Mich eingeschlossen.
Aber seine Zeit hat keiner gestohlen, also kannst du auch den
Hintern hochkriegen und nicht einfach irgendwas zusammenkopieren
und hier dann fertigstellen lassen.
Den Hauptfehler habe ich dir doch schon gesagt.
(uint8_t)pgm_read_word(&test_m[state].funktion);
was du mit Funktionsklammern meinst verstehe ich nicht, wenn du das
meinst:
(uint8_t)pgm_read_word(&test_m[state].funktion());
ist das Falsch.
ja, das ist falsch.
Du musst erst in den Typ "Zeiger auf Funktion ohne Parameer und
void-Rückgabewert" casten, dann Klammern drumrum und das Ganze
dann mit einem Klammerpaar dahinter aufrufen.
Ich habe jetzt keine Lust, einen AVR dafür auszupacken.
Aber auf einem PC kann man das wie gesagt auch schön simulieren.
Da sind Zeiger nur halt 4 Byte groß und deshalb nehme ich eine uint32_t:
1
#include<stdio.h>
2
#include<stdint.h>
3
4
5
voidf()
6
{
7
printf("hi\n");
8
}
9
10
intmain(intnargs,char**args)
11
{
12
uint32_tfi=(uint32_t)f;
13
14
((void(*)())fi)();// Aufruf von f über den Zeiger bzw. die ganze Zahl fi
15
16
return0;
17
}
fi ist jetzt uneleganterweise eine ganzzahlige Variable, das
entspricht bei dir dem uint16_t-Ausdruck
pgm_read_word(&test_m[state].funktion).
fi ist also formal nur eine ganze Zahl (auch wenn ich weiß,
daß sich dahinter in Wirklichkeit die Adresse einer Funktion
verbirgt).
Aus dieser ganzen Zahl muß jetzt für den Compiler erstmal
ein Zeiger auf eine Funktion gemacht werden.
Dazu schreibt man den richtigen Typ in Klammern davor,
gemeinhin als cast bekannt (ähnlich wie ich bei der Initialisierung
von fi aus der Adresse der Funktion f mit (uint32_t) davor
eine ganze Zahl gemacht hatte, nur jetzt halt in die andere
Richtung).
Was ist der richtige Typ?
Das wäre in Worten "Zeiger (also: *) auf eine Funktion
(deshalb die runden Klammern am Ende), die nichts bekommt
(deshalb die runden Klammern am Ende leer) und nichts
zurückliefert (deshalb void vornedran)".
In C:
void(*)()
Das wäre also der Typ, in den gecastet werden muss.
Das erste Klammerpaar um * ist nötig wegen der
Auswertungsreihenfolge, aus void und * würde der Compiler
sonst einen Typ void* machen und mit dem Rest dann nicht mehr
klar kommen.
Dieser Typ in Klammern vor den uint16_t- bzw. uint32_t-
Ausdruck macht aus der ganzen Zahl den Zeiger auf die Funktion:
(void(*)())fi
Das ist insoweit erstmal nur die Adresse der Funktion, aber noch
kein Aufruf.
Dazu muß man das bisherige Gebilde klammern (wieder wegen der
Auswertungsreihenfolge):
((void(*)())fi)
und -jetzt kommt's- mit einem Klammerpaar dahinter die
Funktion aufrufen:
((void(*)())fi)()
Sieht etwas kryptisch aus, aber ist halt C.
erstens macht man für eine neue Frage einen neuen Thread auf,
zweitens gefällt die meine Formierung nicht, und
drittens soll das letzte time0.hour = 2 wohl eher time1.hour = 2 heißen?
Klaus Wachtler schrieb:> erstens macht man für eine neue Frage einen neuen Thread auf,> zweitens gefällt die meine Formierung nicht, und> drittens soll das letzte time0.hour = 2 wohl eher time1.hour = 2 heißen?
1. Wollte ich das Forum hier nicht zu müllen
2. Genau deine Formierung find ich nicht toll, aber was hat es mit der
Frage zu tun?
3. Sollte es
>((void(*)())fi)();// Aufruf von f über den Zeiger bzw. die ganze Zahl
14
>fi
15
>
16
>return0;
17
>}
18
>
>> wie genau funktioniert es, wenn ich Parameter an der Funktion habe? test> hat parameter bla, wie genau ändert sich der Aufruf der Funktion?
Was dir bis jetzt noch keiner gesagt hat:
Mach dir um Himmels Willen einen typedef für den Datentyp des
Funktionspointers. Ansonsten bringt dich die komplexe Syntax um!
Die wichtigsten Dinge über Funktionspointer findest du hier
http://www.mikrocontroller.net/articles/FAQ#Funktionszeiger
zusammengestellt. Und natürlich auch ausführlich in deinem C-Buch.
Hier definiere ich das die Funktionen bei
{m_300, 1, 0, 4, 4, test}
keine Argumente enthält. Doch einige Funktionen brauchen ein Argument.
Ich kann ja nicht void (*funktion)(void); einfach ändern.
Stier schrieb:> Hier definiere ich das die Funktionen bei>> {m_300, 1, 0, 4, 4, test}>> keine Argumente enthält. Doch einige Funktionen brauchen ein Argument.> Ich kann ja nicht void (*funktion)(void); einfach ändern.
Das kannst du nicht nur, das musst du sogar.
Im verlinkten Artikel steht ausdrücklich: Alle Funktionen müssen
dieselbe Signatur haben.
Wenn also einige Funktionen ein Argument brauchen, dann musst du deinen
Funktionspointer darauf auslegen. Das bedeutet allerdings dann auch,
dass alle Funktionen ein Argument annehmen müssen, auch die die
eigentlich keines brauchen. Das ist aber auch kein großes Problem, wenn
sie ein Argument mitbekommen und einfach nichts damit tun.
Karl heinz Buchegger schrieb:> Wenn also einige Funktionen ein Argument brauchen, dann musst du deinen> Funktionspointer darauf auslegen. Das bedeutet allerdings dann auch,> dass alle Funktionen ein Argument annehmen müssen, auch die die> eigentlich keines brauchen. Das ist aber auch kein großes Problem, wenn> sie ein Argument mitbekommen und einfach nichts damit tun.
ok habs nun verstanden
Stier schrieb:> Karl heinz Buchegger schrieb:>> Wenn also einige Funktionen ein Argument brauchen, dann musst du deinen>> Funktionspointer darauf auslegen. Das bedeutet allerdings dann auch,>> dass alle Funktionen ein Argument annehmen müssen, auch die die>> eigentlich keines brauchen. Das ist aber auch kein großes Problem, wenn>> sie ein Argument mitbekommen und einfach nichts damit tun.>> ok habs nun verstanden
Alternativ kann man sich natürlich auch immer noch die Dinge
zurechtcasten wie man sie braucht, aber ich warne ausdrücklich davor.
Sowas führt ganz schnell ins Chaos und zu schwer zu findenden Fehlern.
Eine andere Möglichkeit wäre noch zb. den Mechanismus mit dem an main
eine beliebige Anzahl an Argumenten übergeben wird zu kopieren.
Inwiefern das bei einer Handvoll Funktionen mit einer überschaubaren
Anzahl an Parametern sinnvoll ist, sei jetzt erst mal dahingestellt.
Und dann gibt es ja auch noch globale Variablen mit denen man einen
Argument Passing Mechanimus inszenieren könnte. Aber auch hier
wiederrum: Sobald Menüs rekursiv andere Menüs für Teilaufgaben benutzen
wirds schnell unübersichtlich und fehleranfällig.
Und da Menüsysteme nichts zeitkritisches sind, ist es auch normalerweise
völlig unerheblich ob eine Funktion jetzt ein paar Parameter bekommt,
die sie eigentlich gar nicht benötigt und nur deshalb hat, weil alle
Funktionen gleich aussehen müssen.