2012-04-15 6 views
6

Die get_cpu_var marcro, die wie untenAbstruse #define Makro in Linux-Kernel Quelle begegnete

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

unverständlich definiert ist, scheint zu be.I ist die Annahme, es eine Art von Funktion Makro war, die einen variablen Zeiger zurückgeben (basierend auf der Sternchen) oder ist es eine Art Funktion Zeiger.Am ich sogar nah daran? Könnte mich jemand aufklären?

Antwort

15

Was Sie zwischen der Öffnung sehen ({ und }) Schließen ist ein Aussage Ausdruck - ein Nicht-Standard-Feature von GCC-Compiler, die eine zusammengesetzte Anweisungen in C-Ausdrücke einzubetten. Das Ergebnis eines solchen Anweisungsausdrucks ist die letzte Ausdrucksanweisung innerhalb der ({}). In Ihrem Fall wäre das &__get_cpu_var(var).

Der Operator & wird auf das Ergebnis von __get_cpu_var(var) Teilausdruck angewendet. Das bedeutet, dass __get_cpu_var einen Lvalue zurückgibt. Wenn dies in der Tat C ist, dann muss __get_cpu_var auch ein Makro sein, da in C-Sprache Funktionen lvales nicht zurückgeben können.

Der Operator & erzeugt einen Zeiger (das Ergebnis des gesamten Anweisungsausdrucks), der dann von einem *-Operator dereferenziert wird, der zu Beginn der obigen Makrodefinition vorhanden ist. Das obige Makro entspricht also im Wesentlichen dem Ausdruck *&__get_cpu_var(var).

Einige könnten fragen, warum es als *&__get_cpu_var(var) und nicht nur __get_cpu_var(var) implementiert ist. Dies wird auf diese Weise erreicht, um das lvalueness des Ergebnisses von __get_cpu_var(var) zu erhalten. Das Ergebnis eines Anweisungsausdrucks ist immer ein R-Wert, auch wenn der letzte Abschnitt in ({}) ein L-Wert ist. Um die Wertigkeit des Ergebnisses zu erhalten, wird der bekannte Trick *& verwendet.

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

int a, b; 

und Sie möchten einen Ausdruck schreiben, die entweder a oder b als L-Wert zurückkehren würde (sagen wir, wir 42 es zuweisen möchten) in Abhängigkeit von der Auswahl Variable select. Ein naiver Versuch aussehen könnte wie folgt

(select ? a : b) = 42; 

Dies funktioniert nicht, da in C-Sprache der ?: Bediener die lvalueness seiner Operanden verliert. Das Ergebnis ist ein rvalue, dem nicht zugeordnet werden kann. In dieser Situation kommt der *& Trick zur Rettung

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

und jetzt funktioniert es wie beabsichtigt.

Dies ist genau, wie und warum die ursprüngliche Definition des Posters eine scheinbar redundante Anwendung von * und & enthält.Aufgrund der, dass Sie die oben get_cpu_var Makro auf beiden Seiten von einem assgnment

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

ohne diesen Trick verwenden können, würden Sie nur in der Lage sein get_cpu_var auf der rechten Seite zu verwenden.

In C++ - Sprache wird der gleiche Effekt durch Verwendung von Referenzen erreicht. In C haben wir keine Referenzen, also verwenden wir stattdessen Tricks wie diese.

+0

also ist das * (...) eine Typumwandlung zum Ergebnis? * edited.noted und danke. –

+0

Also im Grunde diese gibt '* & __get_cpu_var (var)' - aber warum haben sie nicht einfach zurückgeben '__get_cpu_var (var)'? Irgendeine Art von Kompilierzeit Typprüfung? – Anthales

+1

@Anthales: Ich habe die Antwort über – AnT