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
Uhu U. schrieb: > Nein. Doch. Grundsätzlich schon. Die Zeiger können gleich sein, müssen es aber nicht (z.B. bei Harvard).
bei C++ gibt es auch Funktionszeiger auf Memberfunktionen, die haben eine andere Größe, weil das Objekt und der Offset codiert werden müssen.
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
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.
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.
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.
Michael B. schrieb: > Ausnahmen gibt es also immer wieder, je nach kranker Architektir IBM-PC und Nachfahren und Klone. Also 80x86 im Real-Mode.
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
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.
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.
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)
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.
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
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.
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.
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.
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.
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.
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?
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)(); |
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.