(Die structs stop und logs habe ich bereits definiert, ebenso das
chararray menuL0.)
Wieso erhalte ich aber nun wegen des Funktionsaufrufs einen Fehler? Wie
muss ich die Funktion korrekt an das Node Struct start übergeben?
Ich möchte wenn möglich verhindern, dass ich einen eigenen Pointer auf
die Funktion zuerst definiere, den ich dann an den Struct übergebe.
Vielen Dank
Markus
Garnicht.
Das ist ja kein Aufruf, der da erfolgt, sondern eine Initialisierung.
Ein Zeiger auf eine Funktion ist genau das. Ein Zeiger auf eine Funktion
ist nicht "ein Zeiger auf eine Funktion plus Parameterliste für einen
zukünftigen Aufruf". Dann wäre auch unklar wie sich eine Initialisierung
von einem tatsächlichen Aufruf syntaktisch unterscheidet. Weiter wäre
unklar, zu welchen Zeitpunkt ein Aufruf wirklich erfolgt.
Soll die Funktion tatsächlich aufgerufen werden, wenn die Initilisierung
erfolgt oder erst später? Was ist die Fehlermeldung (Netiquette)?
In C99 und C++ ist es durchaus erlaubt auch nicht konstante Ausdrücke
für die Initialisierung von auto Variablen zu verwenden. Dann aber ist
die Deklaration von run () falsch, das ja keinen Funktionszeiger
zurückgibt sondern void.
Wie müsste ich dann die Funktionsdeklaration anpassen? Grundsätzlich ist
doch ein Aufruf einer Funktion mit Argument per Zeiger möglich wie z. B.
unter
http://www.c-howto.de/tutorial-funktionen-teil2-zeiger-auf-funktionen.html
beschrieben ?!
Somit sollte doch die Technik auch anwendbar sein, wenn ich den Zeiger
als Struct Element speichern will oder?
Die Fehlermeldung könnte ich erst abends wieder reproduzieren, ich hab
verschiedene Varianten ausprobiert, bei einer war die Meldung effektiv
irgend was von wegen nicht konstanter Ausdruck.
Markus schrieb:> Wie müsste ich dann die Funktionsdeklaration anpassen?
Gar nicht. fp ist ein Zeiger auf eine Funktion des Typs void
foo(uint8_t).
run ist solch eine Funktion. Bei der Initialisierung des Structs bekommt
fp die Adresse von run zugewiesen. Mehr nicht.
Ein Wert für das Argunemt wird erst beim Aufruf der Funktion übergeben.
Das geht dann über run(5), oder über deinen Struct mit Node.fp(5).
Oliver
>Grundsätzlich ist doch ein Aufruf einer Funktion mit Argument per Zeiger >möglich
wie z. B. unter (...) beschrieben ?!
Sicher ist das möglich. Das Beispiel zeigt jedoch den Aufruf einer
Funktion über einen Zeiger innerhalb eines Anweisungsblocks der wiederum
Teil der Funktion main ist.
Im Gegensatz dazu versuchst Du eine Variable zu initialisieren.
>Somit sollte doch die Technik auch anwendbar sein, wenn ich den Zeiger>als Struct Element speichern will oder?
Das geht wie gesagt durchaus: Das Problem ist, das Deine Ausdrucksweise
einen Funktionsaufruf beschreibt und nicht einen Zeiger auf eine
Funktion.
Schau Dir den Code mal genau an:
Zum Funktionsaufruf wird der Funktionsname "rechne" durch das Anhängen
der Klammern.
1
printf("%d hoch 2: %d\n",zahl,rechne(zahl));
Wenn Du nur den Zeiger auf die Funktion meinst, dann nennst Du nur den
Namen der Funktion.
1
rechne=hoch2;
Dabei wird die Funktion hoch2 nicht ausgeführt.
Nehme ich das einfachste an, so willst Du eine struct-Variable mit dem
Zeiger auf eine Funktion initialisieren.
Das geht eben mit
1
Nodestart={&stop,&logs,NULL,NULL,menuL0,run};
Durch Deine Nachfrage: "... und wie kann ich dann das Argument (des Typs
uint8_t) übergeben?"
bekommt das Problem aber noch mindestens eine weitere Dimension.
Du unterstellst nämlich in der Frage, das schrieb ich schon, dass es
einen Weg gibt, ein Strukturmitglied, welches ein Zeiger auf eine
Funktion ist mit sowohl dem eigentlichen Zeiger auf die Funktion als
auch mit der Argumentliste initialisieren kannst. Das aber geht nicht.
Es gibt nur zwei Verwendungen von Funktionszeigern.
1. Zuweisungen an Zeiger gleichen Typs. Dann ist eine Argumentliste
nicht möglich.
2. Aufruf der Funktion auf die gezeigt wird. Dann ist eine
(möglicherweise leere) Argumentliste notwendig.
Um aber direkt auf Deine Frage "Wie müsste ich dann die
Funktionsdeklaration anpassen?" zu antworten will ich folgendes
hinzufügen auch wenn Du das vermutlich garnicht so machen willst.
Du bräuchtest eine weitere Funktion GetRunFunc mit uint8_t Argument die
einen Zeiger auf eine Funktion mit void Resultat und uint8_t Argument
zurückliefert.
1
void(*GetRunFunc(uint8_tcode))(uint8_t)
2
{
3
switch(code)
4
{
5
caseblabla:
6
returnxyz;
7
break;
8
default:
9
returnrun;// einfachstes Beispiel, liefert einfach den Zeiger auf run
10
break;
11
}
12
}
In meinem Beispiel liefert die Funktion einfach einen Zeiger auf run
zurück (die Deklaration von oben angenommen).
Aber in Wirklichkeit würdest Du abhängig vom Parameter eine von mehreren
Funktionen zurückliefern.
Diese Funktion könntest Du dann bei der Initialisierung einer
Auto-Variablen (wohlgemerkt unter C99/C++) verwenden. Aber eben nur
dort.
Da letzlich nur zwischen verschiedenen schon definierten Funktionen
gewählt werden kann (Du kannst zur Laufzeit keine neuen Funktionen
hinzufügen, dann nimm lieber Lisp, Smalltalk oder sowas)
ist der Nutzen eher auf andere Fälle begrenzt. In Deinem Fall ist das
vermutlich garnicht nötig.
Wenn Du also fragst: "Wie müsste ich dann die Funktionsdeklaration
anpassen?" müsstest erstmal genau erklären was Du willst und weiter
welchen Compiler/Sprache Du verwendest.
Es könnte natürlich auch sein, dass du das hier willst:
In deiner Datenstruktur willst du sowohl einen Funktionspointer wie
beschrieben aufrufen, als auch die Argumente, die an die Funktion zu
übergeben sind.
In deinem Beispiel, soll run mit dem Argument 5 aufgerufen werden. In
einem anderen Knoten könnte es sein, dass du run mit einem Argument von
8 aufrufen willst.
Dazu musst du dir erst mal Platz in der Struktur schaffen. Du brauchst
einen Member, der das Funktionsargument aufnehmen kann:
1
typedefstructNode{
2
structNode*next;
3
structNode*prev;
4
structNode*parent;
5
structNode*child;
6
char*text;
7
void(*fp)(uint8_t);
8
uint8_tArgToFp;
9
}Node;
Dann kannst du in deinem konkreten Fall ein Strukturobjekt
initialisieren mittels:
>In deiner Datenstruktur willst du sowohl einen Funktionspointer wie>beschrieben aufrufen, als auch die Argumente, die an die Funktion zu>übergeben sind.
Mönsch, darauf hätte ich ihn gern selbst kommen lassen. Wo ist Deine
Geduld, Karl-Heinz ;-)
Noname schrieb:>>In deiner Datenstruktur willst du sowohl einen Funktionspointer wie>>beschrieben aufrufen, als auch die Argumente, die an die Funktion zu>>übergeben sind.> Mönsch, darauf hätte ich ihn gern selbst kommen lassen. Wo ist Deine> Geduld, Karl-Heinz ;-)
:-)
Das Problem war, das ich jeglichen Hinweis in diese Richtung in deiner
Antwort vermisst habe. Vielleicht war er auch für meine Begriffe etwas
zu subtil versteckt.
Dafür hab ich mir den 2-ten Teil der Antwort 2 mal durchlesen müssen,
bis ich geschnallt habe, worauf du eigentlich hinaus willst und
festgestellt habe, dass er mit dem Problem des TO höchstwahrscheinlich
überhaupt nichts zu tun hat. Ich hab das ein wenig als Spitzfindigkeit
empfunden, eine Initialisiersyntax, von der klar ist dass sie falsch
ist, unter allen Umständen zu retten.
Die erste Hälfte allerdings ist dir gut gelungen.
>Das Problem war, das ich jeglichen Hinweis in diese Richtung in deiner>Antwort vermisst habe. Vielleicht war er auch für meine Begriffe etwas>zu subtil versteckt.
Nun, Du hast das ganz richtig erkannt. Es gibt keinen direkten Hinweis
und für den für den subtilen Teil müsste man sich durch den von Dir
inkriminierten 2. Teil der Antwort durcharbeiten, einen Schritt
zurückgehen und das ganze Bild betrachten. Insbesondere eine Klarheit
darüber herbeiführen was man (der TO) eigentlich will.
Ob das didaktisch so geschickt ist? Darüber kann man verschiedener
Meinung sein. Ich schätze jedenfalls Dein Urteil.
Ich nehme bei Dir wahr (und nicht nur bei Dir) das Du meist von dem
einfacheren Fall ausgehst. Annimmst, das der TO die simplere der
Möglichkeiten beabsichtigt hat, die weniger Erfahrung und Kenntnisse
voraussetzt. Das führt häufig zum Erfolg wie ich feststelle.
Ich sehe mich durch meine Denkweise veranlasst auch die Verwicklungen zu
berücksichtigen weil und wenn ich wenig oder garkeinen Anhaltspunkt
habe, welche Vorkenntnisse der TO hat. Auch dazu, die Alternativen zu
benennen und sei es um den TO erkennen zu lassen, das er diese garnicht
wollte, aber mit dem Gewinn, das er weiss das es da noch andere Aspekte
gibt.
In diesem speziellen Fall war es zwar weniger wahrscheinlich aber
dennoch möglich, das er wirklich einen Funktionsaufruf bei der
Initialisierung haben wollte aber nicht wusste das das nur bei
auto-Variablen und nur ab C99/C++ geht. Genauso wie es möglich war, das
er den Zeiger erst durch eine Funktion erzeugen wollte (wenn das auch,
meiner Ansicht nach, in dem Fall für die Verwendung wenig Sinn macht).
Kontext des Codesnippets, Fehlermeldung und Compiler waren ja unbekannt.
Ich stand vor der Wahl das zu berücksichtigen oder wegzulassen. Die
Wahrscheinlichkeiten schienen mir nicht wirklich entscheidend
unterschiedlich zu sein. Die Seitenwege interessant genug um darüber zu
schreiben.
Kurz und gut: Danke für den Hinweis.
Vielleicht liegt das Verständnisproblem beim Unterschied von
1
funktion()
und
1
funktion
.
1
funktion()
= Funktionaufruf = ausführbarer Befehl
1
funktion
= Adresse, an der die Funktion liegt
Ein Zeiger auf eine Funktion speichert eine Adresse.
Ein Zeiger ist aber auch nur eine Speicherstelle und kein ausführbarer
Befehl.
Oder andersrum:
Wenn Du eine Funktion aufrufen/ausführen willst, braucht es im
Assembler-Code einen "JUMP"-Befehl (o.ä.) dazu.
In
ok, ich glaub ich hab inzwischen den Unterschied eines Funktionsaufrufs
und einem Zeiger auf eine Funktion verstanden, besten Dank.
Wenn ich nun aber meine Instanz mit
1
Nodestart={&stop,&logs,NULL,NULL,menuL0[0],run};
initialisiere, erhalte ich immer noch eine Fehlermeldung:
display_test.o:(.data+0xa): undefined reference to `run'
Wie muss ich die Fehlermeldung in diesem File verstehen? Der Code an
sich scheint ja korrekt zu sein, da keine Fehlermeldung für das
Sourcefile aufgeführt werden. Ich bin verwirrt :S
Wenn du die Funktion nur deklariert hast und nicht definiert, dann ist
es kein Wunder, daß du verwirrt bist.
Ansonsten: mehr Info liefern, sonst kann man nur spekulieren.
Nochmal wegen deinem Wunsch, die Funktion bei der Initialisierung der
Struct aufzurufen:
Mach dir bewußt, wann im Prozessor tatsächlich Code ausgeführt wird.
Eine Variable (oder Struct usw) besitzt keinerlei ausführbaren Code. Es
ist letztendlich nur eine Speicherstelle.
Jetzt könnte man meinen, daß der C-Compiler aus
1
inta=123;
einen Assembler Code à la
[asm]move #123,$10000[asm]
erzeugt.
Das ist aber nicht so, sondern der C-Compiler tut die Variable in einen
Speicherbereich, der am Stück durch (so etwas wie memcpy) initialisiert
wird.
Was ich damit sagen will: man kann ein ganze C-Datei schreiben, in der
nur Variablen angelegt sind, und erzeugt keine Zeile ausführbaren Code
damit.
Aus diesem Grund kannst Du auch am Initialisieren keine Funktion
aufrufen, weil es gar keinen individuellen Initialisierungscode gibt,
der diese Funktion aufrufen könnte.
In C++ gibt es dazu übrgens den "Constructor", der genau solche
komplexen Initialisierungen möglich macht.