Forum: Mikrocontroller und Digitale Elektronik Pointer auf Funktion eine Adresse zuweisen


von Fischle (Gast)


Lesenswert?

Hi,

ist es möglich in C einem Funktionspointer eine fixe Adresse zuzuweisen?

Ich möchte zur Laufzeit den Code in einem reservierten Ram Bereich 
ändern und benötige einen Einsprungpunkt. Zum Beispiel die Adresse 
0x1234, die sich ebenfalls in einem reservierten Ram Bereich befindet.
1
//So würde ich es bei Variablen machen:
2
int* ptr;
3
ptr=(int*) 0x1234;

Aber wie funktioniert es bei ganzen Funktionen?
vielleicht so:
1
//.h Datei
2
void funktion(void);
3
4
//.c Datei
5
void funktion ()
6
{
7
// tu was
8
}
9
10
void main()
11
{
12
void (*funktionsPointer)(void) = (void*) 0x1234
13
14
funktionsPointer=funktion;
15
//Aufruf von "funktion" über einen Pointer, der fix an der Adresse 0x1234 abgelegt ist
16
(*funktionsPointer)();
17
}

Der Compiler sagt ich kann den Pointer so nicht initialisieren weil sich 
"void*" nicht eignet um den Typ "void(*)(void)" zu initialisieren ...
Kann mir jemand sagen ob das generell nicht geht, oder nur ein 
Syntaxproblem ist.

Liebe Grüße
Fischle

von user (Gast)


Lesenswert?

Du kannst dem Linker sagen wo er welche Funktion hinlegen soll.

von Rainer B. (katastrophenheinz)


Lesenswert?

Hi, Syntaxproblem.
so geht's:
1
typedef void ( *fPointer ) ( void );
2
...
3
fPointer func = ( fPointer ) 0x1234;
4
...
5
func();

Gruss, Rainer

von Sag ich nicht (Gast)


Lesenswert?

Oder wenn du dich von der Es-muss-lesbar-sein Fraktion nicht erwischen 
lässt:
void (*funktionsPointer)(void) = (void (*)(void)) 0x1234;

von Fischle (Gast)


Lesenswert?

Hi,

vielen Dank für eure Antworten!
Die Lösung für das Syntaxproblm funktioniert, es wird kompiliert.

Gruß
Fischle

von W.S. (Gast)


Lesenswert?

Fischle schrieb:
> ist es möglich in C einem Funktionspointer eine fixe Adresse zuzuweisen?

na klar doch. Sogar richtige konstante (fixe) Strukturen kann man 
hinkriegen. Hier ein kleiner Ausschnitt aus dem menüsystem der Lernbetty 
(siehe Codesammlung..):

#define RCST  const struct TMenuItem
struct TMenuItem
{ struct TRect R;
  const struct TMenuItem *davor;
  const struct TMenuItem *danach;
  const struct TMenuItem *Owner;
  const struct TMenuItem *Members;
  dword Flags;
  void  *Data;
  void  (*OnKey)  (RCST* self, word *aKey);
  void  (*OnEvent)(RCST* self, word *aEvent);
  void  (*Draw)   (RCST* self);
};

und

RCST Appfinder_Liste =
{ /* l-t-r-b */ { 0, 20, 127, 159},
  /* davor  */  &Appfinder_Uhrzeit,
  /* danach */  &Appfinder_exit,
  /* Owner  */  &Appfinder_Mainmenu,
  /* Members*/  0,
  /* Flags  */  opaque | canFocus,
  /* *Data  */  0,
  /* OnKey  */  Appfinder_Liste_OnKey,
  /* OnEvent*/  Appfinder_Liste_OnEvent,
  /* Draw   */  Appfinder_Liste_Draw
};

und

void Appfinder_Liste_OnEvent (RCST *self, word *aEvent)
{.....
}



Die 3 Funktionen OnKey, OnEvent und Draw sind nette Beispiele, wie man 
sowas deklariert und wie man es benutzt.

UND: Ich meine, sowas ist durchaus lesbar und verständlich geschrieben - 
im dezenten Gegensatz zu
"void (*funktionsPointer)(void) = (void (*)(void)) 0x1234;"

