2016-12-01 4 views
6

Ich habe einen Funktionszeiger, dessen Funktion als erwartet char * Argumente erklärt wird.Ist es, möchte ich Speichern Sie einen Zeiger auf eine Funktion, die als char const* Argumente deklariert wird.Übergeben von `int (*) (char const *)` wobei 'int (*) (char *) `erwartet wird

Ich denke, ich kann entweder einen Wrapper oder eine Besetzung verwenden. Casts scheinen einfacher zu sein, aber kann ich das Ergebnis einer solchen Funktion Pointer Cast legal nennen?

Beispiel-Code unten:

static int write_a(char * X){ 
    return 0; 
} 

static int write_b(char const* X){ 
    return 0; 
} 
static int wrapped_write_b(char * X){ 
    return write_b(X); 
} 

typedef int (*write_fn)(char *); 

write_fn a = write_a; 
write_fn b = wrapped_write_b; 
write_fn b1 = (write_fn)write_b; //is b1 legally callable? 
+5

Laut [dieser] (http://Stackoverflow.com/a/559671/669576) ist es UB (glaube ich). Da "const" jedoch nur zur Laufzeit und nicht zur Laufzeit aktiviert wird, glaube ich nicht, dass dies ein Problem sein wird. –

+0

@Danh: Die Besetzung ist vollkommen legal. Der von Ihnen angegebene Link steht überhaupt nicht mit diesem Cast in Zusammenhang. – AnT

Antwort

3

Genau genommen ist es nicht erlaubt.

Ein pointer-to-something ist nicht mit einem pointer-to-qualified-something kompatibel. Da ein pointer-to-qualified-something keine qualifizierte Art von pointer-to-something ist

Gleiches gilt für

pointer-to-function-accepting-something

und

pointer-to-function-accepting-qualified-something.

Zwei Typen haben kompatible Typ, wenn ihre Typen sind die gleichen:

Dies kann durch C11 6.2.7 kompatiblen Typ gefunden werden. Zusätzliche Regeln zur Bestimmung, ob zwei Typen sind kompatibel sind in 6.7.2 für Typdeklarierer beschrieben, in 6.7.3 für Typ -Qualifikation ...

Wo 6.7.3 der relevante Teil ist. Es heißt

Damit zwei qualifizierte Typen kompatibel sind, müssen beide die identisch qualifizierte Version eines kompatiblen Typs haben;

Die Umwandlung Kapitel 6.3.2.3 bedeutet dies nicht im Widerspruch:

Ein Zeiger auf eine Funktion eines Typs in einen Zeiger umgewandelt werden kann, auf eine Funktion eines anderen Typs und wieder zurück; Das Ergebnis muss mit dem ursprünglichen Zeiger verglichen werden. Wenn ein konvertierter Zeiger verwendet wird, um eine Funktion aufzurufen, deren Typ nicht mit dem referenzierten Typ kompatibel ist, ist das Verhalten nicht definiert.

EDIT

Wie von Holt in der Antwort erwähnt, ist die Kompatibilität von zwei Funktionen explizit in 6.7.6.3/15 beschrieben.


Ich denke immer noch, dass eine Wrapper-Funktion die beste Lösung ist. Die Wurzel des Problems ist, dass write_a nicht const-korrekt ist. Wenn Sie diese Funktion nicht ändern können, schreiben Sie einen Wrapper darum.

+0

Die Besetzung selbst ist perfekt erlaubt und legal. Was "nicht erlaubt" ist, ruft die Funktion durch "b1" auf. Aber der OP-Code macht keine Anrufe über 'b1'. – AnT

+0

Auch hier ist die Bemerkung "strikter Alias" irrelevant. Die Konzepte von * Aliasing * (und * strenger Aliasing *) sind nur für Zeiger auf Daten, nicht für Zeiger auf Funktionen anwendbar. – AnT

+0

@AnT In Bezug auf Ihre erste Bemerkung, das ist genau das, was der kühne Text sagt. Sie haben Recht bezüglich des strengen Aliasings, der Standard erwähnt nur Objekte. Fest. – Lundin

10

Dies ist nicht definiertes Verhalten - Sie einen Zeiger verwenden, können nur eine Funktion eines anderen Typs zu nennen, wenn die Typen kompatibel sind (6.3.2.3/8):

Ein Zeiger auf eine Funktion eines Typs kann in einen Zeiger auf eine Funktion eines anderen Typs und wieder zurück umgewandelt werden; Das Ergebnis muss mit dem ursprünglichen Zeiger verglichen werden. Wenn ein konvertierter Zeiger verwendet wird, um eine Funktion aufzurufen, deren Typ nicht mit dem referenzierten Typ kompatibel ist, ist das Verhalten nicht definiert.

zwei Funktionen haben kompatible Typen, wenn (vereinfachte Version) sie gleiche Rendite haben und Argumente sind kompatibel (6.7.6.3, Semantics/15):

Für zwei Funktionstypen kompatibel sein, beide sollen Geben Sie kompatible Rückgabetypen an.146) Darüber hinaus müssen die Listen der Parametertypen, falls beide vorhanden sind, in der Anzahl der Parameter und im Gebrauch des Auslassungszeichen-Abschlusszeichens übereinstimmen; entsprechende Parameter müssen kompatible Typen haben.

