Forum: Mikrocontroller und Digitale Elektronik Pointer Problem mit SDCC


von Michael F. (fury)


Angehängte Dateien:

Lesenswert?

Hallo,

ich hab mir ein Menü programmiert, das die Funktionen über Pointer
aufruft. Jetzt hätte ich beim Aufruf der Funktionen gerne einen
Parameter übergeben.
SDCC meldet mir
D:/Daten/8051/C/Menü.c:120: warning 92: Functions called via pointers
must be 'reentrant' to take arguments
und kompiliert die Funktionen danach nicht mehr.

Kann mir jemand verraten wie ich beim Aufruf von Funktionen über
Pointer einen Parameter übergeben ?

Im Anhang findet ihr mein komplettes Programm.

Gruß Michael

von Michael F. (fury)


Lesenswert?

Nochmal ich :-)

Im Thread http://www.mikrocontroller.net/forum/read-2-40721.html im
Beitrag von Karl Heinz Buchegger (etwa in der Mitte) ist sowas erklärt,
aber funktioniert nicht.

P.S. Das Struct des Menüs hab ich bereits geändert
typedef struct MENU {
  const unsigned char *MenueText;
  unsigned char MenueNo;
  unsigned char NextMenueNo;
  void (*MenueFunction)(unsigned char);
} MENUE_ENTRY;

von Karl heinz B. (kbucheg)


Lesenswert?

Nun, du möchtest mit

  Menue[ArrayPos+MenuePos].MenueFunction(MenuePos);

deine Funktion aufrufen.

Wie ist MenuFunction deklariert:

typedef struct MENU {
  const unsigned char *MenueText;
  unsigned char MenueNo;
  unsigned char NextMenueNo;
  void (*MenueFunction)( void );
} MENUE_ENTRY;


Da steht:
Menufunction ist ein Zeiger auf eine Funktion die nichts
zurückliefert (das erste void) und keine Argumente nimmt (das
zweite void). Wenn du also der Funktion Argumente geben willst,
dann musst du das in der Deklaration des Funktionszeigers auch
vereinbaren:

  void (*MenueFunction)( unsigned char );

Jetzt ist MenuFunction ein Zeiger auf eine Funktion die als
erstes Argument einen unsigned char nimmt.

Natürlich müssen jetzt auch alle Funktionen die du an so
einem Funktionszeiger binden willst auch tatsächlich einen
unsigned char annehmen.

von Michael F. (fury)


Lesenswert?

Hallo Karl Heinz,
genau das hab ich in meinem 2.Post beschrieben, aber die Fehlermeldung
bleibt dieselbe. Das merkwürdige ist, das die gleiche Zeile bemängelt
wird.

Es funktioniert auch, wenn ich einen Zeiger benutze der nicht in einem
Struct definiert ist.
Ich suche also weiter, wie richtig auf Elemente eines Structs
zugegriffen wird.

von Karl heinz B. (kbucheg)


Lesenswert?

Das stimmt an und für sich alles, da das Standard-C ist.

Die Fehlermeldung allerdings deutet darauf hin, dass das
ein compilerspezifisches Problem ist. Der Ausdruck 'reentrant'
ist im C-Standard nicht definiert (auch wenn wir alle wissen
was damit gemeint ist). Da wirst du also im Compiler-Handbuch
suchen muessen, was dem Compiler da nicht passt.

von TheMason (Gast)


Lesenswert?

ich kenn mich zwar mit dem sdcc nicht so aus, aber hast du mal folgendes
probiert (sollte eigentlich mit jedem compiler möglich sein) :



typedef void (FKT_ZEIG) (char cParameter1);

void test (char cPar1)
{
 ...
}


FKT_ZEIG *func1 = (FKT_ZEIG *) &test;

void main (void)
{
  char cP = 10;

  ...

  if (func1 != NULL)
  {
    func1 (cP)
  }

  ...
}

von Karl heinz B. (kbucheg)


Lesenswert?

Ich vermite mal, dass es da noch irgendeine 'Dekoration'
für die Funktion gibt, mit dem man dem Compiler mitteilt,
dass die Funktion tatsächlich reentrant ist.

von Karl heinz B. (kbucheg)


Lesenswert?

> Es funktioniert auch, wenn ich einen Zeiger benutze der nicht in
> einem Struct definiert ist.

Das ist interessant.

Versuch mal:
  (Menue[ArrayPos+MenuePos].MenueFunction)(MenuePos);

oder

  (*(Menue[ArrayPos+MenuePos].MenueFunction))(MenuePos);