gelle?

W.S.

von Rainer B. (katastrophenheinz)


Lesenswert?

@Sag ich nicht: Da isses schon passiert :p !

von Fischle (Gast)


Lesenswert?

Hi,

danke nochmals an alle Antwortenden! Dieses Forum ist klasse :)

Nochmals ein kurzer Raport meiner Lösung:
MSP430g2553 auf Launchpad an CCSEv4 (nammi) ....
1
//Im MeinFile.c File bei den globalen Variablen
2
#define IRGENDWASHALT
3
typedef void (*fktPtr)(void);
4
extern fktPtr ptr_MeineFunktion;
5
6
...
7
8
//Im msp430g2553.cmd File direkt unter den Includes
9
#include MeinFile.h
10
ptr_MeineFunktion = 0x0200 //Erste RAM-Adresse beim MSP430G2553
11
12
...
13
14
//Im MeinFile.h stehen die defines welche zur Aufteilung des 
15
//Speicherplatzes verwendet werden
16
#define FKTPTR_START_ADDR             (0x0200)
17
#define FKTPTR_END_ADDR               (0x020F)
18
#define FKTPTR_LEN                    (0x0010)//Platz für 8 Pointers
19
//Der Rest vom RAM beginnt dann natuerlich erst bei
20
#define RAM_START_ADDR                (0x0210)
21
//Und die Länge von NORAMLRAM_LEN muss um 16Bit gekürzt werden
22
23
24
...
25
26
//Wieder im msp430g2553.cmd File ersetzt man die eine Zeile
27
RAM               : origin = 0x0200, length = 0x0200
28
//Durch diese beiden Zeilen
29
FKTPTR            : origin = FKTPTR_START_ADDR, length = FKTPTR_LEN
30
NORAMLRAM         : origin = NORAMLRAM_START_ADDR, length =NORAMLRAM_LEN
31
//Dadurch wird der Speicherbereich für die Variablen vom "NormalenRAM"
32
//getrennt und die extra dort plazierten Vars werden nicht aus Versehen
33
//ueberschrieben

Gruß
Fischle

von Fischle (Gast)


Lesenswert?

Hi,

nächstes Problem ;)
Kann sein, dass W.S. das in seinem Beitrag bereits erklärt hat, ich hab 
das BSP aber noch nicht ganz verstanden ...

Ich würde gerne -ebenfalls über den Funktionspointer- eine Funktion 
aufrufen, nur diesmal mit Parametern.
Ausgedacht hab ich mir das so:
1
//Probiert hab ich es so wie zuvor nur eben mit Parameter.
2
typedef void (*fkt_mit_ParameterPtr)(int*);
3
extern fkt_mit_ParameterPtr ptr_MeineFunktionMitParameter;//Ramadresse=0x0206
4
5
//Zeiger auf ein int, weil ich den übergebenen Wert 
6
//ebenfalls an einer festen Speicheradresse 
7
//zwischengelagert haben möchte.
8
extern int *Parameter;//Ramadresse=0x0208
9
10
//Funktionsdeklaration
11
void MeineFunktionMitParameter (int *Parameter)
12
{
13
Parameter++; //irgendwas
14
}
15
16
//Verknüpfung der Funktion mit dem Funktionsointer in main()
17
ptr_MeineFunktionMitParameter = MeineFunktionMitParameter;
18
19
//Funktionsaufruf im dynamischen Bereich
20
//Als Parameter soll ein Intwert verwendet werden 
21
//Diesen moechte ich da gern als Zahl eintragen können
22
(*ptr_MeineFunktionMitParameter)(7)
Aber der Compiler mag es nicht ...

Dann dachte ich an ein Call by Reference, also sowas wie:
1
(*ptr_MeineFunktionMitParameter)(&7)
Dafür komm ich wahrscheinlich in die Hölle, aber funktionieren tut es 
natürlich auch nicht ...

Weiß jemand Rat? (außer, dass ich noch viel zu lernen hab :D)

Gruß
Fischle

von Fischle (Gast)


Lesenswert?