A const char * ist nicht kompatibel mit einem char * (6.7.6.1, Semantiken/2):

Für zwei Zeigertypen kompatibel zu sein, beide sind gleich qualifiziert sein und werden beide Zeiger auf kompatibel Arten.

Da const char * und char * nicht gleich qualifiziert sind, sind sie nicht kompatibel, und write_b durch b Aufruf ist nicht definiertes Verhalten.

+0

Kein Problem. Vielen Dank. – PSkocik

+0

"Sie können einen Zeiger verwenden, um eine Funktion aufzurufen ..." Aber der OP-Code * ruft * die Funktion nicht über 'b1' auf! – AnT

2

write_fn b1 = (write_fn) write_b; // ist das legal?

Ist was legal?

Funktionszeigertypen, die an dieser Besetzung beteiligt sind, sind nicht kompatibel.

Dennoch ist es absolut legal, den Funktionszeiger auf einen inkompatiblen Funktionszeigers umzulenken. Das einzige, was Sie mit einem derart gewaltsam umgewandelten Zeiger machen können, ist, ihn in den ursprünglichen Typ zurück zu konvertieren. Die Sprachspezifikation garantiert, dass eine solche Round-Trip-Konvertierung den ursprünglichen Zeigerwert beibehält. Aus diesem Grund können wir beispielsweise void (*)(void) als "universellen Speichertyp" für Funktionszeiger verwenden (wie void * für Datenzeiger). Es kann für Speichern Funktionszeiger eines beliebigen Typs verwendet werden (aber nicht für Aufruf die Funktionen). Um einen solchen Pointerspeicher (und -abruf) durchzuführen, müssen wir explizite Umwandlungen verwenden, genau wie die in Ihrem Code. Es gibt nichts Illegales daran.

Wenn Sie versuchen, die Funktion über b1 aufzurufen, führt dies zu undefiniertem Verhalten, insbesondere weil der Zeigertyp nicht mit dem tatsächlichen Funktionstyp kompatibel ist.

In Ihrer Frage geben Sie deutlich an, dass Sie den Zeiger auf diese Funktion "speichern" möchten. Solange wir nur über das "Speichern" (Speichern) des Zeigers sprechen, ist Ihr Code vollkommen fehlerfrei.

+0

Guter Punkt. Der Aufruf der Funktion durch diesen Zeiger war ein Ziel, das ich ausdrücklich erwähnen sollte. – PSkocik

+0

Vorschlag hinzufügen ref: C11 §6.3.2.3 8 "Ein Zeiger auf eine Funktion eines Typs kann in einen Zeiger auf eine Funktion eines anderen Typs und wieder zurück konvertiert werden; das Ergebnis muss mit dem ursprünglichen Zeiger übereinstimmen." – chux

Verwandte Themen