Warum sind Funktionszeiger und Datenzeiger in C / C ++ nicht kompatibel?

130

Ich habe gelesen, dass das Konvertieren eines Funktionszeigers in einen Datenzeiger und umgekehrt auf den meisten Plattformen funktioniert, aber nicht garantiert funktioniert. Warum ist das so? Sollten nicht beide einfach Adressen im Hauptspeicher sein und daher kompatibel sein?

Gexizid
quelle
16
In Standard C undefiniert, in POSIX definiert. Achten Sie auf den Unterschied.
Ephemient
Ich bin ein bisschen neu darin, aber solltest du nicht die Besetzung auf der rechten Seite des "=" machen? Sieht für mich so aus, als ob das Problem darin besteht, dass Sie einen ungültigen Zeiger zuweisen. Aber ich sehe, dass die Manpage dies tut, also kann mich hoffentlich jemand erziehen. Ich sehe Beispiele im Netz der Leute, die den Rückgabewert von dlsym gießen, zB hier: daniweb.com/forums/thread62561.html
JasonWoof
9
Beachten Sie, was POSIX im Abschnitt über Datentypen sagt : §2.12.3 Zeigertypen. Alle Funktionszeigertypen müssen dieselbe Darstellung haben wie der Typzeiger auf void. Die Konvertierung eines Funktionszeigers in void *darf die Darstellung nicht verändern. Ein void *Wert, der sich aus einer solchen Konvertierung ergibt, kann ohne Informationsverlust mithilfe einer expliziten Umwandlung wieder in den ursprünglichen Funktionszeigertyp konvertiert werden. Hinweis : Der ISO C-Standard verlangt dies nicht, ist jedoch für die POSIX-Konformität erforderlich.
Jonathan Leffler
2
Dies ist die Frage im Abschnitt ÜBER dieser Website. :) :)
Wir
1
@KeithThompson: Die Welt verändert sich - und POSIX auch. Was ich 2012 geschrieben habe, gilt 2018 nicht mehr. Der POSIX-Standard hat die Aussprache geändert. Es ist jetzt verknüpft mit dlsym()- beachten Sie das Ende des Abschnitts 'Anwendungsnutzung', in dem es heißt: Beachten Sie, dass die Konvertierung von einem void *Zeiger in einen Funktionszeiger wie in: fptr = (int (*)(int))dlsym(handle, "my_function"); nicht durch den ISO C-Standard definiert ist. Dieser Standard erfordert, dass diese Konvertierung bei konformen Implementierungen ordnungsgemäß funktioniert.
Jonathan Leffler

Antworten:

171

Eine Architektur muss Code und Daten nicht im selben Speicher speichern. Bei einer Harvard-Architektur werden Code und Daten in einem völlig anderen Speicher gespeichert. Die meisten Architekturen sind Von Neumann-Architekturen mit Code und Daten im selben Speicher, aber C beschränkt sich nicht nur auf bestimmte Arten von Architekturen, wenn dies überhaupt möglich ist.

Dirk Holsopple
quelle
15
Selbst wenn Code und Daten an derselben Stelle in der physischen Hardware gespeichert sind, verhindern Software- und Speicherzugriff häufig, dass Daten als Code ohne "Genehmigung" des Betriebssystems ausgeführt werden. DEP und dergleichen.
Michael Graczyk
15
Mindestens so wichtig wie unterschiedliche Adressräume (möglicherweise wichtiger) ist, dass Funktionszeiger eine andere Darstellung haben können als Datenzeiger.
Michael Burr
14
Sie müssen nicht einmal über eine Harvard-Architektur verfügen, um Code- und Datenzeiger mit unterschiedlichen Adressräumen zu haben - das alte DOS-Speichermodell "Small" hat dies getan (in der Nähe von Zeigern mit CS != DS).
Café
1
Selbst moderne Prozessoren würden mit einer solchen Mischung zu kämpfen haben, da der Befehl und der Datencache normalerweise getrennt behandelt werden, selbst wenn das Betriebssystem es Ihnen ermöglicht, irgendwo Code zu schreiben.
PypeBros
3
@EricJ. Bis Sie aufrufen VirtualProtect, um Datenbereiche als ausführbar zu markieren.
Dietrich Epp
37

