Linux Advanced Statement Expression

expression

Expressions and statements are the basic concepts in C language. What is an expression? An expression is a formula composed of a series of operators and operands. Operators can be various arithmetic operators, logical operators, assignment operators, comparison operators, etc. specified by the C language standard. The operand can be a constant or a variable. Expressions can also have no operators. A single constant or even a string is also an expression. The following character sequences are all expressions:

  • 2 + 3
  • 2
  • i = 2 + 3
  • i = i++ + 3
  • "wit"

Expressions are generally used for data calculations or algorithms that implement a certain function. Expressions have 2 basic attributes: value and type. As the above expression 2 + 3, its value is 5. According to different operators, expressions can be divided into many types, such as:

  • Relational expression
  • Logical expression
  • Conditional expression
  • Assignment expression
  • Arithmetic expression
  • ……

Statement

Statements are the basic unit of a program. The general form is as follows:

表达式 ;
i = 2 + 3 ;

Add one after the expression; it constitutes a basic statement. When the compiler compiles and parses the program, it is not based on the physical line, but on the semicolon; to determine the end mark of a statement. Such as i = 2 + 3; This statement, you can also compile through the following form:

i =
2 + 
3
;

Code block

Different statements, enclosed in braces {}, constitute a code block. The C language allows you to define a variable in a code block, and the scope of this variable is also limited to this code block, because the compiler manages the scope of the variable based on the {} to perform stack operations. Such as the following procedure:

int main(void)
{
    int i = 3; 
    printf("i=%d\n",i);
    {
        int i = 4;
        printf("i=%d\n",i);
    }
    printf("i=%d\n",i);
    return 0;
}
运行结果为:
i=3
i=4
i=3

Statement expression

Definition of statement expression

GNU C extends the C standard to allow statements to be embedded in an expression, allowing local variables, for loops, and goto jump statements within expressions. Such expressions are called statement expressions . The format of the statement expression is as follows:

({ 表达式1; 表达式2; 表达式3; })

The outermost of the statement expression is enclosed in parentheses () . Inside the pair of curly brackets {} is the code block , which allows various statements to be embedded. The format of the statement can be "expression;" This is a general format statement, or it can be a statement such as a loop or a jump.

Like regular expressions, statement expressions have their own values. The value of the statement expression is the value of the last expression in the embedded statement . Let's take an example, using statement expressions to evaluate.

int main(void)
{
    int sum = 0;
    sum = 
    ({
        int s = 0;
        for( int i = 0; i < 10; i++)
            s = s + i;
            s;
    });
    printf("sum = %d\n",sum);
    return 0;
}

In the above program, the cumulative summation from 1 to 10 is realized by the statement expression, because the value of the statement expression is equal to the value of the last expression, so after the for loop, we want to add an s; statement representation The value of the entire statement expression. If you do not add this sentence, you will find sum = 0. Or you can change this statement to 100; you will find that the value of the last sum becomes 100, because the value of the statement expression is always equal to the value of the last expression.

Use goto to jump within statement expressions

In the above program, we defined local variables within the statement expression and used a for loop statement. In the statement expression, we can also use goto to jump.

int main(void)
{
    int sum = 0;
    sum = 
    ({
        int s = 0;
        for( int i = 0; i < 10; i++)
            s = s + i;
            goto here;
            s;  
    });
    printf("sum = %d\n",sum);
here:
    printf("here:\n");
    printf("sum = %d\n",sum);
    return 0;
}

Use statement expressions in macro definitions

The highlight of statement expressions is macros that define complex functions. Using statement expressions to define macros can not only achieve complex functions, but also avoid ambiguity and loopholes caused by macro definitions. The following is an example of macro definition, let us see the powerful lethality of the sentence expression in the macro definition!

If you are interviewing at the moment, the interview position is: Linux C language development engineer. The interviewer has a question for you:

Please define a macro to find the maximum of two numbers .

Don't look at such a simple test question, the interviewer can judge your C language skills based on the macro you write, and decide whether to give you an Offer.

qualified

For students who have learned the C language, writing this macro is basically not difficult, and it can be done using conditional operators:

#define  MAX(x,y)  x > y ? x : y

This is the most basic C language grammar. If you can't even write this, it is estimated that the scene will be more awkward. In order to ease the embarrassment, the interviewer will generally say to you: lad, you are great, go back and wait for the news. We will inform you when there is news! At this time, you should understand: don't wait any longer, read this article quickly, and then go home. This macro can be written, and don't think you are very good X, because this only shows that you have the foundation of the C language, but there is still much room for improvement. For example, let's write a program to verify that the macro we defined is correct:

#define MAX(x,y) x > y ? x : y
int main(void)
{
    printf("max=%d",MAX(1,2));
    printf("max=%d",MAX(2,1));
    printf("max=%d",MAX(2,2));
    printf("max=%d",MAX(1!=1,1!=2));
    return 0;
}

Test the program? We must test all possible situations. No, test the statement on line 4. When the parameter of the macro is an expression, it is found that the actual running result is max = 0, which is different from our expected result max = 1. This is because, after the macro is expanded, it looks like this:

printf("max=%d",1!=1>1!=2?1!=1:1!=2);

Because the comparison operator> has a priority of 6, which is greater than! = (Priority is 7), the order of operations in the expanded expression changes, and the result is not what we expected. In order to avoid this expansion error, we can add a parenthesis () to the parameters of the macro to prevent the operation order of the expression from changing after expansion. Only such a macro can be considered a qualified macro:

#define MAX(x,y) (x) > (y) ? (x) : (y)

medium

The above macro can only be considered qualified, but there are still loopholes. For example, we use the following code to test:

#define MAX(x,y) (x) > (y) ? (x) : (y)
int main(void)
{
    printf("max=%d",3 + MAX(1,2));
    return 0;
}

In the program, we print the value of the expression 3 + MAX (1, ​​2). The expected result should be 5, but the actual running result is 1. After we started, we found that there were also problems:

3 + (1) > (2) ? (1) : (2);

Because the operator + has a higher priority than the comparison operator>, this expression becomes 4> 2? 1: 2, and it turns out that the result is 1 is not surprising. At this point we should continue to modify this macro:

#define MAX(x,y) ((x) > (y) ? (x) : (y))

Use parentheses to wrap the macro definition, so that when an expression contains both the macro definition and other high-priority operators, the calculation order of the entire expression is destroyed. If you can write this step, it means that you are better than the former student who passed the interview. The former student has gone back to wait for the news, and we then interview for the next round.

good

Although the above macro solves the problems caused by operator precedence, there are still certain loopholes. For example, we use the following test program to test the macro we defined:

#define MAX(x,y) ((x) > (y) ? (x) : (y))
int main(void)
{
    int i = 2;
    int j = 6;
    printf("max=%d",MAX(i++,j++));
    return 0;
}

In the program, we define two variables i and j, and then compare the size of the two variables, and do the increment operation. The actual operation results found that max = 7, instead of the expected result max = 6. This is because the variables i and j have been incremented twice after the macro expansion, resulting in the value i being printed as 7.

In this case, what should I do? At this time, the statement expression should come on stage. We can use statement expressions to define this macro, and define two temporary variables in the statement expressions to temporarily store the values ​​of i and j, and then compare them, so as to avoid the problem of two increments and decrements.

#define MAX(x,y)({     \
    int _x = x;        \
    int _y = y;        \
    _x > _y ? _x : _y; \
})
int main(void)
{
    int i = 2;
    int j = 6;
    printf("max=%d",MAX(i++,j++));
    return 0;
}

Expression in the statement, we define two local variables  _x, _y to store the value of the macro parameters x and y, then to compare the size and _y _x, thus avoiding to bring i and j increment operator 2 problem.

You can stick to this level and write such a macro with BGM, the interviewer may already have the intention to give you an offer. But at this moment, don't be proud! In order to completely dispel the psychological concerns of the interviewer, we need to continue to optimize this macro.

excellent

In the above macro, the data types of the two temporary variables we defined are int, and we can only compare the data of two integer types. For other types of data, you need to redefine a macro, which is too much trouble! We can continue to modify based on the above macro, so that it can support any type of data comparison size:

#define MAX(type,x,y)({     \
    type _x = x;        \
    type _y = y;        \
    _x > _y ? _x : _y; \
})
int main(void)
{
    int i = 2;
    int j = 6;
    printf("max=%d\n",MAX(int,i++,j++));
    printf("max=%f\n",MAX(float,3.14,3.15));
    return 0;
}

In this macro, we add a parameter: type, used to specify the types of temporary variables _x and _y. In this way, when comparing the size of two numbers, as long as the two data types are passed as parameters to the macro, you can compare any type of data. If you can write such a macro in the interview, the interviewer will definitely be very happy. He will generally tell you: Wait a moment, HR will talk to you about treatment later. Congratulations, you got the Offer!

Use of statement expressions in the Linux kernel

Statement expressions, as an extension of GNU C to the C standard, are widely used in the kernel, especially in the macro definition of the kernel. Using statement expressions to define macros can not only achieve complex functions, but also avoid some ambiguities and vulnerabilities caused by macro definitions. For example, in the Linux kernel, max_t and min_t macro definitions use statement expressions:

#define min_t(type, x, y) ({            \
    type __min1 = (x);          \
    type __min2 = (y);          \
    __min1 < __min2 ? __min1 : __min2; })

#define max_t(type, x, y) ({            \
    type __max1 = (x);          \
    type __max2 = (y);          \
    __max1 > __max2 ? __max1 : __max2; })

In addition, in the Linux kernel and GNU open source software, you will find that a large number of macro definitions use statement expressions. Through the study of this tutorial, I believe that everyone will encounter this kind of macro defined by statement expressions in the future, and they will definitely know what is going on.

发布了81 篇原创文章 · 获赞 69 · 访问量 5万+

Guess you like

Origin blog.csdn.net/s2603898260/article/details/103657829