Forum: PC-Programmierung Sind Funktionspointer "spezielle" Pointer?


von Dennis S. (eltio)


Lesenswert?

Hallo zusammen,

die Frage steht im Betreff: gibt es einen Unterschied in der 
Implementierung (nicht Anwendung) von Zeigern auf Variablen und Zeigern 
auf Funktionen?

Gruß
Dennis

von Uhu U. (uhu)


Lesenswert?

Nein.

von Kernalhacker (Gast)


Lesenswert?

Uhu U. schrieb:
> Nein.

Doch. Grundsätzlich schon. Die Zeiger können gleich sein, müssen es 
aber nicht (z.B. bei Harvard).

von Peter II (Gast)


Lesenswert?

bei C++ gibt es auch Funktionszeiger auf Memberfunktionen, die haben 
eine andere Größe, weil das Objekt und der Offset codiert werden müssen.

von Dennis S. (eltio)


Lesenswert?

Kernalhacker schrieb:
> Doch. Grundsätzlich schon. Die Zeiger können gleich sein, müssen es
> aber nicht (z.B. bei Harvard).
Wo wäre dann der Unterschied?

Peter II schrieb:
> bei C++ gibt es auch Funktionszeiger auf Memberfunktionen, die
> haben
> eine andere Größe, weil das Objekt und der Offset codiert werden müssen.
Ach... ich vergesse immer, dass der Filter nur die Ansicht ändert und 
nichts beim Posten: es geht um reines C.

Gruß
Dennis

von Michael B. (laberkopp)


Lesenswert?

Dennis S. schrieb:
> gibt es einen Unterschied in der Implementierung (nicht Anwendung)
> von Zeigern auf Variablen und Zeigern auf Funktionen?

Auf einer von-Neumann-Machine nicht.

Auf einer Harvard Maschine schon, da können beide unterschiedliche 
Grössen haben

(z.B. Renesas M16: Datenpointer 16 bit, Funktionspointer 20 bit, uups, 
das ist eine von-Neumann-Maschine mit eingeschränktem Datenadressraum).

Ausnahmen gibt es also immer wieder, je nach kranker Architektir 
(Siemens C166 sach ich nur).

Ordentliche Prozessoren haben aber beide in der selben Grösse und können 
sie in denselben Registern und Speicherstellen aufbewahren, der 
Unterschied ergibt sich also bloss aus der Verwendung.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Dennis S. schrieb:
> Wo wäre dann der Unterschied?

Bspw. in der Größe.  Wenn du zwei getrennte Busse für Daten und
Befehle hast, müssen diese ja nicht gleich groß sein.  Ein AVR mit
256 KiB Flash hat beispielsweise 17 Bit für Funktionszeiger (das
Bit 0 wird nicht gespeichert, weil Code immer auf geraden Adressen
liegt), aber nur 16 Bit für Datenzeiger.

Aus diesem Grunde schreibt der C-Standard auch vor, dass portabler
Code keine Zeiger zwischen Funktionen und Objekten hin und her
konvertieren darf.  Es ist allerdings garantiert, dass die Wandlung
eines Funktionszeigers in einen anderen Funktionszeigertyp und dann
zurück zum ursprünglichen funktioniert.

von Dr. Sommer (Gast)


Lesenswert?

Michael B. schrieb:
> Auf einer von-Neumann-Machine nicht.

Gibt es überhaupt (noch) von-Neumann-Maschinen? Selbst die kleinsten 
MCU's haben doch zwei Datenbusse für Daten/Code, selbst wenn die in den 
selben Speicher gehen. Größere Prozessoren wie ARM oder gar x86 haben 
gern sogar noch mehr Busse (Speicher, Code, Peripherie, ...)

Es ist allerdings korrekt dass in C normale Zeiger und Funktionszeiger 
nicht gleich groß sein müssen. In C++ sind Member-Zeiger nochmal anders. 
Auf POSIX-Systemen wie Linux sind Funktions-und Datenzeiger aber immer 
gleich groß, denn sonst würden dlsym() und Konsorten nicht 
funktionieren.

von Dirk B. (dirkb2)


Lesenswert?

Michael B. schrieb:
> Ausnahmen gibt es also immer wieder, je nach kranker Architektir

IBM-PC und Nachfahren und Klone. Also 80x86 im Real-Mode.

von (prx) A. K. (prx)


Lesenswert?