Einige Computer haben (hatten) separate Adressräume für Code und Daten. Auf solcher Hardware funktioniert es einfach nicht.

Die Sprache wurde nicht nur für aktuelle Desktop-Anwendungen entwickelt, sondern ermöglicht auch die Implementierung auf einer großen Menge von Hardware.


Es scheint, als hätte das C-Sprachkomitee nie beabsichtigt void*, ein Zeiger auf die Funktion zu sein, sondern nur einen generischen Zeiger auf Objekte.

Die C99-Begründung lautet:

6.3.2.3 Zeiger
C wurde jetzt auf einer Vielzahl von Architekturen implementiert. Während einige dieser Architekturen einheitliche Zeiger aufweisen, die die Größe eines ganzzahligen Typs haben, kann maximal portabler Code keine notwendige Entsprechung zwischen verschiedenen Zeigertypen und den ganzzahligen Typen annehmen. Bei einigen Implementierungen können Zeiger sogar breiter sein als jeder Ganzzahltyp.

Die Verwendung von void*("Zeiger auf void") als generischer Objektzeigertyp ist eine Erfindung des C89-Komitees. Die Übernahme dieses Typs wurde durch den Wunsch angeregt, Funktionsprototypargumente anzugeben, die entweder beliebige Zeiger (wie in fread) leise konvertieren oder sich beschweren, wenn der Argumenttyp nicht genau übereinstimmt (wie in strcmp). Über Zeiger auf Funktionen, die möglicherweise nicht mit Objektzeigern und / oder Ganzzahlen übereinstimmen, wird nichts gesagt.

Hinweis Über Zeiger auf Funktionen im letzten Absatz wird nichts gesagt . Sie können sich von anderen Hinweisen unterscheiden, und das ist sich des Ausschusses bewusst.

Bo Persson
quelle
Der Standard könnte sie kompatibel machen, ohne dies zu beeinträchtigen, indem einfach die Datentypen gleich groß gemacht werden und garantiert wird, dass die Zuweisung zu einem und dann zurück denselben Wert ergibt. Sie tun dies mit void *, dem einzigen Zeigertyp, der mit allem kompatibel ist.
Edward Strange
15
@CrazyEddie Sie können a keinen Funktionszeiger zuweisen void *.
Ouah
4
Ich könnte mich irren, wenn ich Funktionszeiger für void * akzeptiere, aber der Punkt bleibt. Bits sind Bits. Der Standard könnte verlangen, dass die Größe der verschiedenen Typen die Daten voneinander aufnehmen kann und die Zuweisung garantiert funktioniert, selbst wenn sie in verschiedenen Speichersegmenten verwendet werden. Der Grund für diese Inkompatibilität liegt darin, dass dies NICHT durch den Standard garantiert wird und daher Daten bei der Zuordnung verloren gehen können.
Edward Strange
5
Das Erfordernis sizeof(void*) == sizeof( void(*)() )würde jedoch Platz verschwenden, wenn Funktionszeiger und Datenzeiger unterschiedliche Größen haben. Dies war ein häufiger Fall in den 80er Jahren, als der erste C-Standard geschrieben wurde.
Robᵩ
8
@RichardChambers: Die unterschiedlichen Adreßräumen auch andere Adressen aufweisen Breiten , wie beispielsweise einen Atmel AVR dass verwendet 16 Bits für Befehle und 8 Bits für die Daten; In diesem Fall wäre es schwierig, von Datenzeigern (8 Bit) in Funktionszeiger (16 Bit) und wieder zurück zu konvertieren. C soll einfach zu implementieren sein; Ein Teil dieser Leichtigkeit besteht darin, dass Daten- und Anweisungszeiger nicht miteinander kompatibel sind.
John Bode
30

