Warum ist dieses Makro als ({ 1; }) definiert?

Lesezeit: 6 Minuten

Benutzer-Avatar
Yann Droneaud

In mehreren ARM-Back-Ends von Linux sehe ich Dateien clkdev.h diese Makrodefinition:

#define __clk_get(clk) ({ 1; })

Siehe zum Beispiel ./arch/arm/mach-versatile/include/mach/clkdev.h

Dieses Makro verwendet die GCC-Erweiterung Anweisungen und Deklarationen in Ausdrücken

Später wird dieses Makro in Datei verwendet ./drivers/clk/clkdev.cin Funktion clk_get_sys()

 if (cl && !__clk_get(cl->clk))
         cl = NULL;

Ich frage mich, warum hier nicht ein einfaches Makro verwendet wird:

#define __clk_get(clk) (1)

BEARBEITEN:

Ich habe eine andere Verwendung dieses Konstrukts in den Kernelquellen gefunden, die das folgende grep-Muster verwendet:

grep -R '({[[:space:]]*[a-zA-Z0-9_()+=/!&*>., ?:-]\+[[:space:]]*;[[:space:]]*})' .

Hier sind einige der Spiele:

./kernel/trace/trace_selftest.c:# define trace_selftest_startup_dynamic_tracing(trace, tr, func) ({ 0; })
./kernel/profile.c:#define create_hash_tables()         ({ 0; })
./include/asm-generic/bug.h: * Use of ({0;}) because WARN_ON_SMP(x) may be used either as
./include/asm-generic/bug.h:# define WARN_ON_SMP(x)         ({0;})
./include/linux/key.h:#define key_get(k)            ({ NULL; })
./include/linux/key.h:#define key_get(k)            ({ NULL; })
./include/linux/audit.h:#define audit_alloc
./include/linux/audit.h:#define audit_bprm(p) ({ 0; })
./include/linux/audit.h:#define audit_sockaddr(len, addr) ({ 0; })
./include/linux/audit.h:#define audit_log_bprm_fcaps(b, ncr, ocr) ({ 0; })
./include/linux/audit.h:#define audit_log_start(c,g,t) ({ NULL; })
./include/linux/atalk.h:#define atalk_proc_init()   ({ 0; })
./include/linux/ftrace.h:#define register_ftrace_function(ops) ({ 0; })
./include/linux/ftrace.h:#define unregister_ftrace_function(ops) ({ 0; })
./include/linux/ftrace.h:#define ftrace_regex_open(ops, flag, inod, file) ({ -ENODEV; })
./include/linux/ftrace.h:#define ftrace_set_filter(ops, buf, len, reset) ({ -ENODEV; })
./include/linux/ftrace.h:#define ftrace_set_notrace(ops, buf, len, reset) ({ -ENODEV; })
./include/linux/cpu.h:#define unregister_hotcpu_notifier(nb)    ({ (void)(nb); })
./include/linux/proc_fs.h:#define proc_net_fops_create(net, name, mode, fops)  ({ (void)(mode), NULL; })
./arch/powerpc/include/asm/pgtable-ppc64.h:#define pgd_set(pgdp, pudp)  ({pgd_val(*(pgdp)) = (unsigned long)(pudp);})
./arch/sh/math-emu/math.c:#define WRITE(d,a)    ({if(put_user(d, (typeof (d)*)a)) return -EFAULT;})
./arch/sh/math-emu/math.c:#define READ(d,a) ({if(get_user(d, (typeof (d)*)a)) return -EFAULT;})
[...]

Hinweis: Das Konstrukt ({if(put_user(d, (typeof (d)*)a)) return -EFAULT;}) scheint eine gute Verwendung der zusammengesetzten Anweisung zu sein. Dies kann aber auch durch typischere ersetzt werden do { if(put_user(d, (typeof (d)*)a)) return -EFAULT; } while(0)

Ein Streichholz kam zurück grep ist interessant: ein ./include/asm-generic/bug.h Es gibt einen Kommentar zur Verwendung von ({ 0; }). Das ist ganz die gleiche Antwort von AndreyT.

In der Tat kann man nicht verwenden ((void)0)da es nicht als r-Wert verwendet werden kann.
({ 0; }) funktioniert jeweils.

Wenn Sie also ein Makro haben, das wie eine Funktion verwendet werden kann, die einen Wert zurückgibt, der verwendet werden kann oder nicht, scheint die zusammengesetzte Anweisung Ihre einzige Option zu sein.

Aber __clkget() wird nie als etwas anderes als r-Wert verwendet

Einige Verknüpfungen

  • Ich vermute, dass das Makro früher etwas Fleisch hatte, und irgendwann hat es jemand gehackt, um es immer auf 1 auszuwerten, ohne die Klammer-Klammer-Syntax der GCC-Erweiterung zu entfernen.

    – Telefontagger

    18. Dezember 2012 um 17:44 Uhr

  • Nein, jetzt denke ich, dass die Klammer-Klammern-Syntax der GCC-Erweiterung möglicherweise eine Warnung “Bedingter Ausdruck immer wahr” zunichte macht?

    – Telefontagger

    18. Dezember 2012 um 17:49 Uhr

  • @phonetagger Ist das eine gcc-Warnung? Aber ich sehe, dass eine MSVC C4127-Warnung möglich ist.

    – Joseph Quinsey

    18. Dezember 2012 um 19:14 Uhr


  • @JosephQuinsey – Ich bekomme selbst keine solche Warnung, aber ein Typ, der mit mir zusammenarbeitet, nimmt ständig Änderungen am Code vor, die alle anderen einchecken, weil seine Version von GCC auf seiner eigenen Linux-Box viel neuer ist als die Version, die alle anderen verwenden server, und die normalen Codierungspraktiken aller anderen funktionieren anscheinend nicht so gut (in Bezug auf Warnungen), egal welche Version er verwendet. Ich weiß nicht, welche Version er verwendet.

    – Telefontagger

    18. Dezember 2012 um 19:55 Uhr

  • @H2CO3 Ich glaube nicht, dass jemand den Vanilla-Linux-Kernel mit Microsoft-Produkten kompilieren kann.

    – Alvin Wong

    19. Dezember 2012 um 12:16 Uhr