Dr. Sommer schrieb:
> Gibt es überhaupt (noch) von-Neumann-Maschinen?

Wenn man dem tieferen Sinn der Sache folgt und nicht bloss die Worte des 
Meisters runterbetet, dann wendet man diese Klassifizierung auf logische 
Adressräume statt physikalischer Datenbusse an. Solange man sich wie in 
dieser Frage auf der Ebene der Instruction Set Architecture befindet und 
nicht über Implementierungsdetails konkreten Siliziums diskutiert.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Jörg W. schrieb:
> Aus diesem Grunde schreibt der C-Standard auch vor, dass portabler
> Code keine Zeiger zwischen Funktionen und Objekten hin und her
> konvertieren darf.

Eigentlich schreibt er sogar vor, dass man es gar nicht kann. Die Zeiger 
sind inkompatibel, und es müsste eine Fehlermeldung kommen. Kaum ein 
Compiler handhabt das aber so.

von Peter D. (peda)


Lesenswert?

Dennis S. schrieb:
> gibt es einen Unterschied in der
> Implementierung (nicht Anwendung) von Zeigern auf Variablen und Zeigern
> auf Funktionen?

Natürlich.
Bei einer Funktion muß ein CALL ausgeführt werden, bei einer Variable 
ein Lese oder Schreibzugriff (LD, ST).
Die () Klammern hinter dem Pointer sagen dem C-Compiler, führe einen 
CALL aus.

von Programmiersprachentheaterintendant (Gast)


Lesenswert?

Schlimmer noch:
>> gibt es einen Unterschied in der
>> Implementierung von Zeigern?

Es gibt Systeme da sind nichtmal "Zeiger" auf RAM und auf (Flash)ROM 
gleich, auch wenn beides "Zeiger" auf Daten sind.
Es gibt Systeme die können gar keine "Zeiger" auf I/O.
Da haben wir gar noch nicht von "Zeiger" auf Code (Funktionen) 
gesprochen...

Zum Glück gibt es auf der anderen Seite auch Systeme bei denen alles 
orthogonal ist.

Ich hab Zeiger in Anf.strichen geschrieben weil es auf den Blickwinkel 
ankommt:
* physisch (von u.n.o.; Assembler) kommen die Unterschiede von 
separaten Bussysteme (RAM, ROM, I/O) welche nicht alle zwingend gleiche 
Breite in Bits haben und Maschineninstruktionen welche nicht alle Sorten 
(Daten, Code) verarbeiten können.
* logisch (von o.n.u.; Hochsprache) kommen die Unterschiede vom 
Verwendungszweck, der Absicht; also Äpfel (Zutaten) und Geschirr 
(Verarbeitung) gehören nun mal nicht mitenander verrechnet.


Klassischer Vergleiche:
- 80xx vs. 68xx (aus d. Scheibenwelt)
- x86 vs. SPARC (aus d. Planetensystem)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rolf M. schrieb:
>> Aus diesem Grunde schreibt der C-Standard auch vor, dass portabler
>> Code keine Zeiger zwischen Funktionen und Objekten hin und her
>> konvertieren darf.
>
> Eigentlich schreibt er sogar vor, dass man es gar nicht kann. Die Zeiger
> sind inkompatibel, und es müsste eine Fehlermeldung kommen.

Formal kannst du es als Zwischenschritt über eine Ganzzahl auffassen.
Wandlung eines Zeigers zu und von einer Ganzzahl ist zulässig (da
steht im Standard auch “any pointer type” und nicht “any object
pointer”), wobei das Verhalten dann implementierungsabhängig ist.
Undefiniert wird es nur, wenn der Wertebereich für die Ganzzahl durch
die Wandlung überschritten wird.

Damit ist natürlich zumindest abgedeckt, dass eine konkrete
Implementierung halt etwas wie das schon genannte dlopen() auch
implementieren darf.

von Rolf M. (rmagnus)


Lesenswert?

Jörg W. schrieb:
> Damit ist natürlich zumindest abgedeckt, dass eine konkrete
> Implementierung halt etwas wie das schon genannte dlopen() auch
> implementieren darf.

Aber wer castet schon den Rückgabewert erstmal in einen integer und dann 
in einen Funktionszeiger?
Der Compiler dürfte es eigentlich nicht zulassen, den Rückgabewert von 
dlsym() direkt in einen Funktionszeiger zu konvertieren, auch nicht mit 
einem Cast.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