Für diejenigen, die sich an MS-DOS, Windows 3.1 und älter erinnern, ist die Antwort recht einfach. All dies unterstützte mehrere verschiedene Speichermodelle mit unterschiedlichen Merkmalskombinationen für Code- und Datenzeiger.

So zum Beispiel für das Compact-Modell (kleiner Code, große Datenmengen):

sizeof(void *) > sizeof(void(*)())

und umgekehrt im mittleren Modell (großer Code, kleine Daten):

sizeof(void *) < sizeof(void(*)())

In diesem Fall hatten Sie keinen separaten Speicher für Code und Datum, konnten jedoch nicht zwischen den beiden Zeigern konvertieren (ohne nicht standardmäßige Modifikatoren __near und __far).

Darüber hinaus gibt es keine Garantie dafür, dass die Zeiger, selbst wenn sie dieselbe Größe haben, auf dasselbe verweisen - im DOS Small-Speichermodell werden sowohl Code als auch Daten in der Nähe von Zeigern verwendet, sie zeigen jedoch auf unterschiedliche Segmente. Wenn Sie also einen Funktionszeiger in einen Datenzeiger konvertieren, erhalten Sie keinen Zeiger, der überhaupt eine Beziehung zur Funktion hat, und daher war eine solche Konvertierung nicht sinnvoll.

Tomek
quelle
Betreff: "Das Konvertieren eines Funktionszeigers in einen Datenzeiger würde Ihnen keinen Zeiger geben, der überhaupt eine Beziehung zur Funktion hat, und daher gab es keine Verwendung für eine solche Konvertierung": Dies folgt nicht vollständig. Wenn Sie ein int*in ein void*konvertieren, erhalten Sie einen Zeiger, mit dem Sie eigentlich nichts anfangen können, aber es ist trotzdem nützlich, die Konvertierung durchführen zu können. (Dies liegt daran void*, dass jeder Objektzeiger gespeichert werden kann und daher für generische Algorithmen verwendet werden kann, die nicht wissen müssen, welchen Typ sie enthalten. Dasselbe könnte auch für Funktionszeiger nützlich sein, wenn dies zulässig wäre.)
ruakh
4
@ruakh: Bei der Umwandlung der int *zu void *, die void *auf dasselbe Objekt zumindest Punkt gewährleistet ist wie das Original int *hat - so ist dies nützlich für die generischen Algorithmen , dass der Zugang des spitzen-zu - Objekt, wie int n; memcpy(&n, src, sizeof n);. In dem Fall, in dem das Konvertieren eines Funktionszeigers in a void *keinen Zeiger ergibt, der auf die Funktion zeigt, ist dies für solche Algorithmen nicht sinnvoll. Das einzige, was Sie tun können, ist, das void *Zurück erneut in einen Funktionszeiger zu konvertieren Verwenden Sie einfach einen Zeiger unionmit einem void *und einer Funktion.
Café
@caf: Fair genug. Vielen Dank für den Hinweis. Und im Übrigen wäre es eine schlechte Idee für die Leute, sie weiterzugeben , selbst wenn void* sie auf die Funktion hinweisen würden memcpy. :-P
Ruakh
Von oben kopiert : Beachten Sie, was POSIX unter Datentypen sagt : §2.12.3 Zeigertypen. Alle Funktionszeigertypen müssen dieselbe Darstellung haben wie der Typzeiger auf void. Die Konvertierung eines Funktionszeigers in void *darf die Darstellung nicht verändern. Ein void *Wert, der sich aus einer solchen Konvertierung ergibt, kann ohne Informationsverlust mithilfe einer expliziten Umwandlung wieder in den ursprünglichen Funktionszeigertyp konvertiert werden. Hinweis : Der ISO C-Standard verlangt dies nicht, ist jedoch für die POSIX-Konformität erforderlich.
Jonathan Leffler
@caf Wenn es nur an einen Rückruf weitergeleitet werden soll, der den richtigen Typ kennt , bin ich nur an der Sicherheit von Hin- und Rückflügen interessiert, nicht an einer anderen Beziehung, die diese konvertierten Werte möglicherweise haben.
Deduplikator
23

