一.switch-case
switch-case语句主要用在多分支条件的环境中,在这种环境中使用if语句会存在烦琐且效率不高的弊端。
switch(expression)
{
case const expression1:
....
case const expression2;
...
default:
...
}
在执行过程中,expression的值会与每个case的值比较,实现switch语句的功能。关键字case和它所关联的值被称作case标号。每个case标号的值都必须是一个整形常量表达式,且不能存在两个case标号相同的情形。除此之外,还有一个特殊的case标号-default标号。
如果expression的值与其中一个case标号相匹配,则程序将从该标号后面的第一个语句开始执行各个语句,直到switch结束或者遇到break语句为止,如果没有发生与之匹配的case标号(并且也没有default标号),则程序会从switch语句后面的一条语句继续执行。
关于switch一般存在这样的误解:以为程序只会执行匹配的case标号相关的语句。实际上并非如此,该标号只是程序会执行的起始点,程序会从该点执行,并跨越case边界继续执行其他语句,直到switch结束或遇到break语句为止。
break语句的使用,是switch-case语句的核心。因为在大多数情况下,在下一个case标号前面必须加上一个break语句。
故意省略break是一种特别罕见的用法,因此在这种形式的代码附近,请务必添加一些注释,说明其运行逻辑。
在switch-case结构中,只能在最后一个case标号或default中定义内部变量。指定这种规则是为了避免出现代码跳过变量定义和初始化的情况。
这个规则存在的原因:一般如果定义一个变量,此变量便从此定义开始有效,直到所在的语句块结束。如果在两个case中间定义一个变量,那么对于定义变量的case标号后面的其他标号都可以使用这个变量。但是如果switch从那些后续case标号开始执行呢?可能这个变量没有定义就使用了,这是我们所不想看到的。为了实现在case中可以定义变量,可以引进语句块思想实现。在该语句块中定义变量,从而保证这个变量在使用前定义和初始化。而出了这个语句块该变量就是非法了。所以,在case语句块中,最好不要定义变量,所需的变量应在switch之前进行定义和初始化。如果必须定义变量,请谨慎而为,请采用语句块方式实现内部变量的定义。
对于那些哪怕没有语句在default标号下执行的环境中,定义default标号依然是有用的,定义default标号可明确地告诉读者,这种情况已考虑到,只是没有什么可以执行的。default标号不能单独存在,它必须位于语句之前,如果switch以default结束,而default分支没有什么任务需要执行,那么default标号后面必须添加 个空语句。
二.&&,||
&&和||是C++逻辑或和逻辑与操作符。在求值过程中,仅当a不能确定表达式的值时,才会求解b。在逻辑与表达式中,a的计算结果是true。如果a的计算结果是false,则无论b的值是什么,逻辑表达式的值都为false。当a的值为true时,只有b的值也为true时,逻辑与表达式的值才为true。在逻辑或中同样,这就是短路求值。
&&和&,||和|的区别:&和|是位与和位或操作符。需要两个整型操作数。&,在每个位的位置,如果两个操作数对应的位都为1,则操作结果中该位为1,否则为0。|操作,在每个位的位置,如果两个操作数对应的位只有一个为1,则操作结果中该位为1,否则为0;位操作符和逻辑操作符的每一个区别是||和&&操作符具有短路性质,如果表达式的值根据左操作数就可以决定,则它就不再对右操作数进行求值。与之相反,|和&操作符两边的操作数都需要进行求值。
在C++中,;标示一条语句的结束。如果不小心多写了一个分号,可能不会造成什么不良的后果。这个分号可能会被当做一个不会产生任何效应的空语句;或者这个多余的分号被编译器识别,编译器会因为这个多余的分号产生一条警告语句,根据这个警告语句你可以很快找到并去掉它。但在if,for,while语句之后多加了一个分号,那么原来紧跟在if或者while子句之后的语句与条件判断部分就没有任何关系了。例如
int a = 1;
while(a == 1);
{
a--;
}
正常编译,且循环
同样少了分号也会带来错误,例如
int arrIntMatrix[3] = {0,1,3}
void main()
{
int iIndex = 3;
if(iIndex<3)
return
arrIntMaxtrix[0] = 3;
}
分号标示C++语句结束,多了一个分号和少了一个分号都可能造成不良后果。应小心谨慎。
三.条件运算符和逗号运算符
条件运算符是C++语言唯一的"三目运算符"。其接受3个操作数,同样条件的操作符会控制表达式的求值顺序。用法如下:
expression1?expression2:expression3; (1)
(expression1)?(expression2):(expression3); (2)
条件运算符的优先级非常低,所以它的各操作数即使不加括号,一般也不会产生问题。但是为了表达的清楚性,还是倾向于在各个表达式两端加上括号。首先计算expression1,如果值为真,那么整个表达式的值就是expression2的值,而expression3不会进行求值。否则,如果expression1的值为假,那么整个条件语句的值就是expression3的值,而expression2不会进行求值。
逗号操作符将两个或多个表达式分隔开来,这些表达式自左向右逐个进行求值,而整个表达式的值是最后那个表达式的值。用法如下:
expression1,expression2,expression3,...,expressionn;
逗号操作符中的每个表达式会被求值,而整个表达式的值却是最后一个表达式的值。因为这些表达式的求值顺序已经固定,即采用从左到右的求值顺序。常用在for语句的增量部分,即如果迭代变量不止一个的话,它就派上用场了, for(int i = 0,j=MAX;i<=j;++i,--j)
四.循环语句
不可在for循环全内修改循环变量,防止for循环失去控制,最终导致死循环等奇怪现象
建议for语句的循环控制变量的取值采用"半开半闭区间"写法,因为这种写法更加直观。
x值属于半开半闭区间"0<=x<N",起点到终点的间隔为N,循环次数为N x值属于闭区间"0<=x<=N-1",起点到终点的间隔为N-1,循环次数为N
for(int x=0;x<N;x++) for(int x=0;x<=N-1;x++) //代码1更加直观
在C/C++循环语句中,for语句使用的频率最高,while语句次之,do..while语句很少用。
在多重循环中,如果有可能,应将最长的循环放在最内层,最短的循环放在最外层,以减少CPU跨切循环层的次数。原因如下:最长循环放到内部可以提高I cache的效率,降低因为循环跳转造成cache的miss以及流水线flush造成的延时,多次相同循环后也能提高跳转预测的成功率,提高流水线效率。
示例1:for(row = 0;row<100;row++) 示例2:for(row = 0;row<100;row++)
for(col=0;col<5;col++) for(col=0;col<5;col++)
{ {
sum = sum+a[row][col]; sum = sum+a[row][col];
} }
示例1低效率,长循环在最外层,示例2高效率,长循环在最内层
如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面。
示例3:for(i = 0;i<100;i++) 示例4:if(condition)
{ {
if(condition) for(i = 0;i<100;i++)
DoSomething(); DoSomething();
else }else
DoOtherthing(); {
} for(i = 0;i<100;i++)
DoSomething();
}
示例3的程序比示例4多执行了N-1次逻辑判断。并且由于前者总要进行逻辑判断,打断了循环"流水线"作业,使得编译器不能对循环进行做优化处理,降低了效率。如果N非常大,最好采用示例4的写法,可以提高效率。如果N非常小,两者效率差别并不明显,采用示例3的写法比较好。因为程序更加简洁。