Der Aufruf des Funktionspointers braucht natürlich noch ein Semikolon
1
(*ptr_MeineFunktionMitParameter)(&7);

von Rainer B. (katastrophenheinz)


Lesenswert?

Hi,
...aber in den heißesten Teil der Hölle.
Ist doch eigentlich einfach:
7 ist kein int, sondern eine Compilezeit-Konstante. 
Compilezeit-Konstanten belegen keinen Speicherplatz im Sinn von 
Variablen, also kann man sie auch nicht referenzieren. Was soll da 
rauskommen? D.h. du musst erst einen int-wert mit 7 initialisieren und 
dann kannst du mit & auf diesen int-Wert referenzieren.

Und das hier
1
//Funktionsdeklaration
2
void MeineFunktionMitParameter (int *Parameter)
3
{
4
Parameter++; //irgendwas
5
}
wird auch nicht das tun, was du erwartest. Überleg mal selbst, warum.

: Bearbeitet durch User
von Fischle (Gast)


Lesenswert?

Hi,

Logisch, das ist Quatsch ...
Darf man das in C so machen?:
1
//Funktionsdeklaration
2
void MeineFunktionMitParameter (int *Parameter)
3
{
4
*Parameter++; 
5
}

Ich teste mal schnell das mit dem Funktionsaufrun und dem &-Operator ...

von Georg G. (df2au)


Lesenswert?

Fischle schrieb:
> *Parameter++;

K&R, Kapitel 5

Setz Klammern, sonst passiert das, was du nicht willst.

von Rainer B. (katastrophenheinz)


Lesenswert?

>Darf man das in C so machen?
Man darf alles das machen, was der Compiler nicht anmeckert.
Die eigentliche Frage ist "Was willst du machen?"
Zur weiteren Verwirrung:
1
*Parameter++; 
2
(*Parameter)++; 
3
*(Parameter++);
Alles syntaktisch ok, der Compiler frisst es.
Aber was ist der Unterschied? Und welche der unteren beiden 
Schreibweisen entspricht der oberen?
Gruss, Rainer

von Fischle (Gast)


Lesenswert?

Das war nur ein Beispiel (mit einem dummen Fehler drin) was in der 
Funktion passieren soll ... aber ich lerne immer gerne was dazu :)
Also, hier meine Interpretation:
1
*Parameter++;//Keine Ahnung was hier passieren wird ...
2
//Die Punkt vor Strich Regel kann ich nicht anwenden oder? ;)
3
(*Parameter)++;//Increment des Inhalts vom ptr Parameter 
4
*(Parameter++);//Increment des ptr Parameter und dann ein Zugriff ohne Effekt?
wirklich verwenden möchte ich den Parameter nacher so:
1
switch(*Parameter)
2
{
3
   case 1:
4
         foo1();
5
         break;
6
   case 7:
7
         foo2();
8
         break;
9
   //usw...
10
}


Was mich aber auch noch beschäftigt, ist die Frage ob (Codeabschnitt von 
vorhin)
1
typedef void (*fkt_mit_ParameterPtr)(int*);
2
extern fkt_mit_ParameterPtr ptr_MeineFunktionMitParameter;//Ramadresse=0x0206
3
4
//Zeiger auf ein int, weil ich den übergebenen Wert 
5
//ebenfalls an einer festen Speicheradresse 
6
//zwischengelagert haben möchte.
7
extern int *Parameter;//Ramadresse=0x0208
8
9
//Funktionsdeklaration
10
void MeineFunktionMitParameter (int *Parameter)
11
{
12
//swich case anweisung mit Inhalt von Parameter
13
}
tatsächlich dafür sorgt, dass die 7 an die Speicheradresse 0x028 gelegt 
wird, wenn ich die Funktion mittels
1
i=7;
2
(*ptr_MeineFunktionMitParameter)(&i);
aufrufe.

von Rainer B. (katastrophenheinz)


Lesenswert?

Hi, Interpretation der unteren beiden korrekt.
Um festzustellen, was der obere macht, brauchst du die Präzedenzregeln 
für unäre Operatoren, gibt's in dem bereits erwähnten K&R-Buch und 
bestimmt auch im Netz. Da ++ eine höhere Präzendenz als * hat, 
entspricht die Schreibweise ohne Klammern der unteren geklammerten. Aber 
denk dir nichts dabei, wenn du die nicht im Kopf hast, ich muss auch 
noch nachgucken.