Benutzer-Avatar
AnT steht zu Russland

Ich merke das in -Wall Modus ein Standalone (1); Ausdrucksanweisung generiert eine Warnung “Anweisung ohne Wirkung”, während eine eigenständige ({ 1; }); Ausdrucksanweisung erzeugt keine Warnungen.

Vielleicht enden sie irgendwo im Code irgendwie mit Standalone __clk_get Anrufe, die das Ergebnis ignorieren. Das (1) Definition würde bei solchen Aufrufen zu einer Warnung führen, während ({ 1; }) hält es ruhig, während es in anderen Kontexten den gleichen Effekt erzeugt.

  • Ich vermute, dass dies der Wahrheit etwas näher kommt. Manchmal werden bestimmte Makros auf besonders merkwürdige Weise definiert, weil sie a) ohne unerwartete Nebeneffekte in einer Vielzahl unterschiedlicher Einstellungen richtig funktionieren (und möglicherweise sogar bestimmte falsche Verwendungen verhindern) und b) das Generieren von Compiler-Diagnosen vermeiden möchten, egal wie Das Makro wird am Ende verwendet … Es auf die “offensichtliche” Weise zu definieren, kann 95% des Weges dorthin reichen, aber bestimmte ungewöhnliche Fälle könnten einige ansonsten seltsam aussehende Optimierungen erfordern …

    – Twalberg

    16. Dezember 2014 um 18:39 Uhr


Benutzer-Avatar
manav mn

Warum ist dieses Makro als ({ 1; }) definiert?

Es hängt alles vom Programmierstil des Programmierers ab. Es gibt lediglich den Wert 1 zurück. Zum Beispiel on x86 Bogen in “include/asm-generic/clkdev.h“, __clk_get ist definiert als

static inline int __clk_get(struct clk *clk) { return 1; }

Auch in linux/arch/c6x/include/asm/clkdev.h

static inline int __clk_get(struct clk *clk)
{
        return 1;
}

  • Beim Greppen von Kernelquellen nach ({ 1; }) es gibt kein anderes Ergebnis als die __clk_get() Makro.

    – Yann Droneaud

    19. Dezember 2012 um 6:19 Uhr

  • Möglicherweise verwenden Sie den Kernel-Quellbaum nur für ARM. Versuchen Sie, die vollständige Kernel-Codebasis zu verwenden. lxr.linux.no/linux+v3.7.1/include/asm-generic/clkdev.h#L20

    – manav mn

    19. Dezember 2012 um 6:27 Uhr

  • Es ist insofern eine Frage des Stils, als eine schlecht durchdachte, unnötige Verwendung einer Compiler-Erweiterung Stil ist.

    – Kas

    9. Juni 2013 um 21:03 Uhr

Eine mögliche Erklärung ist, die Verwendung in unerwünschten Situationen zu verhindern.
Dies ist nützlich, um die Codeportabilität zu verbessern – wenn die Implementierung des Makros in einer anderen Architektur fehlschlagen würde, möchten wir, dass diese auch fehlschlägt.

Beispiel: static int x = __clk_get(clk); – Es macht keinen Sinn, mit der Uhr statisch zu initialisieren.
Mit #define __clk_get(clk) (1)es wird klappen.
Mit #define __clk_get(clk) ({ 1; })es wird scheitern.

  • Ein nicht konstanter Ausdruck kann in Standard-C erhalten werden, wie 1 ? 1 : 1.

    – Kas

    9. Juni 2013 um 21:05 Uhr

  • @ Kaz, vielleicht __clk_get hätte man auch so umsetzen können.

    – ugoren

    10. Juni 2013 um 7:32 Uhr

  • @Kaz: ?: ist in Standard-C in konstanten Ausdrücken erlaubt. Eigentlich sind praktisch alle Operatoren erlaubt. Solange alle Operanden konstante Ausdrücke sind, erzeugen Operatoren konstante Ausdrücke. So, 1 ? 1 : 1 ist ein konstanter Ausdruck in C. Warum dachten Sie, er sollte nicht konstant sein?

    – AnT steht zu Russland

    19. November 2013 um 5:41 Uhr


  • @AndreyT Mein schlechtes; es ist ein Kommaoperator, den wir wollen, nicht ternär: Konstante Ausdrücke dürfen keine Zuweisungs-, Inkrement-, Dekrement-, Funktionsaufruf- oder Kommaoperatoren enthalten, es sei denn, sie sind in einem Unterausdruck enthalten, der nicht ausgewertet wird. [ISO 9899:1990 6.6 Constant Expressions, Constraints] … obwohl ich mich zu erinnern scheine, dass ternäre Ausdrücke auch in C90 nicht konstant waren.

    – Kas

    19. November 2013 um 5:52 Uhr


  • @AndreyT Die Sache ist, es ist nicht nützlich, wenn der Compiler es nicht tatsächlich als erforderlich diagnostiziert.

    – Kas

    19. November 2013 um 5:55 Uhr

1362830cookie-checkWarum ist dieses Makro als ({ 1; }) definiert?

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

Privacy policy