Zeiger auf ungültig sollen einen Zeiger auf jede Art von Daten aufnehmen können - aber nicht unbedingt einen Zeiger auf eine Funktion. Einige Systeme haben andere Anforderungen an Zeiger auf Funktionen als Zeiger auf Daten (z. B. gibt es DSPs mit unterschiedlicher Adressierung für Daten gegenüber Code, mittleres Modell unter MS-DOS verwendet 32-Bit-Zeiger für Code, aber nur 16-Bit-Zeiger für Daten). .

Jerry Sarg
quelle
1
aber dann sollte die Funktion dlsym () nicht etwas anderes als eine Leere * zurückgeben. Ich meine, wenn die Leere * nicht groß genug für den Funktionszeiger ist, sind wir dann nicht schon fubared?
Manav
1
@Kickerkicker: Ja wahrscheinlich. Wenn Speicherplatz zur Verfügung steht, wurde der Rückgabetyp von dlsym in der E-Mail-Liste der OpenGroup ausführlich besprochen, wahrscheinlich vor 9 oder 10 Jahren. Nebenbei erinnere ich mich nicht, was (wenn überhaupt) daraus geworden ist.
Jerry Coffin
1
Du hast recht. Dies scheint eine ziemlich schöne (wenn auch veraltete) Zusammenfassung Ihres Punktes zu sein.
Manav
2
@LegoStormtroopr: Interessant, wie 21 Personen der Idee des Up-Votings zustimmen , aber nur etwa 3 haben dies tatsächlich getan. :-)
Jerry Coffin
13

Zusätzlich zu dem, was hier bereits gesagt wurde, ist es interessant, sich POSIX anzusehen dlsym():

Der ISO C-Standard verlangt nicht, dass Zeiger auf Funktionen hin und her auf Zeiger auf Daten geworfen werden können. In der Tat verlangt der ISO C-Standard nicht, dass ein Objekt vom Typ void * einen Zeiger auf eine Funktion enthalten kann. Implementierungen, die die XSI-Erweiterung unterstützen, erfordern jedoch, dass ein Objekt vom Typ void * einen Zeiger auf eine Funktion enthalten kann. Das Ergebnis der Konvertierung eines Zeigers auf eine Funktion in einen Zeiger auf einen anderen Datentyp (außer void *) ist jedoch noch nicht definiert. Beachten Sie, dass Compiler, die dem ISO C-Standard entsprechen, eine Warnung generieren müssen, wenn eine Konvertierung von einem void * -Zeiger in einen Funktionszeiger wie folgt versucht wird:

 fptr = (int (*)(int))dlsym(handle, "my_function");

Aufgrund des hier erwähnten Problems kann eine zukünftige Version entweder eine neue Funktion hinzufügen, um Funktionszeiger zurückzugeben, oder die aktuelle Schnittstelle kann zugunsten von zwei neuen Funktionen veraltet sein: eine, die Datenzeiger zurückgibt, und die andere, die Funktionszeiger zurückgibt.

