语法陷阱##
2.1 理解函数声明###
任何C变量的声明都由两部分组成:类型以及一组类似表达式的声明符(declarator)。
//表示该函数的返回值是一个指向浮点数的指针
float *g();
///表示h是一个指针,h所指向的函数的返回值为浮点类型
float (*h)();
//表示返回值为浮点类型的函数的指针
(float(*)());
((void()())0()表示在计算机启动时,硬件将调用首地址为0位置的子例程。
分析表达式 ((void()())0()
-
假设变量fp是一个函数指针,调用该函数指针指向的函数的调用方法为:
(*fp)()
-
找到一个恰当的表达式来替换fp,对(*fp)()进行转换为:指向返回值为void类型的函数的指针:
void (*fp)()
-
调用存储位置为0的子例程
void (fp)()
(fp) ();
4.对常数0转型为“指向返回值为void的函数的指针”类型,可写成:(void ()()) 0,用(void ()()) 0替代fp,得到(void()())0 ()
5.通过tyepdef可以简化:
typedef void (funcptr) ();
((funcptr)0) ();
2.2 运算符的优先级问题###
1.函数调用的优先级要高于单目运算符的:
p为一个函数指针,要调用p所指向的函数:(*p) ()
2.单目运算符是自右向左结合:
p++会被编译成(p++),即取指针p所指向的对象,然后将p递增1
3.任何一个逻辑运算符的优先级低于任何一个关系运算符
4.移位运算符的优先级比算术运算符要低,但是比关系运算符要高
5.任何两个逻辑运算符都具有不同的优先级。所有的按位运算符优先级要比顺序运算符的优先级高,每个“与”运算要比相应的“或”运算符的优先级要高。按位异或运算符的优先级介于按位与运算符和按位或运算符之间。
2.3 注意作为语句结束标识的分号###
if(x[i]>big);
big=x[i];
if判断语句多写了一个分号,则这个语句单独成为一个语句。相当于语句后多了一个空中括号{}
if(n<3)
return
logrec.date=x[0];
logrec.time=x[1];
logrec.code=x[2];
第三,四行多了分号,前三行自成一个语句块。导致程序没能达到预期结果。
struct logrec
{
int date;
int time;
int code;
}
int main(void)
{
.....
}
在第一个}与紧随其后的函数main定义之间,遗漏了一个分号。上面代码实际的效果是声明函数main的返回值是结构logrec类型。
2.4 switch语句###
C语言的switch语句的控制流程能够依次通过并执行各个case部分。
程序员容易遗漏各个case部分的break语句。但有时候刻意隐去break能达到不一样的效果:
加减法程序:
case SUBTRACT:
opnd2=-opnd2;
case ADD:
.......
2.5 函数调用###
在函数调用即使函数不带参数,也应该包括参数列表:
f()是一个函数调用语句,而f却不是。
2.6 “悬挂”else引发的问题###
if(x==0)
if(y==0) error();
else{
z=x+y;
f(&z);
}
这段代码当x等于0时,如果y也等于0就进行错误处理,y不等于0的情况下,将x与y之和赋给z,然后以z的地址为参数来调用函数f。但是在程序运行的时候却发生错误,由于C语言中的else始终与同一对括号内最近的未匹配的if结果,所以程序运行时是以下列缩进进行的:
if(x==0){
if(y==0)
error();
else{
z=x=y;
f(&z);
}
}
如果x不等于0,程序将不会做任何处理。
改进:
有两种方法可以解决这个问题:
1.将第二个if语句用括号封装起来,这样就不会与else进行结合了。(个人觉得这种方法比较可行)。
2.通过宏定义来解决问题。