7 Stimmen

Abstruses #define-Makro im Linux-Kernel-Quelltext gefunden

Die Marcro get_cpu_var ist wie folgt definiert

 29 #define get_cpu_var(var) (*({                           \
 30         extern int simple_identifier_##var(void);       \
 31         preempt_disable();                              \
 32         &__get_cpu_var(var); }))

Ich nehme an, dass es sich um eine Art von Funktionsmakro handelt, das einen Variablenzeiger zurückgibt (basierend auf dem Sternchen), oder ist es eine Art Funktionszeiger, bin ich überhaupt in der Nähe davon?

16voto

AnT Punkte 300728

Was Sie zwischen der Öffnung sehen ({ und Schließen }) ist eine Anweisungsausdruck - eine nicht standardisierte Funktion des GCC-Compilers, die es ermöglicht, zusammengesetzte Anweisungen in C-Ausdrücke einzubetten. Das Ergebnis eines solchen Anweisungsausdrucks ist die allerletzte Ausdrucksanweisung innerhalb der ({}) . In Ihrem Fall wäre das &__get_cpu_var(var) .

Le site & Operator wird auf das Ergebnis von __get_cpu_var(var) Unterausdruck. Das bedeutet, dass __get_cpu_var gibt einen l-Wert zurück. Wenn dies tatsächlich C ist, dann __get_cpu_var muss ebenfalls ein Makro sein, da in der Sprache C Funktionen keine lWerte zurückgeben können.

Le site & Operator erzeugt einen Zeiger (das Ergebnis des gesamten Anweisungsausdrucks), der dann von einem anderen Operator dereferenziert wird. * Operator, der ganz am Anfang der obigen Makrodefinition steht. Das obige Makro ist also im Wesentlichen gleichwertig mit dem *&__get_cpu_var(var) Ausdruck.

Manch einer mag sich fragen, warum sie als *&__get_cpu_var(var) und nicht nur __get_cpu_var(var) . Dies geschieht auf diese Weise, um die lvalueness des Ergebnisses von __get_cpu_var(var) . Das Ergebnis des Anweisungsausdrucks ist immer ein r-Wert, auch wenn der letzte Satz innerhalb der ({}) ein l-Wert war. Um die L-Wertigkeit des Ergebnisses zu erhalten, wird die bekannte *& Trick angewendet wird.

Dieser Trick ist in keiner Weise auf GCC-Anweisungsausdrücke beschränkt. Er wird relativ häufig in der alltäglichen C-Programmierung verwendet. Stellen Sie sich zum Beispiel vor, Sie haben zwei Variablen

int a, b;

und Sie wollen einen Ausdruck schreiben, der entweder a o b als l-Wert (sagen wir mal, wir wollen 42 zu), abhängig von der Selektorvariable select . Ein naiver Versuch könnte folgendermaßen aussehen

(select ? a : b) = 42;

Dies wird nicht funktionieren, da in der Sprache C die ?: Operator verliert die l-Wertigkeit seiner Operanden. Das Ergebnis ist ein r-Wert, der nicht zugewiesen werden kann. In dieser Situation wird der *& Der Trick kommt zur Rettung

*(select ? &a : &b) = 42;

und jetzt funktioniert es wie vorgesehen.

Genau aus diesem Grund enthält die Makrodefinition des ursprünglichen Posters eine scheinbar redundante Anwendung von * et & . Aus diesem Grund können Sie die oben genannten get_cpu_var Makro auf beiden Seiten eines Assgnments

something = get_cpu_var(something);
get_cpu_var(something) = something;

Ohne diesen Trick könnten Sie nur get_cpu_var auf der rechten Seite.

In der Sprache C++ wird derselbe Effekt durch die Verwendung von Referenzen . In C haben wir keine Verweise, also verwenden wir stattdessen Tricks wie diesen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X