"C Traps and Defects"----Chapter 2 Syntax Traps

2.1 Understanding function declarations

2.1.1 How to understand function declarations

First look at a sentence:

(*(void(*)())0)();

There is only one simple rule for constructing such expressions: declare them the way they are used.

The declaration of any C variable consists of two parts: the type and a set of expression-like declarators. A declarator is superficially similar to an expression, and its evaluation should return a result of the type given in the declaration.

  1. Simple variable declaration:

    float f;
    //含义:当对f进行求值时,表达式f的类型为浮点数类型。因为声明符与表达式类似,所以也可以在声明符中任意使用括号:
    float ((f));
    //含义:((f))的类型是浮点类型,由此可以推知,f也是浮点类型。
    
  2. Declaration of function and pointer types

    float ff();
    //含义:表达式ff()的求值结果是一个浮点数,也就是说,ff是一个返回值为浮点类型的函数。
    float *pf;
    //含义:*pf是一个浮点数,也就是说,pf是一个指向浮点数的指针。
    

Corollary: Just remove the variable name in the declaration and the semicolon at the end of the declaration, and then "encapsulate" the rest with a parenthesis to get the caster.

Note: There is a function pointer below:

void (*fp)();

Because fp is a function pointer, then *fp is the function that the pointer points to, so (*fp)() is the way to call the function. The ANSI C standard allows programmers to abbreviate the above expression as fp(). But keep in mind that this notation is just a shorthand.

In the expression (*fp)(), the parentheses around *fp are very important, because the function operator () has higher precedence than the unary operator *. If there are no parentheses around *fp, then *fp() actually has exactly the same meaning as *(fp()), which ANSI C uses as a shorthand for *((*fp)()). (*fp and fp are equivalent, that is, the above theorem applied)

2.1.2 Understanding Statements with Examples

2.1.2.1 Example 1

(*(void(*)())0)();
//将0进行强制类型转换为(void (*)()),即函数指针类型,然后对其进行解引用,解引用之后就找到了那个函数,然后再进行函数调用
//此处就是把0当作是一个地址,本质上就是一个函数的调用

2.1.2.2 Example 2

void (*signal(int,void(*)(int)))(int);
//首先signal先和圆括号先结合,形成一个函数,这个函数有两个参数,参数1是一个整数,参数2是一个函数指针,该函数指针指向的函数的参数为int类型,返回类型为void类型,signal函数的返回类型为void(*)(int),即一个函数指针类型,该函数指针指向的函数参数类型为int,返回类型为void。

2.2 Operator precedence

Operator precedence order summary

image-20220302211047449

image-20220302211107657

image-20220302211134778

2.2.1 Common mistakes

2.2.1.1 Error Example 1

if(flags & FLAG!=0 )

We want to use the results of the expressions flags and FLAG& as the judgment condition of the if conditional statement, but due to the problem of priority, the above expression is combined as follows, and the meaning of the expression is contrary to us:

if(flags & (FLAG!=0))

2.2.1.2 Error 2

r = h<<4 + low;//r的值等于h左移4位后的值与变量low的和

But it's actually combined like this:

r = hi<< (4 + low);

The meaning expressed is contrary to what we want to express.

2.2.1.3 Error example 3

while(c = getc(in)!=EOF)
	putc(c,out);

In the above statement, we are trying to copy one file to another, but it is actually combined like this:

while(c = (getc(in)!=EOF))

The meaning of the expression does not achieve the purpose we want to achieve. Here the return value of the function getc(in) is just a temporary variable which is discarded after the comparison with EOF. Therefore, the resulting copy of the file contains only a set of binary 1 byte streams.

2.2.1.4 Error 4

if((t=BTYPE(pt1->aty)==STRTY) || t==UNIONTY)

The original intention of this line of code is to first assign a value to t, and then determine whether t is equal to STRTY or UNIONTY.

But the actual combination is like this:

if((((t=BTYPE(pt1->aty))==STRTY) || t)==UNIONTY)

The actual results are quite different: according to whether the value of BTYPE(pt1->aty) is equal to STRTY, the value of t is either 1 or 0: if the value of t is 0, it can be further compared with UNIONTY.

2.2.1.5 Error 5

while(c == '\t' || c = ' '|| c == '\n')
	c = getc(f);

This example is illegal. Because the assignment operator has lower precedence than other operators in the while clause, the above example can be explained:

while((c == '\t' || c) = (' '|| c == '\n'))

Of course, this is illegal because (c == '\t' || c)it cannot appear on the left-hand side of an assignment operator.

2.2.2 Associative understanding of operators

*p++;
//上面的代码应该像下面这样进行理解:
*(p++);//++和*都是单目运算符,具有相同的优先级,但是结合性是自右向左的

2.2.3 Precedence of relational operators

The precedence of the six relational operators is not the same. The precedence of >, >=, <, <= is higher than the precedence of the two operators == and !=, which is why we often write as follows .

if(a < b == c < d)
//上面代码等价于
if((a < b) == (c < d))

2.2.4 Summary of priority order from right to left

unary operator, ternary operator, assignment operator

2.3 Note the semicolon as the end of the statement

2.3.1 Add a semicolon

Note: Don't add a semicolon after an if or while clause. Putting a semicolon after it often has incalculable consequences, causing code errors and often hard to find.

E.g:

if(x[i] > big);
	big = x[i];

The above code is actually equivalent to:

if(x[i] > big) {}
	big = x[i];

That is, the big = x[i] statement will be executed regardless of whether the condition is true or not.

2.3.2 Missing semicolon

Note: Missing a semicolon can also often have immeasurable consequences.

E.g:

if(n<3)
	return 
logrec.date = x[0];

The above code is equivalent to the following code:

if(n<3)
	return logrec.date = x[0];

At this point, when the n<3 condition is satisfied, there may not be a big problem, but if the condition n<3 is not satisfied, the logrec.date = x[0] statement will be skipped. This is contrary to what we want to express.

There is the following piece of code:

struct logrec
{
	int date;
	int time;
	int code;
}//分号被省略
main()
{
	···
}

If the semicolon is not omitted, the return value type of the function main() is int type (the compiler default), and the return type after omission is struct type.

2.4 switch statement

Note: If we do not want to continue to execute the next case statement after the case statement in C language, we must add break at the end of the case statement to end the current case statement.

2.5 List of functions

==C language requirement: When calling a function, even if the function takes no parameters, the parameter list should be included. == So if f is a function, then

f();

is a function call statement, and

f;

But it's a statement that does nothing. This statement computes the address of the function f without calling the function.

Guess you like

Origin blog.csdn.net/m0_57304511/article/details/123449168