Casts zwischen Objekt- und Funktonszeigern sind im C-Standard als
"common Extension" aufgeführt:

1
J.5 Common extensions
2
3
...
4
5
J.5.7 Function pointer casts
6
7
A pointer to an object or to void may be cast to a pointer to a
8
function, allowing data to be invoked as a function (6.5.4).
9
10
A pointer to a function may be cast to a pointer to an object or to
11
void, allowing a function to be inspected or modified (for example, by a
12
debugger) (6.5.4).

Ein explizites Verbot dieser Casts in unerweitertem C habe ich im
C-Standard nicht gefunden. Ich schätze, es ist bewusst weggelassen bzw.
aufgweicht worden, um einen Konflikt mit einem anderen Standard, nämlich
POSIX, zu vermeiden, der explizit die Möglichkeit dieser Casts fordert.

von Uhu U. (uhu)


Lesenswert?

Peter D. schrieb:
> Bei einer Funktion muß ein CALL ausgeführt werden, bei einer Variable
> ein Lese oder Schreibzugriff (LD, ST).

Das hat aber nix mit der Implementierung des Pointers zu tun.

von Kernalhacker (Gast)


Lesenswert?

Uhu U. schrieb:
> Das hat aber nix mit der Implementierung des Pointers zu tun.

Indirekt schon, wenn die Adressen in verschiedenen Adressräumen sind, 
die unterschiedlich groß und ausgeprägt sein können (z.B. 
byte/wortadressierbarkeit, Segmentierung). Damit ändert sich dann das 
Bitformat des Zeigers.

von Rolf M. (rmagnus)


Lesenswert?

Yalu X. schrieb:
> Ein explizites Verbot dieser Casts in unerweitertem C habe ich im
> C-Standard nicht gefunden.

Hmm, ich dachte, es stünde irgendwo explizit, aber hab's jetzt auch 
nicht finden können. Es scheint also weder explizit verboten, noch 
explizit erlaubt zu sein.

Uhu U. schrieb:
> Peter D. schrieb:
>> Bei einer Funktion muß ein CALL ausgeführt werden, bei einer Variable
>> ein Lese oder Schreibzugriff (LD, ST).
>
> Das hat aber nix mit der Implementierung des Pointers zu tun.

Doch, schon. Eine Dereferenzierung macht ganz unterschiedliche Dinge.

von Uhu U. (uhu)


Lesenswert?

Rolf M. schrieb:
> Uhu U. schrieb:
>> Peter D. schrieb:
>>> Bei einer Funktion muß ein CALL ausgeführt werden, bei einer Variable
>>> ein Lese oder Schreibzugriff (LD, ST).
>>
>> Das hat aber nix mit der Implementierung des Pointers zu tun.
>
> Doch, schon. Eine Dereferenzierung macht ganz unterschiedliche Dinge.

Das sind Operationen auf Pointern, nicht der Pointer selbst.

von Rolf M. (rmagnus)


Lesenswert?

Uhu U. schrieb:
> Das sind Operationen auf Pointern, nicht der Pointer selbst.

Was ist dann für dich "die Implementation", wenn nicht das, was hinter 
den Kulissen abläuft, wenn man ihn benutzt? Etwas die Tastsache, dass es 
in Form einer bestimmten Anzahl an Bytes im Speicher steht?

von Peter D. (peda)


Lesenswert?

Uhu U. schrieb:
> Das sind Operationen auf Pointern, nicht der Pointer selbst.

Na wenn nicht so, dann gibt es gar keine Unterscheide, in C sind alle 
Ausdrücke nur Zahlen.
Ob Werte, Variablen oder Funktionen, alles wurscht.
Allein die Verwendung entscheidet, was aus der Zahl compiliert wird.
Z.B. hier mal die 0 als Funktionspointer:
1
  ((void(*)())0)();

von Mikro 7. (mikro77)


Lesenswert?

Bei "Implementierung" habe ich bspw. an Größe (notwendige Anzahl der 
Bits/Bytes) gedacht und was dort genau abgelegt ist. Hier was 
interessantes dazu...

http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Mikro 7. schrieb:
> Bei "Implementierung" habe ich bspw. an Größe (notwendige Anzahl der
> Bits/Bytes) gedacht und was dort genau abgelegt ist.

Das würd ich jetzt als (Binär)Darstellung bezeichnen.

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.