Anonyme Funktionen, die GCC-Anweisungsausdrücke verwenden
Lesezeit: 5 Minuten
Bill VB
Diese Frage ist nicht sehr spezifisch; Es ist wirklich für meine eigene C-Anreicherung und ich hoffe, dass andere es auch nützlich finden können.
Haftungsausschluss: Ich weiß, dass viele den Impuls haben werden, mit “wenn Sie versuchen, FP zu machen, dann verwenden Sie einfach eine funktionale Sprache” zu antworten. Ich arbeite in einer eingebetteten Umgebung, die mit vielen anderen C-Bibliotheken verknüpft werden muss und nicht viel Platz für viele weitere große gemeinsam genutzte Bibliotheken hat und viele Sprachlaufzeiten nicht unterstützt. Außerdem kommt eine dynamische Speicherallokation nicht in Frage. Ich bin auch einfach nur neugierig.
Viele von uns haben dieses raffinierte C-Makro für Lambda-Ausdrücke gesehen:
int (*max)(int, int) = lambda (int, (int x, int y) { return x > y ? x : y; });
max(4, 5); // Example
Verwenden gcc -std=c89 -E test.cerweitert sich das Lambda zu:
int (*max)(int, int) = ({ int __fn__ (int x, int y) { return x > y ? x : y; } __fn__; });
Also, das sind meine Fragen:
Was genau bedeutet die Linie Ganzzahl (*X); erklären? Na sicher, Ganzzahl * X; ist ein Zeiger auf eine Ganzzahl, aber wie unterscheiden sich diese beiden?
Schauen Sie sich das erweiterte Makro an, was in aller Welt macht das Finale __fn__ tun? Wenn ich eine Testfunktion schreibe void test() { printf("hello"); } test; – das wirft sofort einen Fehler. Ich verstehe diese Syntax nicht.
Was bedeutet das für das Debuggen? (Ich habe vor, selbst damit und mit gdb zu experimentieren, aber die Erfahrungen oder Meinungen anderer wären großartig). Würde dies statische Analysatoren vermasseln?
Außerdem sehe ich nicht int (*X); irgendwo.
– Oliver Charlesworth
1. Mai 2012 um 22:49 Uhr
Es ist nicht so drin. Als ich versuchte, die Syntax herauszufinden, war ich ein wenig ratlos, dass int (*X); kompiliert, aber nicht wirklich sicher, was es definiert hat.
– Bill VB
1. Mai 2012 um 22:51 Uhr
int (*X)(<arguments>); ist ein Prototyp für einen Funktionszeiger namens X. Mit anderen Worten: X ist ein Zeiger auf eine akzeptierende Funktion <arguments> und Rückkehr a int.
– pmg
1. Mai 2012 um 22:52 Uhr
Nehmen wir also an, wir haben gerade int (*x) – Dies ist ein Zeiger auf eine Funktion mit … einer variablen Liste von Argumenten, keine Argumente?
– Bill VB
1. Mai 2012 um 22:54 Uhr
Um gcc zu einem strikten C89- (oder C99-) Compiler zu machen, verwenden Sie -std=c89 -pedantic.
– pmg
1. Mai 2012 um 22:56 Uhr
ouah
Diese Deklaration (im Blockbereich):
int (*max)(int, int) =
({
int __fn__ (int x, int y) { return x > y ? x : y; }
__fn__;
});
Beide verschachtelte Funktionen (Definieren einer Funktion innerhalb einer zusammengesetzten Anweisung) und Anweisungsausdrücke (({})im Grunde ein Block, der einen Wert liefert) sind in C nicht erlaubt und stammen von GNU C.
In einem Anweisungsausdruck ist die letzte Ausdrucksanweisung der Wert des Konstrukts. Deshalb die verschachtelte Funktion __fn__ erscheint als Ausdrucksanweisung am Ende des Anweisungsausdrucks. Ein Funktionsbezeichner (__fn__ in der letzten Ausdrucksanweisung) in einem Ausdruck wird durch die üblichen Konvertierungen in einen Zeiger auf eine Funktion umgewandelt. Dies ist der Wert, der zum Initialisieren des Funktionszeigers verwendet wird max.
Solch eine nützliche Funktionalität, wann wird diese in ANSI C implementiert?
– Schrittmacher
6. März 2015 um 11:03 Uhr
Es ist syntaktisch gültiges GNU C, aber ist es wirklich semantisch gültig? Wenn der Block, der die Funktionsdefinition enthält, beendet wird (was vor der tatsächlichen Verwendung des “Lambda-Ausdrucks” liegt), würde das Funktionstrampolin nicht zusammen damit ungültig gemacht werden?
– Dolda2000
9. August 2016 um 16:37 Uhr
@Dolda2000 Daten, die in einem Anweisungsausdruck erstellt wurden, haben wahrscheinlich eine statische Dauer. Schließlich ist es nicht mehr ANSI C. Es ist GNU C. GNU definiert diesen Standard.
– Braden Best
4. Februar 2017 um 12:22 Uhr
@BradenBest: Es hat sicherlich keine statische Dauer, da ein Zeiger auf eine verschachtelte Funktion ungültig ist, nachdem die enthaltende Funktion zurückgegeben wurde. Da eine verschachtelte Funktion Zugriff auf alle Variablen hat, die sich lexikalisch in ihrem Geltungsbereich befinden, wäre es nicht verwunderlich, wenn ihrem Trampolin die gleiche Dauer wie diesen Variablen zugewiesen würde. Das Handbuch ist jedoch etwas unklar in Bezug auf das Problem.
– Dolda2000
10. Februar 2017 um 3:27 Uhr
Der @Pacerier-Anweisungsausdruck wird von gcc und clang unterstützt, sodass er ziemlich portabel ist und Sie ihn sicher verwenden können (selbst Linux-Kernel-Code verwendet ihn).
– SwiftMango
23. November 2017 um 15:57 Uhr
Ihr Lambda-Makro nutzt zwei unkonventionelle Funktionen. Zuerst verwendet es tatsächlich verschachtelte Funktionen definieren den Körper Ihrer Funktion (Ihr Lambda ist also nicht wirklich anonym, es verwendet nur eine implizite __fn__ Variable (die in etwas anderes umbenannt werden sollte, da Namen mit doppelt führenden Unterstrichen für den Compiler reserviert sind, also vielleicht so etwas wie yourapp__fn__ wäre besser).
die letzte Anweisung der zusammengesetzten Anweisung ist die Adresse der gerade deklarierten Funktion. Jetzt, int (*max)(int,int) bekommt einfach den Wert der zusammengesetzten Anweisung zugewiesen, die nun der Zeiger auf die gerade deklarierte ‘anonyme’ Funktion ist.
Das Debuggen von Makros ist natürlich eine königliche Qual.
Was den Grund angeht test; .. zumindest hier bekomme ich den ‘Test als andere Art von Symbol neu deklariert’, was meiner Meinung nach bedeutet, dass GCC ihn als Deklaration und nicht als (nutzlosen) Ausdruck behandelt. Da nicht typisierte Variablen standardmäßig auf int und weil Sie bereits erklärt haben test als Funktion (im Wesentlichen void (*)(void)) du verstehst das.. aber da könnte ich mich irren.
Dies ist jedoch bei weitem nicht tragbar.
Teilantwort: Sie interessieren sich nicht für int(*X), sondern für int(*X)(y,z). Das ist ein Funktionszeiger auf die Funktion namens X, die (y,z) nimmt und int zurückgibt.
Für das Debuggen wird dies wirklich schwierig. Die meisten Debugger können Makros nicht verfolgen. Sie müssten höchstwahrscheinlich die Assembly debuggen.
int (*max)(int, int) ist der Variablentyp, den Sie deklarieren. Es ist als ein Funktionszeiger namens max definiert, der int zurückgibt und zwei ints als Parameter akzeptiert.
__fn__ bezieht sich auf den Funktionsnamen, der in diesem Fall max.
Da habe ich keine Antwort. Ich würde mir vorstellen, dass Sie es durchlaufen können, wenn Sie es durch den Präprozessor ausgeführt haben.
14121300cookie-checkAnonyme Funktionen, die GCC-Anweisungsausdrücke verwendenyes
Außerdem sehe ich nicht
int (*X);
irgendwo.– Oliver Charlesworth
1. Mai 2012 um 22:49 Uhr
Es ist nicht so drin. Als ich versuchte, die Syntax herauszufinden, war ich ein wenig ratlos, dass int (*X); kompiliert, aber nicht wirklich sicher, was es definiert hat.
– Bill VB
1. Mai 2012 um 22:51 Uhr
int (*X)(<arguments>);
ist ein Prototyp für einen Funktionszeiger namensX
. Mit anderen Worten:X
ist ein Zeiger auf eine akzeptierende Funktion<arguments>
und Rückkehr aint
.– pmg
1. Mai 2012 um 22:52 Uhr
Nehmen wir also an, wir haben gerade
int (*x)
– Dies ist ein Zeiger auf eine Funktion mit … einer variablen Liste von Argumenten, keine Argumente?– Bill VB
1. Mai 2012 um 22:54 Uhr
Um gcc zu einem strikten C89- (oder C99-) Compiler zu machen, verwenden Sie
-std=c89 -pedantic
.– pmg
1. Mai 2012 um 22:56 Uhr