Bedeutung von int (*) (int *) = 5 (oder einem beliebigen ganzzahligen Wert)

87

Ich kann das nicht herausfinden:

int main() {
    int (*) (int *) = 5;
    return 0;
}

Die obige Zuordnung wird mit g ++ c ++ 11 kompiliert. Ich weiß, dass dies int (*) (int *)ein Zeiger auf eine Funktion ist, die ein (int *)Argument als akzeptiert und ein int zurückgibt, aber ich verstehe nicht, wie Sie es mit 5 gleichsetzen können. Zuerst dachte ich, es ist eine Funktion, die ständig 5 zurückgibt (aus meinem letzten Lernen in F #, wahrscheinlich, haha), dann dachte ich kurz, dass der Funktionszeiger auf Speicherplatz 5 zeigt, aber das funktioniert eindeutig nicht und Hex-Werte auch nicht.

Da ich dachte, dass dies daran liegen könnte, dass die Funktion ein int zurückgibt und dass das Zuweisen eines int (irgendwie) in Ordnung ist, habe ich auch Folgendes versucht:

int * (*) (int *) = my_ptr

Dabei my_ptrhandelt es sich um int *den gleichen Typ wie dieser zweite Funktionszeiger, wie im ersten Fall mit dem Typ int. Dies wird nicht kompiliert. Das Zuweisen von 5 oder eines beliebigen int-Werts anstelle von my_ptrwird auch für diesen Funktionszeiger nicht kompiliert.

Was bedeutet die Aufgabe?

Update 1

Wir haben die Bestätigung, dass es sich um einen Fehler handelt, wie in der besten Antwort gezeigt. Es ist jedoch immer noch nicht bekannt, was tatsächlich mit dem Wert geschieht , den Sie dem Funktionszeiger zuweisen, oder was mit der Zuweisung geschieht. Alle (guten) Erklärungen dazu wären sehr dankbar! Weitere Informationen zum Problem finden Sie in den folgenden Änderungen.

Bearbeiten 1

Ich benutze gcc Version 4.8.2 (in Ubuntu 4.8.2)

Bearbeiten 2

Eigentlich funktioniert es auf meinem Compiler, es mit irgendetwas gleichzusetzen. Sogar das Gleichsetzen mit einer std :: string-Variablen oder einem Funktionsnamen, der ein Double zurückgibt, funktioniert.

Bearbeiten 2.1

Interessanterweise kann es kompiliert werden, wenn es zu einem Funktionszeiger auf eine Funktion gemacht wird, die einen Datentyp zurückgibt, der kein Zeiger ist, z

std::string (*) () = 5.6;

Sobald sich der Funktionszeiger jedoch auf eine Funktion bezieht, die einen Zeiger zurückgibt, wird er nicht kompiliert, z. B. mit

some_data_type ** (*) () = any_value;
Konrad Kapp
quelle
3
Hmm ... es sieht nicht richtig aus und Clang akzeptiert es nicht. Könnte eine gcc-Erweiterung (oder ein Fehler) sein.
Wintermute
4
g ++ kompiliert, aber gcc funktioniert nicht:error: expected identifier or '(' before ')' token
tivn
3
@ 0x499602D Beachten Sie, dass der Code dem Zeiger keinen Namen gibt. Mit int *x = 5dir hat es benannt x. Damit int * (*x) (int *) = 5wird nicht kompiliert. (obwohl das als C-Code kompiliert wird).
Nr.
5
Reduzierter Testfall: int(*) = 5;undint(*);
Johannes Schaub - litb

Antworten:

59

Es ist ein Fehler in g ++.

 int (*) (int *) 

ist ein Typname.

In C ++ können Sie keine Deklaration mit einem Typnamen ohne Bezeichner haben.

Das kompiliert also mit g ++.

 int (*) (int *) = 5;

und dies kompiliert auch:

 int (*) (int *);

aber sie sind beide ungültige Erklärungen.

EDIT :

TC erwähnt in den Kommentaren Bugzilla Bug 60680 mit einem ähnlichen Testfall, der jedoch noch nicht genehmigt wurde . Der Fehler wird in Bugzilla bestätigt.

EDIT2 :

Wenn sich die beiden obigen Deklarationen im Dateibereich befinden, gibt g ++ eine Diagnose korrekt aus (die Diagnose wird im Blockbereich nicht ausgegeben).

EDIT3 :

Ich habe das Problem in der neuesten Version von g ++ Version 4 (4.9.2), der neuesten Vorabversion 5 (5.0.1 20150412) und der neuesten experimentellen Version 6 (6.0.0 20150412) überprüft und kann es reproduzieren.

ouah
quelle
5
MSVC lehnte den bearbeiteten veröffentlichten Code miterror C2059: syntax error : ')'
Wetterfahne
Wenn es sich um einen Typnamen handelt, warum nicht 'int (*) (int *) int_func;' Arbeit?
Konrad Kapp
1
Für den GCC-Bugzilla ist "NEU" ein bestätigter Fehler. (Nicht bestätigte Fehler sind "NICHT BESTÄTIGT").
TC
4
@KonradKapp: Es funktioniert einwandfrei, wenn Sie sagen, int (*int_func)(int *); welcher einen Funktionszeiger mit dem Namen deklariert int_func.
Edward
3
@KonradKapp C ++ verwendet die Infixnotation zum Platzieren des Bezeichners. der gleiche Grund ist es int x[5];und nichtint[5] x;
MM
27

Es ist nicht gültig C ++. Denken Sie daran, dass Ihr bestimmter Compiler, der gerade kompiliert, nicht gültig ist. Compiler haben, wie jede komplexe Software, manchmal Fehler, und dies scheint einer zu sein.

Im Gegensatz dazu clang++beschwert sich:

funnycast.cpp:3:11: error: expected expression
    int (*) (int *) = 5;
          ^
funnycast.cpp:3:18: error: expected '(' for function-style cast or type construction
    int (*) (int *) = 5;
             ~~~ ^
funnycast.cpp:3:19: error: expected expression
    int (*) (int *) = 5;
                  ^
3 errors generated.

Dies ist das erwartete Verhalten, da die fehlerhafte Zeile in C ++ nicht gültig ist. Es gibt vor, eine Zuweisung zu sein (wegen der =), enthält jedoch keine Kennung.

Edward
quelle
9

Wie andere Antworten gezeigt haben, ist es ein Fehler, dass

int (*) (int *) = 5;

kompiliert. Eine vernünftige Annäherung an diese Aussage, von der erwartet wird, dass sie eine Bedeutung hat, ist:

int (*proc)(int*) = (int (*)(int*))(5);

Jetzt procist ein Zeiger auf eine Funktion, der erwartet, dass die Adresse 5die Basisadresse einer Funktion ist, die eine nimmt int*und eine zurückgibt int.

Bei einigen Mikrocontrollern / Mikroprozessoren kann 5es sich um eine gültige Code-Adresse handeln, und es kann möglich sein, eine solche Funktion dort zu lokalisieren.

Auf den meisten Universalcomputern ist die erste Speicherseite (Adressen 0-1023für 4K-Seiten) absichtlich ungültig (nicht zugeordnet), um sie abzufangennull .

Während das Verhalten von der Plattform abhängt, kann man vernünftigerweise erwarten, dass ein Seitenfehler auftritt, wenn er *procaufgerufen wird (z (*proc)(&v). B. ). Vor der Zeit, zu der*proc aufgerufen wird, geschieht nichts Ungewöhnliches.

Wenn Sie keinen dynamischen Linker schreiben, sollten Sie Adressen mit ziemlicher Sicherheit nicht numerisch berechnen und sie Zeiger-Funktions-Variablen zuweisen.

Atsby
quelle
2
/usr/lib/gcc/x86_64-pc-cygwin/4.9.2/cc1plus.exe -da so.cpp

Diese Befehlszeile generiert viele Zwischendateien. Der erste von ihnen so.cpp.170r.expandsagt:

...
int main() ()
{
  int D.2229;
  int _1;

;;   basic block 2, loop depth 0
;;    pred:       ENTRY
  _1 = 0;
;;    succ:       3

;;   basic block 3, loop depth 0
;;    pred:       2
<L0>:
  return _1;
;;    succ:       EXIT

}
...

Dies beantwortet immer noch nicht genau, was passiert, aber es sollte ein Schritt in die richtige Richtung sein.

Roland Illig
quelle
Interessant. Was ist der Zweck dieser Zwischendateien?
Konrad Kapp
@KonradKapp Das Erstellen von Maschinencode aus menschlichem Code ist ein recht komplexer Prozess (insbesondere, wenn Ihr Compiler seine Ausgabe optimieren soll). Da das Kompilieren so komplex ist, dass es nicht in einem Schritt ausgeführt wird, haben die meisten Compiler eine Form der Intermediate Representation (IR).
11684
2
Ein weiterer Grund für eine IR ist, dass Sie bei einer genau definierten IR das Front-End und das Back-End Ihres Compilers trennen können. (ZB kompiliert Front-End C zu Ihrem IR, Back-End kompiliert IR zu Intel-Maschinencode. Wenn Sie nun ARM-Unterstützung hinzufügen möchten, benötigen Sie nur ein zweites Back-End. Und wenn Sie Go kompilieren möchten, benötigen Sie nur ein zweites Front-End, und
obendrein
@ 11684 OK, macht Sinn. Sehr interessant. Ich konnte nicht feststellen, welche Sprache Roland in dieser Antwort gab ... es sieht aus wie eine Art Versammlung gemischt mit C.
Konrad Kapp
IR muss nicht druckbar sein; Ich habe keine Ahnung, was gcc verwendet, dies kann nur eine druckbare Darstellung sein @KonradKapp
11684