Sollte eigentlich keinen Unterschied machen, wenn ich die
Precedence Regeln noch richtig im Kopf habe, aber es soll
ja auch schon Compilerfehler gegeben haben.

von Michael F. (fury)


Lesenswert?

@TheMason

unsigned int foo1( int Arg1 )
{
  return (int)( Arg1 + 0.5 );
}

void main(void)
  {
    unsigned int (*MyFunc)( int );
    MyFunc = foo1;

    i = (*MyFunc)( 4 );   /* Aufruf der Funktion ueber den Pointer */
  }

Das funktioniert, wobei Das Ergebnis natürlich Quatsch ist.

@Karl Heinz
Das Klammern reicht nicht, aber ich habe auch den Verdacht, das es am
Compiler liegt.
Bei Gelegenheit werde ich das mal Visual C testen.

von Karl heinz B. (kbucheg)


Lesenswert?

Kannst du dir sparen. In Visual C und allen anderen
C-Compilern auf denen ich bisher gearbeitet habe funktioniert
das klaglos. Muss es auch. Entspricht so den C-Regeln.

von Karl heinz B. (kbucheg)


Lesenswert?

Auf einer Website (google: SDCC reentrant)
habe ich folgenden Satz gefunden:

<Zitat>
Avoid calling functions from within an ISR. If you must do this,
declare the function as reentrant (see SDCC manual) which allocates
all local variables in the function on the stack instead of in RAM.
</Zitat>

besonders der letzte Satzteil
"which allocates all local variables in the function on the stack
instead of in RAM" lässt bei mir die Alarmglocken klingen.

Auf jeden Fall: Das ist irgendetwas SDCC spezifisches.

Aus mehreren anderen Websites habe ich mal folgendes abgeleitet:

typedef struct MENU {
  const unsigned char *MenueText;
  unsigned char MenueNo;
  unsigned char NextMenueNo;
  void (*MenueFunction)(unsigned char) reentrant;
} MENUE_ENTRY;

Und dann natürlich noch jede Funktion mit dem Zusatz 'reentrant'
ausstatten.

von Michael F. (fury)


Lesenswert?

Super, das funktioniert mit reentrant.

Vielen vielen Dank Karl Heinz

von A.K. (Gast)


Lesenswert?

Etliche 8bit Microcontroller tun sich mit Daten auf dem Stack erheblich
schwerer als mit statisch adressierten Daten. Vor allem wenn die
Architektur zu einem Zeitpunkt entstand, als C in diesem Sektor noch
nicht verbreitet war (8051,PIC).

Und so ist es bei solchen Compilern üblich, bis auf Widerruf lokale
Daten statisch zu adressieren. Macht Keil auch nicht anders.

von TheMason (Gast)


Lesenswert?

also ist das verhalten von funktionszeigern beim sdcc nicht so einfach
wie ich es unter standard c gewohnt bin.
gibt es damit eigentlich probleme bei anderen compilern (z.b.
mspgcc/armgcc usw ...) ?!
würde mich mal interessieren wenn ich mal was mit beispielsweise
eclipse mache ... (ob es da auch ähnlich problematisch ist)

gruß
rene

von A.K. (Gast)


Lesenswert?

Eclipse ist kein Compiler.

GCC für ARM,AVR,MSP430 arbeitet normal, eine spezielle Kennzeichnung
derartiger Funktionen ist nicht erforderlich.

Zilog Z8 kann zwar mit Stack-Daten umgehen, der Compiler benutzt in
Standardeinstellung jedoch statische Adressierung.

Compiler für Microcontroller sind meistens mit architekturspezifischen
Eigentümlichheiten gesegnet. Getrennte Adressräume für Code und Daten
erschweren das Leben (8051,AVR,PIC), Interrupt-Routinen müssen speziell
behandelt werden (alle), komprimierter Code fordert Tribut (ARM-Thumb),
ROM-Banking kann u.U. eine Reorganisation des Quellcodes erfordern
(PIC), ...

von TheMason (Gast)


Lesenswert?

@a.k.

sorry, meinte natürlich nicht eclipse als solches sondern mit den
jeweilgen plugins für verschiedene mikrocontroller unter eclipse.

mit den wüsten adressräumen beim 8051 habe ich schon bekanntschaft
gemacht. beim msp430 hatte ich bis jetzt keine probleme (speziell mit
funktionszeigern, da ich oftmals exzessiven gebrauch davon mache :-)
deswegen hab ich auch meinen (überflüssigen) senf dazugegeben, weil
mich mal interessieren würde bei welchen prozessoren funktionszeiger
probleme machen können.

gruß
rene

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.