Zu deiner Frage. Natürlich kannst du einen int-Pointer auf eine 
bestimmte Adresse zeigen lassen, genauso wie du das mit dem 
Funktionspointer gemacht hast. Du musst es nur tun (ich hab die Stelle 
zumindest nicht gesehen )
1
int *ptr = (int *)0x208; // Zeigt auf 0x208
2
(*ptr)++;                // Zählt die int-variable in 0x208 ff eins hoch

ptr kannst du nach der Zuweisung natürlich auch wieder als 
Funktionsparameter übergeben.

http://www.mikrocontroller.net/articles/Zeiger

Gruss, Rainer

: Bearbeitet durch User
von Fischle (Gast)


Lesenswert?

Hi,

Die Adresszuweisung passiert hier:
1
extern int *Parameter;//Ramadresse=0x0208
2
//Im Linkerskript steht dann noch 
3
Parameter=0x0208; //hab ich oben nicht extra dazugeschrieben ...

Der Kniff mit dem
1
int i=7;
2
(*ptr_MeineFunktionMitParameter)(&i);
beim Aufruf des Funktionspointers funktioniert zwar, nur passiert da 
etwas, dass ich nicht will ...
Die Deklaration
1
int i=7;
passt bei mir nicht ins Konzept, da ich zur Laufzeit meinen Ramspeicher 
löschen will und komplett andere Funktionen da hineinkopieren möchte. 
(ich weiß, dass man so nicht seine Freitagabende zubringen sollte ...)
Deshalb sind im "Laufzeitdynamischen" (ich nenn das mal so) Code keine 
Deklarationen möglich.

Natürlich könnte ich das auch über:
1
//Ein globaler Pointer für die Werteübergabe
2
*Pointer=7;
3
//Aufruf einer Voidfunktion
4
(*MeineFunktion)();
5
6
//und spaetere Verarbeitung des globalen Pointers machen
7
void MeineFunktion(){
8
//swich case Verarbeitung von *Pointer
9
}
machen, aber
1
(*ptr_MeineFunktion)(7);
wäre natürlich wesentlich schöner und benutzbarer ...

Zum Beispiel wäre das ne Lösung:
1
void Meine Funktion (int wert){
2
&wert=Pointer; //die Adresse von wert soll 0x0208 sein
3
}
Mal abgesehn davon, dass ich dafür wieder in die C-Hölle komme und es 
nich kompiliert wird, könnte ich so zur Laufzeit erstellte Variablen auf 
Fixadressen umbiegen und so den Crash vermeiden, wenn ich meinen Ram 
lösche und mit anderen Funktionen (und Variablen) beschreibe.

Wie könnte ich das angehen?

von Karl H. (kbuchegg)


Lesenswert?

Fischle schrieb:

> Was mich aber auch noch beschäftigt, ist die Frage ob (Codeabschnitt von
> vorhin)
>
1
> typedef void (*fkt_mit_ParameterPtr)(int*);
2
> extern fkt_mit_ParameterPtr 
3
> ptr_MeineFunktionMitParameter;//Ramadresse=0x0206
4
> 
5
> //Zeiger auf ein int, weil ich den übergebenen Wert
6
> //ebenfalls an einer festen Speicheradresse
7
> //zwischengelagert haben möchte.
8
> extern int *Parameter;//Ramadresse=0x0208
9
> 
10
> //Funktionsdeklaration
11
> void MeineFunktionMitParameter (int *Parameter)
12
> {
13
> //swich case anweisung mit Inhalt von Parameter
14
> }
15
>
> tatsächlich dafür sorgt, dass die 7 an die Speicheradresse 0x028 gelegt
> wird, wenn ich die Funktion mittels

Was willst du wirklich?
Irgendwie hab ich das Gefühl dir ist noch nicht so ganz klar, was da 
eigentlich passieren soll.