Maxim Egorushkin
quelle
Bedeutet das, dass die Verwendung von dlsym zum Abrufen der Adresse einer Funktion derzeit unsicher ist? Gibt es derzeit einen sicheren Weg, dies zu tun?
Gexizid
4
Dies bedeutet, dass POSIX derzeit von einem Plattform-ABI verlangt, dass sowohl Funktions- als auch Datenzeiger sicher auf void*und zurück übertragen werden können.
Maxim Egorushkin
@gexicide Dies bedeutet, dass Implementierungen, die POSIX-kompatibel sind, die Sprache erweitert haben und dem undefinierten Verhalten gemäß dem Standard selbst eine implementierungsdefinierte Bedeutung geben. Es ist sogar als eine der gängigen Erweiterungen des C99-Standards aufgeführt, Abschnitt J.5.7 Funktionszeiger-Casts.
David Hammen
1
@DavidHammen Es ist keine Erweiterung der Sprache, sondern eine neue zusätzliche Anforderung. C muss nicht void*mit einem Funktionszeiger kompatibel sein, POSIX dagegen.
Maxim Egorushkin
9

C ++ 11 hat eine Lösung für die seit langem bestehende Nichtübereinstimmung zwischen C / C ++ und POSIX in Bezug auf dlsym(). Man kann reinterpret_casteinen Funktionszeiger in / von einem Datenzeiger konvertieren, solange die Implementierung diese Funktion unterstützt.

Aus dem Standard, 5.2.10 Abs. 8, "Das Konvertieren eines Funktionszeigers in einen Objektzeigertyp oder umgekehrt wird bedingt unterstützt." 1.3.5 definiert "bedingt unterstützt" als "Programmkonstrukt, für dessen Unterstützung keine Implementierung erforderlich ist".

David Hammen
quelle
Man kann, aber man sollte nicht. Ein konformer Compiler muss dafür eine Warnung generieren (die wiederum einen Fehler auslösen sollte, vgl. -Werror). Eine bessere (und nicht UB) Lösung besteht darin, einen Zeiger auf das von dlsym(dh void**) zurückgegebene Objekt abzurufen und diesen in einen Zeiger in einen Funktionszeiger umzuwandeln . Immer noch implementierungsdefiniert, aber keine Ursache für eine Warnung / einen Fehler mehr .
Konrad Rudolph
3
@KonradRudolph: Nicht einverstanden. Der "bedingt unterstützte" Wortlaut wurde speziell geschrieben, um ohne Vorwarnung zuzulassen dlsymund GetProcAddresszu kompilieren.
MSalters
@ MSalters Was meinst du mit "nicht einverstanden"? Entweder bin ich richtig oder falsch. In der dlsym-Dokumentation heißt es ausdrücklich, dass „Compiler, die dem ISO C-Standard entsprechen, eine Warnung generieren müssen, wenn versucht wird, einen void * -Zeiger in einen Funktionszeiger zu konvertieren“. Dies lässt nicht viel Raum für Spekulationen. Und GCC (mit -pedantic) nicht warnen. Auch hier ist keine Spekulation möglich.
Konrad Rudolph
1
Follow-up: Ich denke jetzt verstehe ich. Es ist nicht UB. Es ist implementierungsdefiniert. Ich bin mir immer noch nicht sicher, ob die Warnung generiert werden muss oder nicht - wahrscheinlich nicht. Naja.
Konrad Rudolph
2
@KonradRudolph: Ich war nicht einverstanden mit Ihrem "sollte nicht", was eine Meinung ist. In der Antwort wurde C ++ 11 ausdrücklich erwähnt, und ich war zum Zeitpunkt der Behebung des Problems Mitglied der C ++ CWG. C99 hat in der Tat einen anderen Wortlaut, bedingt unterstützt ist eine C ++ Erfindung.
MSalters
7

Abhängig von der Zielarchitektur können Code und Daten in grundlegend inkompatiblen, physikalisch unterschiedlichen Speicherbereichen gespeichert werden.

Graham Borland
quelle
Ich verstehe, "physisch verschieden", aber können Sie die "grundlegend inkompatible" Unterscheidung näher erläutern? Wie ich in der Frage sagte, ist ein ungültiger Zeiger nicht so groß wie irgendein Zeigertyp - oder ist das eine falsche Vermutung meinerseits.
Manav
@KnickerKicker: void *ist groß genug, um einen Datenzeiger aufzunehmen, aber nicht unbedingt einen Funktionszeiger.
Ephemient
1
zurück in die Zukunft: P
SSpoke
5

