Abstruse #define macro encountered in Linux kernel source

后端 未结 1 1942
野性不改
野性不改 2021-02-04 17:54

The get_cpu_var marcro which is defined as below

 29 #define get_cpu_var(var) (*({                           \\
 30         extern int simple_identifier_##var(vo         


        
相关标签:
1条回答
  • 2021-02-04 18:41

    What you see between the opening ({ and closing }) is a statement expression - a non-standard feature of GCC compiler, which allows one to embed compound statements into C expressions. The result of such statement expression is the very last expression statement inside the ({}). In your case that would be &__get_cpu_var(var).

    The & operator is applied to the result of __get_cpu_var(var) subexpression. That implies that __get_cpu_var returns an lvalue. If this is indeed C, then __get_cpu_var must also be a macro, since in C language functions cannot return lvalues.

    The & operator produces a pointer (the result of the entire statement expression), which is then dereferenced by a * operator present at the very beginning of the above macro definition. So, the above macro is essentially equivalent to the *&__get_cpu_var(var) expression.

    Some might ask why it is implemented as *&__get_cpu_var(var) and not just __get_cpu_var(var). This is done that way to preserve the lvalueness of the result of __get_cpu_var(var). The result of statement expression is always an rvalue, even if the last stetement inside the ({}) was an lvalue. In order to preserve the lvalueness of the result the well-known *& trick is used.

    This trick is not limited to GCC statement expressions in any way. It is relatively often used in ordinary everyday C programming. For example, imagine you have two variables

    int a, b;
    

    and you want to write an expression that would return either a or b as an lvalue (let's say we want to assign 42 to it) depending on the selector variable select. A naive attempt might look as follows

    (select ? a : b) = 42;
    

    This will not work, since in C language the ?: operator loses the lvalueness of its operands. The result is an rvalue, which cannot be assigned to. In this situation the *& trick comes to the rescue

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

    and now it works as intended.

    This is exactly how and why the original poster's macro definition contains a seemingly redundant application of * and &. Because of that you can use the above get_cpu_var macro on either side of an assgnment

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

    without that trick you'd only be able to use get_cpu_var on the right-hand side.

    In C++ language the same effect is achieved by using references. In C we have no references, so we use tricks like this instead.

    0 讨论(0)
提交回复
热议问题