Was soll jetzt eigentlich über die Funtkionsschnittstelle gehen?
Die Adresse an die der Wert hinsoll, oder der Wert selber.

Wenn zweiteres
1
  (*ptr_MeineFunktionMitParameter)(7);
1
void MeineFunktionMitParameter (int Wert)
2
{
3
  *Parameter = Wert;
4
}

Der erste Fall, eine Adresse wird übergeben, macht nur dann Sinn, wenn 
du auch etwas hast, was eine Adresse hat (oder die Adresse selber). 7 
hat keine Adresse. 7 ist eine Konstante und existiert als solches nicht 
im Speicher.

von Rainer B. (katastrophenheinz)


Lesenswert?

Hmm, ich bin mir nicht sicher, ob ich dein Problem richtig verstehe.

Wenn das mit deinem Linkerskript funktioniert, dann hast du sofort, ohne 
dass du noch was dazu tun musst, mit der int*-Variablen "Parameter" 
einen int-Pointer, der auf 0x208 ff zeigt. Den kannst du sofort 
verwenden, als Parameter für deine Funktion, die einen int* erwartet 
oder als ganz normale int-Variable per Dereferenzierung: *Parameter=12

D.h. du schreibst dann einfach
1
ptr_MeineFunktionMitParameter(Parameter);

von Fischle (Gast)


Lesenswert?

Hi,

anscheinend hab ich mal wieder im Kreis gedacht?

Mit der Funktionsdeklaration
1
void MeineFunktion(int Wert)
2
{
3
//verarbeite Wert
4
}
und dem Aufruf
1
(*ptr_MeineFunktion)(7);
tut der Code was er soll und auch beim Austausch des Ramspeicherinhalts 
kommt es zu keinem Absturz ...

Ist der Aufruf einer Funktion über ein Funktionspointer sozusagen ein 
"Call by Reference"?


Gruß
Fischlein

von Rainer B. (katastrophenheinz)


Lesenswert?

Fischle schrieb:
> Ist der Aufruf einer Funktion über ein Funktionspointer sozusagen ein
> "Call by Reference"?
Nein, es macht überhaupt keinen Unterschied, ob du deine Funktion direkt
über MeineFunktion(7) oder ptrMeineFunktion(7) aufrufst. Du Musst es 
nicht einmal so komplex schreiben wie du es oben getan hast, d.h. 
explizite Dereferenzierung und Klammern kannst du auch weglassen. Das 
geht, weil auch "MeinFunktion" selbst aus Compliersicht schon ein Zeiger 
auf eine Funktion ist, Dereferenzierung und Aufruf macht der Compiler 
automatisch, wenn er Klammern und ggf Funktionsparameter hinter dem 
Funktionsnamen findet.

In C werden Aufrufparameter von Funktionen grundsätzlich immer als Kopie 
des Aufrufparameters übergeben, also call-by-value. D.h. für die Dauer 
der Funktionsausführung arbeitest du mit einer Kopie des übergebenen 
Wertes. Aus deiner Konstanten 7 wird während der Funktionsausführung ein 
Funktionsargument in Form einer int-Variable (weil du das im 
Funktionskopf so festgelegt hast)  mit dem Wert 7. Um das Ganze noch 
weiter zu komplizieren: Funktionsargumente unterscheiden sich in nichts 
von normalen Variablen, außer dem Umstand, dass sie nach jeder 
Funktionsausführung wieder gelöscht (=vom Stack geräumt) werden. Damit 
kannst du dann auch legal solche Sachen machen wie
1
void Keks(int param)
2
{
3
   for ( ; param > 0 ; param-- )
4
      printf("Param=%d\n",param);
5
}
6
7
int main(void)
8
{
9
   Keks(7);
10
   Keks(99);
11
}
Wenn du explizit festlegen willst, dass ein Funktionsparameter wie eine 
Konstante behandelt werden soll (in dem Fall als Laufzeitkonstante, denn 
der Aufrufmechanismus "Wertkopie" bleibt), dann kannst du das dem 
Compiler über das Wort "const" mitteilen, also im obigen Beispiel "const 
int param". Probier aus, was der Compiler dann sagt.

Gruss, Rainer

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.