写递归函数时一定要记得写Base Case,否则即使递推关系正确,整个函数也不正确。
for语句
以上我们在while或do/while循环中使用循环变量,其实使用循环变量最常用的是for循环这种形式。for语句的格式为:
for(控制表达式1;控制表达式2;控制表达式3) 语句
如果不考虑语句中包含continue语句的情况(稍后介绍continue语句),这个for循环等价于下列的while循环:
控制表达式1;while(控制表达式2) { 语句 控制表达式3;}
从这种等价形式来看,控制表达式1和3都可以为空,但控制表达式2是必不可少的,例如,for(;1;){...}等价于while(1){...}死循环。C语言规定,如果控制表达式2为空,则当作控制表达式2的值为真,因此,死循环也可以写成for(;;){...}。
中++i这个表达式相当于i = i + 1,++称为前缀自增运算符(Prefix Increment Operator),类似地,--称为前缀自减运算符(Prefix Decrement Operator),--i相当于i = i - 1[10]。如果把++i这个表达式看作一个函数调用,除了传入一个参数返回一个值(等于参数值加1)之外,还产生一个Side Effect,就是把变量i的值增加了1。
++和--运算符也可以用在变量后面,例如i++和i--,为了和前缀运算符区别,称为后缀自增运算符(Postfix Increment Operator)和后缀自减运算符(Postfix Decrement Operator)。如果把i++这个表达式看作一个函数调用,除了传入一个参数返回一个值(就等于参数值)之外,还产生一个Side Effect,就是把变量i的值增加了1,它和++i的区别就在于返回值不同。同理,--i返回减1之后的值,而i--返回减1之前的值,但这两个表达式都产生同样的Side Effect,就是把变量i的值减了1。
我们看一个有意思的问题:a+++++b这个表达式如何理解?应该理解成a++ + ++b还是a++ +++b呢?应该按后一种方式理解。编译的过程分为词法解析和语法解析两个阶段,在词法解析阶段,编译器总是从前到后找最长的合法Token。把这个表达式从前到后分析,变量名a是一个Token,运算符++是一个Token,再往后找又有两个+号,根据最长匹配原则,编译器绝不会止步于一个+号,而一定会把这两个+号当作一个Token。然后进入下一阶段语法解析,a是一个表达式,表达式++还是表达式,表达式再++还是表达式,表达式再+b还是表达式,语法上没有问题。最后编译器会做一些基本的语义分析,这时就有问题了,++运算符要求操作数能做左值,a能做左值所以a++没问题,但表达式a++的值只能做右值,不能再++了,所以最终编译器会报错。
break语句的一种用法,用来跳出switch语句块,这个语句也可以用来跳出循环体。continue语句也用来终止当前循环,和break语句不同的是,continue语句终止当前循环后又回到循环体的开头准备再次执行循环体。对于while和do/while,continue之后测试控制表达式,如果值为真则继续执行下一次循环;对于for循环,continue之后首先计算控制表达式3,然后测试控制表达式2,如果值为真则继续执行下一次循环。
,在学习一门编程语言时,要特别注意以下三方面:
1. 这门语言提供了哪些Primitive,比如基本数据类型,比如基本的运算符、表达式和语句。
2. 这门语言提供了哪些组合规则,比如复合数据类型,比如表达式和语句的组合规则。
3. 这门语言提供了哪些抽象机制,例如数据抽象和过程抽象(Procedure Abstraction)。
struct complex_struct { double x, y;} z1, z2;
这样z1和z2就是两个变量名,变量定义后面带个;号是我们早就习惯的。但即使像上面那样只定义了complex_struct这个Tag而不定义变量,后面的;号也不能少。这点一定要注意,结构体定义后面少;号是初学者很常犯的错误。不管是用上面两种形式的哪一种形式定义了complex_struct这个Tag,以后都可以直接用struct complex_struct来代替类型名了。例如可以这样定义另外两个复数变量:
struct complex_struct z3, z4;
因为基本上所有的C代码风格对于空白符的规定都差不多,主要有以下几条。
1、关键字if, while, for与其后的控制表达式的(括号之间插入一个空格分隔,但括号内的表达式应紧贴括号。例如:
while(1);
2、双目运算符的两侧插入一个空格分隔,单目运算符和操作数之间不加空格,例如i=i+1、++i、!(i<1)、-x、&a[1]等。
3、后缀运算符和操作数之间也不加空格,例如取结构体成员s.a、函数调用foo(arg1)、取数组成员a[i]。
4、,号和;号之后要加空格,这是英文的书写习惯,例如for(i=1;i<10;i++)、foo(arg1,arg2)。
5、以上关于双目运算符和后缀运算符的规则不是严格要求,有时候为了突出优先级也可以写得更紧凑一些,例如for(i=1;i<10;i++)、distance=sqrt(x*x+y*y)等。但是省略的空格一定不要误导了读代码的人,例如a||b&&c很容易让人理解成错误的优先级。
6、由于标准的Linux终端是24行80列的,接近或大于80个字符的较长语句要折行写,折行后用空格和上面的表达式或参数对齐,例如:
if(sqrt(x*x+y*y)>5.0 &&x<0.0 &&y>0.0)
再比如:
foo(sqrt(x*x+y*y), a[i-1]+b[i-1]+c[i-1])
7、较长的字符串可以断成多个字符串然后分行书写,例如:
printf("This is such a long sentence that " "it cannot be held within a line/n");
C编译器会自动把相邻的多个字符串接在一起,以上两个字符串相当于一个字符串"This issuch a long sentence that it cannot be held within a line/n"。
8、有的人喜欢在变量定义语句中用Tab字符,使变量名对齐,这样看起来也很好,但不是严格要求的。
→int →a, b; →double →c;