第2章 语法陷阱

本文详细讲解了C语言中的函数声明,包括函数返回值类型、指针及分号的使用。强调了运算符的优先级问题,例如位运算与逻辑运算的关系,并提醒读者注意避免在if语句和循环条件后的分号误用。此外,还介绍了switch语句的使用注意事项和函数调用的规范,以及“悬挂”else的陷阱,提倡使用大括号明确代码块。
摘要由CSDN通过智能技术生成

2.1 理解函数声明

函数的声明与变量类似,类型 + 声明符,声明符与表达式相似,所以在声明符中可以可以使用括号。

float ((f));
该声明代表对其求值时,((f))为浮点型,则 f 也是浮点型。函数和指针的声明中也是一样的。

float ff();
表达式 ff() 的结果是一个浮点数,即它是一个返回值为浮点类型的函数。

float *pf;
说明 pf 是一个指向浮点数的指针。

float *g() , (*h)();
表示 *g() 与 (*h)() 都是浮点表达式。() 的优先级高于 **g() = *(g()) ,g是一个函数,该函数的返回值类型为指向浮点数的指针。同理,可以得出 h 是一个函数指针,h 所指向的函数的返回值是浮点型。前者是返回浮点型指针,后者是指向返回浮点数的函数。

(float (*)())
将 h 和分号去掉,再用括号括起,该表达式就变成了表示一个 ==“指向返回值为浮点类型的函数的指针”==的类型转换符。

分两步分析(*(void(*)())0)()
第一步
(*fp)();
此为调用一个函数指针 fp 的方法,可简写为 fp(),*fp 两侧的括号非常重要,因为括号优先级高于星号,所以如果没有两侧的括号,就变成了 *fp()实际上与*(fp())含义相同,它是*((*fp)())的简写。
第二步
找个表达式替换 fp 。
(*0)();中的 0 做类型转换,转换类型为 “指向返回值为 void 类型的函数的指针”
如果说 fp 是这样类型的一个指针,(*fp)() 的值就为 void ,默认 fp 初始化为0,fp 的声明如下:
void (*fp)();
(*fp)();
这种写法的代价是多声明了一个哑变量,知道如何声明一个变量,便可以对一个常数进行该变量类型的转换,去掉变量声明中的变量名即可。
所以对 0 做的类型转换即为
(void (*)())0
因此,可以用该表达式替换 fp ,从而得到:
(*(void(*)())0)();
分号使该表达式被调用。

使用 typedef 可以简化过程:

typedef void (*funcptr)();
(*(funcptr)())();

2.2 运算符的优先级问题

  1. 判断两个整型变量是否有同一位为 1,一般写作if (flag & FLAG),但如果为了更好理解写成if (flag & FLAG != 0)虽然更好理解,但却是错误表达,因为 != 的优先级要高于 & 运算符,所以被编译器理解为了if (flag & (FLAG != 0) )
  2. +加法运算优先级高于<<移位运算,例如一个八位整数的高四位整数加低四位整数运算,应该写做r = (hi<<4)+ low;或者r = hi<<4 | low;,因为|的优先级低于<<,如果直接写r = hi << 4 + low;会被理解为r = hi << (4 + low)
  3. 优先级最高的包括:数组下标、函数调用操作符、各结构成员选择操作符。都是自左向右结合,如a.b.c的意思是(a.b).c而不是a.(b.c)
  4. 单目运算符的优先级仅次于操作符等,所以函数调用的优先级要高于单目运算。因此,调用 p 指向的函数,必须写作(*p)()如果写成*p()会被解释为*(p())
  5. 类型转换的优先级与单目运算相同,单目运算符是自右向左结合,所以*p++会被解释为*(p++),本意是将 p 指向的对象进行加一,结果变成先取 p 所指向的对象,然后 p 加一,因为 ++ 放到后边本意就是先运算再加一
  6. 双目运算符优先级低于单目,双目中算数运算符优先级最高,其次是移位、关系、逻辑、赋值、条件(条件运算符实际为三目运算符)。
  7. 任何一个逻辑运算符的优先级低于任何一个关系运算符。
  8. 移位运算符的优先级比算术运算符要低,比关系运算符要高。
  9. ==!=的优先级低于其他关系运算符。比较 a 与 b 的大小顺序是否与 c 和 d 的大小顺序相同,就可以这样写a < b == c < d
  10. 任何两个逻辑运算符都具有不同的优先级。按位与高于顺序运算,与运算高于或运算,按位异或介于按位与和按位或之间。
  11. 三目条件运算符是运算符中优先级最低的。所以可以在其表达式中包含关系和逻辑运算符的组合。例如:rate = income>400 && residency<5 ? 3.5 : 2.0;该例子也说明了,赋值运算的优先级低于条件运算符是有意义的。
  12. 逗号是所有运算符中优先级最低的。
  13. 涉及到赋值运算时容易引起优先级混淆,尽量使用括号括起。例如:
while (c=getc(in) != EOF)
    putc(c,out);

本意是先将 getc(in) 获得的值赋给 c ,然后 c 再与 EOF 进行比较。但因为赋值的优先级低于比较,所以实际的含义变成了将 getc(in) 获得的值与 EOF 进行是否相同比较,将比较结果赋给 c。所以最后 c 的值就是一串二进制字节流。应改写为如下样例:

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

2.3 注意作为语句结束标志的分号

  1. 注意if判断的条件表达式后,不要误加分号。
  2. 注意循环条件后。不加误加分号。
  3. 函数声明后记得加分号。

2.4 switch 语句

注意每个case语句后的break,不要忘记,否则会一直执行直到遇到下一个 break特殊操作可以省略 break 提高程序效率

2.5 函数调用

调用函数必须加参数列表(即函数名后必须加括号)。

2.6 “悬挂”else引发的问题

else 总是与同一对括号内最近的未匹配的 if 结合。所以 if 之后的语句最好使用大括号进行分隔。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值