undefined bedeutet nicht unbedingt nicht erlaubt, es kann bedeuten, dass der Compiler-Implementierer mehr Freiheit hat, es so zu machen, wie er will.

Beispielsweise ist dies bei einigen Architekturen möglicherweise nicht möglich. Undefiniert ermöglicht es ihnen, weiterhin über eine konforme C-Bibliothek zu verfügen, auch wenn Sie dies nicht tun können.

Martin Beckett
quelle
5

Eine andere Lösung:

Unter der Annahme, dass POSIX garantiert, dass Funktions- und Datenzeiger dieselbe Größe und Darstellung haben (ich kann den Text dafür nicht finden, aber das angeführte Beispiel-OP legt nahe, dass sie zumindest beabsichtigen , diese Anforderung zu erfüllen), sollte Folgendes funktionieren:

double (*cosine)(double);
void *tmp;
handle = dlopen("libm.so", RTLD_LAZY);
tmp = dlsym(handle, "cos");
memcpy(&cosine, &tmp, sizeof cosine);

Dadurch wird vermieden, dass die Aliasing-Regeln verletzt werden, indem die char []Darstellung durchlaufen wird , mit der alle Typen aliasisiert werden dürfen.

Noch ein Ansatz:

union {
    double (*fptr)(double);
    void *dptr;
} u;
u.dptr = dlsym(handle, "cos");
cosine = u.fptr;

Aber ich würde den memcpyAnsatz empfehlen, wenn Sie absolut 100% korrektes C wollen.

R .. GitHub HÖREN SIE AUF, EIS ZU HELFEN
quelle
5

Es können verschiedene Typen mit unterschiedlichem Platzbedarf sein. Das Zuweisen zu einem kann den Wert des Zeigers irreversibel aufteilen, sodass das Zurückweisen zu etwas anderem führt.

Ich glaube, es können verschiedene Typen sein, weil der Standard mögliche Implementierungen nicht einschränken möchte, die Platz sparen, wenn sie nicht benötigt werden oder wenn die Größe dazu führen kann, dass die CPU zusätzlichen Mist machen muss, um sie zu verwenden, usw.

Edward Strange
quelle
3

Die einzige wirklich tragbare Lösung besteht darin, sie nicht dlsymfür Funktionen zu verwenden, sondern stattdessen dlsymeinen Zeiger auf Daten zu erhalten, die Funktionszeiger enthalten. Zum Beispiel in Ihrer Bibliothek:

struct module foo_module = {
    .create = create_func,
    .destroy = destroy_func,
    .write = write_func,
    /* ... */
};

und dann in Ihrer Bewerbung:

struct module *foo = dlsym(handle, "foo_module");
foo->create(/*...*/);
/* ... */

Im Übrigen ist dies ohnehin eine gute Entwurfspraxis und erleichtert die Unterstützung des dynamischen Ladens über dlopenund der statischen Verknüpfung aller Module auf Systemen, die keine dynamische Verknüpfung unterstützen oder bei denen der Benutzer / Systemintegrator keine dynamische Verknüpfung verwenden möchte.

R .. GitHub HÖREN SIE AUF, EIS ZU HELFEN
quelle
2
Nett! Obwohl ich damit einverstanden bin, dass dies wartbarer erscheint, ist es (für mich) immer noch nicht offensichtlich, wie ich darüber auf statische Verknüpfungen hämmere. Können Sie das näher erläutern?
Manav
2
Wenn jedes Modul eine eigene foo_moduleStruktur hat (mit eindeutigen Namen), können Sie einfach eine zusätzliche Datei mit einem Array von struct { const char *module_name; const struct module *module_funcs; }und einer einfachen Funktion erstellen , um diese Tabelle nach dem Modul zu durchsuchen, das Sie "laden" und den richtigen Zeiger zurückgeben möchten, und diese dann verwenden anstelle von dlopenund dlsym.
R .. GitHub STOP HELPING ICE
@R .. Stimmt, aber es erhöht die Wartungskosten, indem die Modulstruktur gewartet werden muss.
user877329
3

