Anonyme Funktionen, die GCC-Anweisungsausdrücke verwenden

Lesezeit: 5 Minuten

Benutzeravatar von Bill VB
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:

#define lambda(return_type, function_body) \
({ \
      return_type __fn__ function_body \
          __fn__; \
})

Und ein Beispiel für die Verwendung ist:

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:

  1. 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?

  2. 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.

  3. 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

Benutzeravatar von ouah
ouah

Diese Deklaration (im Blockbereich):

int (*max)(int, int) =
    ({
    int __fn__ (int x, int y) { return x > y ? x : y; }
    __fn__;
    });

ist nicht C, aber gültiges GNU C.

Es nutzt zwei gcc Erweiterungen:

  1. verschachtelte Funktionen
  2. Anweisungsausdrücke

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).

All dies wird selbst innerhalb einer zusammengesetzten GCC-Anweisung durchgeführt (siehe http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprs), dessen Grundformat ungefähr so ​​lautet:

({ ...; retval; })

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.

  1. 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.

  2. __fn__ bezieht sich auf den Funktionsnamen, der in diesem Fall max.

  3. 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.

1412130cookie-checkAnonyme Funktionen, die GCC-Anweisungsausdrücke verwenden

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy