2016-11-29 5 views
-1

Betrachten Sie den folgenden Code ein:Store-Funktion Adresse in die globale Variable

void f() {}; 
void* v = f; 
int i = f; 
int main() { } 

Warum Adresse in int variable Funktion speichert gibt mir eine error:

error: initializer element is not a compile-time constant

Aber für void* Variable nicht?

+3

Sie können einen Elefanten nicht in ein Python-Nest stecken. BTW, Initialisierer für globale Objekte sollte konstant sein. – haccks

+3

Wer hat Ihnen gesagt, dass Int die Adresse einer Variablen speichern kann? Mit anderen Worten: ist "sizeof (int)" gleich "sizeof (void *)"? – LPs

+3

Warum möchten Sie es tun? Sie haben Funktionszeiger für diese – sas

Antwort

2

Wenn ich dies zu kompilieren, erhalte ich:

$ gcc foo.c 

foo.c:3:5: warning: incompatible pointer to integer conversion initializing 'int' with an expression of type 'void()' [-Wint-conversion] 
int i = f; 
    ^ ~ 
foo.c:3:9: error: initializer element is not a compile-time constant 
int i = f; 
     ^
1 warning and 1 error generated. 

Wirklich, ich glaube, dass Warnung sollte ein Fehler sein. Sie versuchen, eine Adresse in eine ganze Zahl zu setzen, und das ist in der Regel schlechte Form.

Das heißt, wenn der Compiler vorausgeht und die Konvertierung vornimmt, ist das Ergebnis keine Kompilierzeitkonstante (weil es ein konvertierter Wert ist). Also der Fehler.

Obwohl Sie nicht nach C++ fragen, war ich neugierig, wie sich die beiden Sprachen unterscheiden, also überprüfte ich, wie sich mein Compiler verhalten hat. In dieser Sprache sind beide Aufträge aus f aus gutem Grund illegal. void* ist kein Zeiger auf eine parameterlose Funktion, und int ist auch nicht.

$ g++ foo.c 
clang: warning: treating 'c' input as 'c++' when in C++ mode, this behavior is deprecated 
foo.c:2:7: error: cannot initialize a variable of type 'void *' with an lvalue of type 'void()' 
void* v = f; 
    ^ ~ 
foo.c:3:5: error: cannot initialize a variable of type 'int' with an lvalue of type 'void()' 
int i = f; 
    ^ ~ 
2 errors generated. 
+0

Diese Zuweisungen sind auch in C nicht erlaubt. Die Frage betrifft C, nicht C++. – Lundin

+0

Sie haben Recht, dass es eine C-Frage ist. Ich werde abändern, um klarzustellen, dass ich das nur für meine eigene Neugierde hinzugefügt habe. Ich versuchte verschiedene C-Dialekte, um zu sehen, ob sich irgendjemand darüber beschweren würde, die Funktion in eine void * umzuwandeln, und keine tat. Mir ist natürlich klar, dass dies ein Bug im Klirren sein könnte, aber ich bin geneigt zu denken, dass es eher ein undefiniertes Verhalten ist, als dass es verboten ist. Insbesondere alle diese gab das gleiche Ergebnis: gcc -stc = c90 foo.c', gcc -std = iso9899: 1999 foo.c', gcc -std = iso9899: 2011 foo.c ' – burlyearly

0

Die Initialisierungen beide v und i illegal sind.

Einige Compiler ermöglichen jedoch die Konvertierung von einem Funktionszeiger zu einer void* als Compiler-Erweiterung.

Versuchen Sie, Ihre Warnungen zusammenzufassen (wie Sie es aus Gewohnheit sollten), und Sie können Warnungen auch auf der void* v = f; Linie bekommen.

Ein richtiger Weg, um einen Zeiger auf die Funktion zum Speichern f

void (*p)() = f; 
3
  • Variablen in Dateigültigkeitsbereich deklariert werden würde („global“) haben statische Speicherdauer.

    Alle Variablen mit statischer Speicherdauer müssen mit einer Kompilierzeitkonstante initialisiert werden. In C zählen andere Variablen (nicht einmal const Einsen) nicht als Kompilierzeitkonstanten, daher der Fehler.

  • Darüber hinaus können Sie einem Integer keinen Zeiger zuweisen. Dies ist nicht zulässig. C. Sie müssen den Zeiger manuell zuerst in einen Integer-Typ konvertieren, indem Sie ihn umwandeln.

  • Darüber hinaus ist die Besetzung void* v = f; nicht gut definiert. Der C-Standard spezifiziert nur Umwandlungen zwischen void* und Zeiger auf den Objekttyp.


Nun, was Sie sollte diese Adresse einer Funktion erhalten tun:

#include <stdint.h> 

uintptr_t i = (uintptr_t) f; 

int main (void) 
{ ... 

uintptr_t garantiert ist groß genug sein, um die Adresse eines Zeigers zu enthalten.

+0

Bedeutet' irgendein Zeiger' auch Zeigerfunktion (nicht nur für Objekte)? – alexolut

+0

@alexolut Eigentlich, streng genommen, denke ich, dass der C-Standard nur Zeiger auf void erwähnt. In der Praxis funktioniert das Konvertieren von Funktionszeigern in Ganzzahlen jedoch immer, da die Ganzzahl unsigniert und groß genug ist. – Lundin

+0

Sie haben angegeben, dass die globale Variable mit der Kompilierzeitkonstante initialisiert werden muss, aber die Adresse der Funktion zur Kompilierzeit nicht bekannt ist. Wie kann es sogar der Variablen uintptr_t zugewiesen werden? – alexolut

0

Wenn Sie wirklich die zugrunde liegenden Bits eines Zeigers in eine int setzen wollte man dies versuchen könnte:

typedef union _UPTRINT 
{ 
    void *ptr; 
    int i; 
} UPTRINT; 

dann die Funktionszeiger auf UPTRINT::ptr und den Zugang als i zuordnen (unter der Annahme, dass Zeiger und ints sind die gleiche Größe auf Ihrer Plattform; passen Sie den Typ von i wie erforderlich an).

+0

Dies ist keineswegs garantiert zu arbeiten, es ist das gleiche wie ein Cast. 'void *' und 'int' haben möglicherweise nicht nur eine andere Größe (dieser Code wird auf einem 64-Bit-System und auf vielen 8- und 16-Bit-Systemen fehlschlagen), sie können eine andere Darstellung haben. Adressen werden normalerweise nicht signiert. – Lundin

+0

Ich habe das im letzten Satz meiner Antwort berücksichtigt. –

+0

Mein Punkt ist, es gibt keinen Grund, "int" überhaupt in diesen Prozess einzubeziehen, und auch keinen "home-brewn" -Typ zu verwenden. 'uintptr_t' existiert für Situationen wie diese, es ist die nächste Sache, die Sie zu einem portablen Integer-Typ bekommen, der Adressen speichern kann. – Lundin