Abstruse#define macro encontrada en Linux kernel source
gcc linux-kernel (1)
Lo que se ve entre la apertura ({
y cierre })
es una expresión de enunciado , una característica no estándar del compilador GCC, que permite incrustar sentencias compuestas en expresiones C. El resultado de dicha expresión de declaración es la última declaración de expresión dentro de ({})
. En tu caso, eso sería &__get_cpu_var(var)
.
El operador &
se aplica al resultado de __get_cpu_var(var)
subexpression. Eso implica que __get_cpu_var
devuelve un valor l. Si esto es realmente C, entonces __get_cpu_var
también debe ser una macro, ya que en el lenguaje C las funciones no pueden devolver lvalues.
El operador &
produce un puntero (el resultado de la expresión de la declaración completa), que luego es desreferenciado por un operador *
presente al principio de la definición de macro anterior. Entonces, la macro anterior es esencialmente equivalente a la expresión *&__get_cpu_var(var)
.
Algunos podrían preguntar por qué se implementa como *&__get_cpu_var(var)
y no solo __get_cpu_var(var)
. Esto se hace de esa manera para preservar la lvaloriedad del resultado de __get_cpu_var(var)
. El resultado de la expresión de declaración siempre es un valor r, incluso si el último stetement dentro de ({})
fue un valor l. Con el fin de preservar la validez del resultado, se usa el conocido *&
truco.
Este truco no está limitado a expresiones de enunciados GCC de ninguna manera. Se usa con relativa frecuencia en la programación común de C cotidiano. Por ejemplo, imagina que tienes dos variables
int a, b;
y desea escribir una expresión que devuelva a
o b
como un valor l (supongamos que queremos asignarle 42
) según la selección de la variable selector. Un intento ingenuo podría verse de la siguiente manera
(select ? a : b) = 42;
Esto no funcionará, ya que en lenguaje C, el operador ?:
Pierde la validez de sus operandos. El resultado es un valor r, que no se puede asignar a. En esta situación, el *&
truco viene al rescate
*(select ? &a : &b) = 42;
y ahora funciona según lo previsto.
Así es exactamente y por qué la definición de macro del póster original contiene una aplicación aparentemente redundante de *
y &
. Por eso puede usar la macro get_cpu_var
arriba a cada lado de un reconocimiento
something = get_cpu_var(something);
get_cpu_var(something) = something;
sin ese truco, solo get_cpu_var
usar get_cpu_var
en el lado derecho.
En lenguaje C ++, el mismo efecto se logra mediante el uso de referencias . En C no tenemos referencias, así que usamos trucos como este en su lugar.
El marcro get_cpu_var que se define a continuación
29 #define get_cpu_var(var) (*({ /
30 extern int simple_identifier_##var(void); /
31 preempt_disable(); /
32 &__get_cpu_var(var); }))
parece incomprensible. Supongo que fue un tipo de macro función que devuelve un puntero variable (basado en el asterisco) o es una especie de puntero a la función. ¿Estoy cerca de él? ¿Alguien podría iluminarme?