Forum: Compiler & IDEs funktionen und pointer darauf... ?


von Stefan Sczekalla (Gast)


Lesenswert?

Hi,

ich moechte einige functions schreiben, die ich dann über einen
"index" aufrufen möchte. ich bin nun leider nicht wirklich sattelfest
was funcionpointer etc. betrifft und der kernigham war mit zwei seiten
nicht wirklich erleuchtend ....

wie bekomme ich denn die "startadresse" der funktion(en) in eine
Tabelle und wie "call"e ich dann die Funktion deren addresse an
Stelle "i" in der dem Pointerarray steht.

vielen Dank im Voraus,

Stefan

von Chris (Gast)


Lesenswert?

Am einfachsten und übersichtlichsten geht's mit einem typedef:

typedef int (*fptr)(int);
/* "zeigt auf eine Funktion, die int übernimmt und int zurückgibt"
*/

int foo(int i) { return i*2; }
int bar(int i) { return i*3; }
int baz(int i) { return i*4; }
fptr array[3] = { &foo, &bar, &baz };

/* ... */

for(i = 0; i < 3; ++i)
    array[i](1); /* jede Funktion aus dem Array wird einmal aufgerufen,
Parameter ist jedesmal 1 */



In C++ geht das so ziemlich sicher, in C müsste es eigentlich auch
gehen.

von Stefan Sczekalla (Gast)


Lesenswert?

Hallo cris,

Also ich habe den Code mal eingbaut und Kompiliert.
dann hab ich mir noch den Kernighan geschnappt und nachgelesen.

Also Fehlermeldung gibts keine - aber woher weiß ich den das der
pointer auf ein Funktion ein "int" lang ist ? - oder inerpretiere ich
den typedef falsch ?

ich meine gut, es wäre logisch wegen des Prozessors ( addresierung
eines ljmp ) - aber meine logik muss da ja nicht die des Kompilerbauers
sein ...

( seuftz - ja ich bin nicht wirklich fitt in C )

Grüße,

Stefan

von Chris (Gast)


Lesenswert?

> Also Fehlermeldung gibts keine - aber woher weiß ich den das der
> pointer auf ein Funktion ein "int" lang ist ? - oder inerpretiere
ich
> den typedef falsch ?
Jep, tust du.

Kurze Erklärung:
Die ints im typedef sagen nur etwas über die Signatur der Funktion aus,
auf die der Zeiger zeigt.

Lange Erklärung:
Das typedef sieht ja so aus:
typedef int (*fptr)(int);

Lassen wir mal das * weg und nennen fptr in f um:
typedef int (f)(int);
Die Klammern um f kann man sich dann natürlich auch schenken:
typedef int f(int);

So, das sieht nun beinahe wie ein ganz gewöhnlicher Funktionsprototyp
aus, abgesehen vom typedef davor.
Der Typ f bezeichnet hier eine Funktion selbst, keinen Zeiger darauf.
Dieser Typ ist damit genauso abstrakt wie void, man kann keine Variable
vom Typ f erstellen. Was man aber darf, ist folgendes:
f* p;
p ist jetzt ein Zeiger auf eben die Funktion, deren Prototyp
("Signatur") ich oben "getypedeft" habe.
Man kann nun also ohne weiteres die beiden letzen Zeilen kombinieren:
typedef int f(int);
typedef f* fptr;

Damit deklariert man den Typ fptr unmissverständlich als
Funktionszeiger. Jetzt möchte man aber gerne nur ein typedef haben,
also wird eingesetzt:
typedef int (int)* fptr; /* oberes typedef stur ins untere eingesetzt
*/
Das ist aber totaler Blödsinn, die Parameter gehören immer ans Ende:
typedef int * fptr (int);

Aber halt! Gerade eben noch hatten wir:
typedef int f(int);
als abstraktes Funktionsobjekt definiert. Was ist jetzt
typedef int* fptr (int);
für den Compiler? Natürlich genauso ein abstraktes Funktionsobjekt. Nur
diesmal liefert die so spezifierte Funktion einen int* zurück.

Aus genau diesem Grund muss man so ungewohnte Klammern setzen:
typedef int (*fptr)(int);
Jetzt muss auch der letzte Compiler akzeptieren, dass man hier einen
Zeiger deklariert, kein abstraktes Funktionstypedef. Und zwar einen
Zeiger auf eine Funktion, die a) einen int zurückliefert und b) einen
int übernimmt.


Funktionszeiger sind in C (und in C++ noch stärker) ein völlig eigener
Typ. Funktionszeiger werden in C++ (und ich denke auch in C) strikt von
Datenzeigern getrennt behandelt.
char*, int*, das sind alles Datenzeiger. void* ist in diesem Sinne aber
auch ein Datenzeiger und damit nicht kompatibel zu Funktionszeigern.
Funktionszeiger können nämlich größer sein als void* (in C++ sind sie
es auch häufig).
In C könnte es z.B. passieren, dass du eine Maschine hast, die viel
mehr Programmspeicher als RAM hat. Dann hat ein void* vielleicht nur 16
Bit, ein Funktionszeiger aber 24 (rein hypothetisch). Das wäre mit dem
C-Standard ohne weiteres vereinbar, führt bei unsauberer Programmierung
aber schnell zu Fehlern.

Einen Funktionszeiger speichert man daher korrekterweise niemals in
einem void*.


Ein int muss sowieso nicht groß genug für einen Zeiger sein, egal ob
Daten- oder Funktionszeiger. Bei 64-Bit-Rechnern etwa hat ein int immer
noch 32 Bit, ein Zeiger aber 64 Bit (beim Visual C++ Compiler).

von Werner B. (Gast)


Lesenswert?

Das mit der länge eines datentyps ist generell compilerabhängig. Der
standard (ANSI C) definiert nur eine mindestlänge die ein datentyp
haben muss.

Darum gibt es auch so viele macros, die abhängig vom verwendeten
compiler eigene "längen"-datentypen definieren.
Die geschieht um die portabilität von sourcecode zwischen den
verschiedenen compilern zu gewährleisten.
z.b. int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t ...

von Jörg Wunsch (Gast)


Lesenswert?

Anmerkung: int8_t und Konsorten sind via <inttypes.h> im C99-Standard
genormt.

von Stefan Sczekalla (Gast)


Lesenswert?

Hallo Chris,

vielen Dank für die ausführliche Erklärung.

Das heist eine Pointer auf eine paramelterlose Funktion die nichts
zurückgibt würde so deffiniert ?

typedef void (*zeiger)(void);

... und dabei weiss dann der Compiler, das er einen "Abstrakten"
Zeiger als "neuen" Typ anlegt. Wie groß der sein muss verhackstückt
der Compiler intern.

Grüße,

Stefan

von Chris (Gast)


Lesenswert?

> Das heist eine Pointer auf eine paramelterlose Funktion die nichts
> zurückgibt würde so deffiniert ?
>
> typedef void (*zeiger)(void);

Genau.

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.