Was ist über die Größe eines Funktionszeigers garantiert?
[*]
In C muss ich die Größe einer Struktur kennen, die Funktionszeiger enthält. Kann ich garantieren, dass auf allen Plattformen und Architekturen:
- die Größe eines void* ist die gleiche Größe wie ein Funktionszeiger?
- die Größe des Funktionszeigers unterscheidet sich nicht aufgrund seines Rückgabetyps?
- die Größe des Funktionszeigers unterscheidet sich nicht aufgrund seiner Parametertypen?
Ich nehme an, dass die Antwort auf alle diese Fragen Ja ist, aber ich möchte sicher sein. Für den Kontext rufe ich an sizeof(struct mystruct)
und nichts weiter.
Oliver Charlesworth
[*]
Aus C99-Spezifikation, Abschnitt 6.2.5, Absatz 27:
Ein Zeiger auf void muss die gleichen Darstellungs- und Ausrichtungsanforderungen haben wie ein Zeiger auf einen Zeichentyp. In ähnlicher Weise müssen Zeiger auf qualifizierte oder nicht qualifizierte Versionen kompatibler Typen dieselben Darstellungs- und Ausrichtungsanforderungen haben. Alle Zeiger auf Strukturtypen müssen die gleichen Darstellungs- und Ausrichtungsanforderungen haben. Alle Zeiger auf Union-Typen müssen untereinander die gleichen Darstellungs- und Ausrichtungsanforderungen haben. Zeiger auf andere Typen müssen nicht die gleichen Darstellungs- oder Ausrichtungsanforderungen haben.
Also nein; keine Garantie, dass a void *
kann einen Funktionszeiger enthalten.
Und Abschnitt 6.3.2.3, Absatz 8:
Ein Zeiger auf eine Funktion eines Typs kann in einen Zeiger auf eine Funktion eines anderen Typs umgewandelt werden und wieder zurück; das Ergebnis soll mit dem ursprünglichen Zeiger verglichen werden.
was impliziert, dass ein Funktionszeigertyp jeden anderen Funktionszeigerwert enthalten kann. Technisch gesehen ist das nicht dasselbe wie zu garantieren, dass die Größe von Funktionszeigertypen nicht variieren kann, sondern lediglich, dass ihre Werte den gleichen Bereich einnehmen.
Steve Jessop
[*]
Nein nein Nein.
C bevorzugt keine Harvard-Architekturen mit unterschiedlichen Code- und Datenzeigergrößen, da Sie beim Programmieren für eine solche Architektur idealerweise Daten im Programmspeicher speichern möchten (String-Literale und dergleichen), und dazu benötigen Sie Objektzeiger der Coderaum. Aber es verbietet es ihnen nicht, so dass Funktionszeiger laut Standard auf einen Adressraum verweisen können, der eine andere Größe als der Datenadressraum hat.
Allerdings kann jeder Funktionszeiger in einen anderen Funktionszeigertyp umgewandelt werden[*] und zurück, ohne den Wert zu löschen, auf die gleiche Weise, auf die jeder Objektzeiger umgewandelt werden kann void*
und zurück. Daher wäre es ziemlich überraschend, wenn Funktionszeiger je nach Signatur in der Größe variieren würden. Es gibt keinen offensichtlichen “Nutzen” für den zusätzlichen Speicherplatz, wenn Sie in der Lage sein müssen, den gleichen Wert irgendwie auf weniger Speicherplatz zu speichern und ihn dann beim Zurückwerfen abzurufen.
[*] Danke, Schot.
-
Allgemeiner: Jeder Funktionszeiger kann in jeden anderen Funktionszeigertyp und zurück konvertiert werden. (C99 6.3.2.3/8)
– schott
15. Oktober 2010 um 11:38 Uhr
-
@schot: Richtig, in diesem Fall wäre es wirklich pervers, sie unterschiedlich groß zu machen, da alle Funktionszeigertypen unabhängig von der Größe genau denselben Satz von Werten darstellen müssen. Nämlich die Adresse jeder Funktion plus einen Nullzeiger.
– Steve Jessop
15. Oktober 2010 um 11:41 Uhr
-
Ausgezeichnete Antwort. Meine Frage von neulich veranschaulicht mögliche Fallstricke hier: stackoverflow.com/questions/3889541/…
– Vicky
15. Oktober 2010 um 11:42 Uhr
-
“C unterstützt Harvard-Architekturen nicht wirklich”; Ich denke, das ist ein roter Hering. Es gibt viele solcher Architekturen, die C unterstützen!
– Oliver Charlesworth
15. Oktober 2010 um 11:45 Uhr
-
@Oli: Sicher, Sie können Sachen in den Datenraum kopieren, aber dann haben Sie (a), aber nicht (b). Sie belegen wertvollen Speicher, was eine programmierergesteuerte Optimierung motiviert. Mit “hässlichem Hack” meine ich nongnu.org/avr-libc/user-manual/pgmspace.html. Sie können dann keinen echten “Objekt”-Zeiger erhalten, auf den zB das Programm selbst übergehen könnte
memcpy
, weil memcpy nicht weiß, wie man es dereferenziert. Um das zu erreichen, müssten Sie aufgeben und einfach die Größe der Objektzeiger erhöhen, was wiederum wertvollen Speicher kostet und wahrscheinlich bei jedem Speicherzugriff einen Laufzeitwechsel erfordert!– Steve Jessop
15. Oktober 2010 um 12:08 Uhr
[*]
Zusätzlich zu den anderen Antworten sagt Wikipedia Folgendes:
http://en.wikipedia.org/wiki/Function_pointer
Obwohl Funktionszeiger in C und C++ als einfache Adressen implementiert werden können, ist dies normalerweise der Fall
sizeof(Fx)==sizeof(void *)
werden Member-Zeiger in C++ oft als “fette Zeiger” implementiert, typischerweise zwei- oder dreimal so groß wie ein einfacher Funktionszeiger, um mit virtueller Vererbung umzugehen.
Ein Funktionszeiger ist eine Abstraktion. Solange die Anforderungen der Norm erfüllt sind, ist alles möglich. Dh wenn Sie weniger als 256 Funktionen in Ihrem Programm haben, könnten Funktionszeiger implementiert werden, indem Sie ein einzelnes Byte mit dem Wert 0 für NULL und den Werten 1 bis 255 als Index in eine Tabelle mit den physikalischen Adressen verwenden. Wenn Sie 255 Funktionen überschreiten, könnte es erweitert werden, um 2 Bytes zu verwenden.