Ein modernes Beispiel dafür, wo sich Funktionszeiger in der Größe von Datenzeigern unterscheiden können: C ++ - Klassenmitgliedsfunktionszeiger

Direkt zitiert von https://blogs.msdn.microsoft.com/oldnewthing/20040209-00/?p=40713/

class Base1 { int b1; void Base1Method(); };
class Base2 { int b2; void Base2Method(); };
class Derived : public Base1, Base2 { int d; void DerivedMethod(); };

Es gibt jetzt zwei mögliche thisZeiger.

Ein Zeiger auf eine Mitgliedsfunktion von Base1kann als Zeiger auf eine Mitgliedsfunktion von verwendet werden Derived, da beide denselben this Zeiger verwenden. Ein Zeiger auf eine Elementfunktion von Base2kann jedoch nicht unverändert als Zeiger auf eine Elementfunktion von verwendet werden Derived, da der this Zeiger angepasst werden muss.

Es gibt viele Möglichkeiten, dies zu lösen. So entscheidet sich der Visual Studio-Compiler dafür:

Ein Zeiger auf eine Mitgliedsfunktion einer mehrfach vererbten Klasse ist wirklich eine Struktur.

[Address of function]
[Adjustor]

Die Größe einer Zeiger-zu-Mitglied-Funktion einer Klasse, die Mehrfachvererbung verwendet, entspricht der Größe eines Zeigers plus der Größe von a size_t.

tl; dr: Bei Verwendung der Mehrfachvererbung kann ein Zeiger auf eine Mitgliedsfunktion (abhängig von Compiler, Version, Architektur usw.) tatsächlich als gespeichert werden

struct { 
    void * func;
    size_t offset;
}

das ist offensichtlich größer als a void *.

Andrew Sun.
quelle
2

Auf den meisten Architekturen haben Zeiger auf alle normalen Datentypen dieselbe Darstellung, sodass das Umwandeln zwischen Datenzeigertypen ein No-Op ist.

Es ist jedoch denkbar, dass Funktionszeiger eine andere Darstellung erfordern, möglicherweise sind sie größer als andere Zeiger. Wenn void * Funktionszeiger enthalten könnte, würde dies bedeuten, dass die Darstellung von void * größer sein müsste. Und alle Abgüsse von Datenzeigern auf / von void * müssten diese zusätzliche Kopie ausführen.

Wie bereits erwähnt, können Sie dies bei Bedarf mithilfe einer Gewerkschaft erreichen. Die meisten Verwendungen von void * beziehen sich jedoch nur auf Daten. Daher wäre es mühsam, die gesamte Speichernutzung zu erhöhen, falls ein Funktionszeiger gespeichert werden muss.

Barmar
quelle
-1

Ich weiß , dass dies nicht auf seit 2012 kommentiert worden, aber ich dachte , es wäre nützlich , hinzufügen , dass ich tun wissen , eine Architektur , die hat sehr auf dieser Architektur überprüft Privileg , da einen Anruf nicht kompatibel Zeiger für Daten und Funktionen und führt zusätzliche Informationen. Keine Menge Casting wird helfen. Es ist die Mühle .

phorgan1
quelle
Diese Antwort ist falsch. Sie können beispielsweise einen Funktionszeiger in einen Datenzeiger konvertieren und daraus lesen (wenn Sie die Berechtigung haben, wie gewohnt von dieser Adresse zu lesen). Das Ergebnis ist genauso sinnvoll wie zB auf x86.
Manuel Jacob