还应注意前面书写的if( a ) long c;。这里的意思并不是如果a非零,就定义变量c,这里涉及到作用域的问题,将在下篇说明。
switch 这个语句的定义或多或少地是因为实现的原因而不是和“if else”一样由于逻辑的原因。先来看它的格式:switch(<整型数字>)<语句>。
上面的<整型数字>和if语句一样,只要是一个数字就可以了,但不同地必须是整型数字(后面说明原因)。然后其后的<语句>与前相同,只要是语句就可以。在<语句>中,应该使用这样的形式:case <整型常数1>:。它在它所对应的位置定义了一个标号,即前面goto语句使用的东西,表示如果<整型数字>和<整型常数1>相等,程序就跳转到“case <整型常数1>:”所标识的位置,否则接着执行后续的语句。
long a, b = 3;
switch( a + 3 )
case 2: case 3: a++;
b *= a;
long a, b = 3;
switch( a + 3 )
{
b = 0;
case 2:
a++; // 假设地址为3003
case 3:
a--; // 假设地址为3004
break;
case 1:
a *= a; // 假设地址为3006
}
b *= a; // 假设地址为3010
switch( a ) if( a ) break;
由于是跳到相应位置,因此如果a为-1,则将执行a++;,然后执行a--;,再执行break;而跳到3010地址处执行b *= a;。并且,上面的b = 0;将永远不会被执行。switch表示的是针对某个变量的值,其不同的取值将导致执行不同的语句,非常适合实现状态的选择。比如用1表示安全,2表示有点危险,3表示比较危险而4表示非常危险,通过书写一个switch语句就能根据某个怪物当前的状态来决定其应该做“逃跑”还是“攻击”或其他的行动以实现游戏中的人工智能。那不是很奇怪吗?上面的switch通过if语句也可以实现,为什么要专门提供一个switch语句?如果只是为了简写,那为什么不顺便提供多一些类似这种逻辑方案的简写,而仅仅只提供了一个分支选择的简写和后面将说的循环的简写?因为其是出于一种优化技术而提出的,就好象后面的循环语句一样,它们对逻辑的贡献都可以通过if语句来实现(毕竟逻辑就是判断),而它们的提出一定程度都是基于某种优化技术,不过后面的循环语句简写的成分要大一些。
我们给出一个数组,数组的每个元素都是4个字节大小,则对于上面的switch语句,如下:
unsigned long Addr[3];
Addr[0] = 3006;
Addr[1] = 3003;
Addr[2] = 3004;
上面就是switch的真面目,应注意上面的goto的写法是错误的,这也正是为什么会有switch语句。编译器为我们构建一个存储地址的数组,这个数组的每个元素都是一个地址,其表示的是某条语句的地址,这样,通过不同的偏移即可实现跳转到不同的位置以执行不同的语句进而表现出状态的选择。
现在应该了解为什么上面必须是<整型数字>了,因为这些数字将用于数组的下标或者是偏移,因此必须是整数。而<整型常数1>必须是常数,因为其由编译时期告诉编译器它现在所在位置应放在地址数组的第几个元素中。
了解了switch的实现后,以后在书写switch时,应尽量将各case后接的整型常数或其倍数靠拢以减小需生成的数组的大小,而无需管常数的大小。即case 1000、case1001、case 1002和case 2、case 4、case 6都只用3个元素大小的数组,而case 0、case 100、case 101就需要102个元素大小的数组。应该注意,现在的编译器都很智能,当发现如刚才的后者这种只有3个分支却要102个元素大小的数组时,编译器是有可能使用重复的if语句来代替上面数组的生成。
switch还提供了一个关键字——default。如下:
long a, b = 3;
switch( a + 3 )
{
case 2:
a++;
break;
case 3:
a += 3;
break;
default:
a--;
}
b *= a;
循环语句——for、while、do while
刚刚已经说明,循环语句的提供主要是出于简写目的,因为循环是方法描述中用得最多的,且算法并不复杂,进而对编译器的开发难度不是增加太多。
for 其格式为for(<数字1>;<数字2>;<数字3>)<语句>。其中的<语句>同上,即可接单句也可接复合语句。而<数字1>、<数字2>和<数字3>由于是数字,就是表达式,进而可以做表达式语句能做的所有的工作——操作符的计算。for语句的意思是先计算<数字1>,相当于初始化工作,然后计算<数字2>。如果<数字2>的值为零,表示逻辑假,则退出循环,执行for后面的语句,否则执行<语句>,然后计算<数字3>,相当于每次循环的例行公事,接着再计算<数字2>,并重复。上面的<语句>一般被称作循环体。
上面的设计是一种面向过程的设计思想,将循环体看作是一个过程,则这个过程的初始化(<数字1>)和必定执行(<数字3>)都表现出来。一个简单的循环,如下:
long a, b;
for( a = 1, b = 1; a <= 10; a++ )
b *= a;
long a, b = 1;
for( ; b < 100; )
for( a = 1, b = 1; a; ++a, ++b )
if( b *= a )
switch( a = b )
{
case 1:
a++; break;
case 2:
for( b = 10; b; b-- )
{
a += b * b;}
case 3: a *= a;
}
break;
}
还应注意C++提出了一种特殊语法,即上面的<数字1>可以不是数字,而是一变量定义语句,即可如此:for( long a = 1, b = 1; a < 10; ++a, ++b );。其中就定义了变量a和b。但是也只能接变量定义语句,而结构定义、类定义及函数定义语句将不能写在这里。这个语法的提出是更进一步地将for语句定义为记数式循环的过程,这里的变量定义语句就是用于定义此循环中充当计数器的变量(上面的a)以实现循环固定次数。
最后还应注意上面写的<数字1>、<数字2>和<数字3>都是可选的,即可以:for(;;);。
while 其格式为while(<数字>)<语句>,其中的<数字>和<语句>都同上,意思很明显,当<数字>非零时,执行<语句>,否则执行while后面的语句,这里的<语句>被称作循环体。
do while 其格式为do<语句>while(<数字>);。注意,在while后接了“;”以表示这个单句的结束。其中的<数字>和<语句>都同上,意思很明显,当<数字>非零时,执行<语句>,否则执行while后面的语句,这里的<语句>被称作循环体。
为什么C++要提供上面的三种循环语句?简写是一重要目的,但更重要的是可以提供一定的优化。for被设计成用于固定次数的循环,而while和do while都是用于条件决定的循环。对于前者,编译器就可以将前面提过的用于记数的变量映射成寄存器以优化速度,而后者就要视编译器的智能程度来决定是否能生成优化代码了。
while和do while的主要区别就是前者的循环体不一定会被执行,而后者的循环体一定至少会被执行一次。而出于简写的目的,C++又提出了continue和break语句。如下:
for( long i = 0; i < 10; i++ )
{
if( !( i % 3 ) )
continue;
if( !( i % 7 ) )
break;
// 其他语句
}
while( --i ) do
{ {
if( i == 10 ) if( i == 10 )
continue; continue;
if( i > 20 ) if( i > 20 )
break; break;
// 其他语句 // 其他语句
} }while( --i );
a = i; a = i;
还应注意嵌套问题,即前面说过的else在寻找配对的if时,总是找最近的一个if,这里依旧。
long a = 0;
P1:
for( long i = a; i < 10; i++ )
for( long j = 0; j < 10; j++ )
{
if( !( j % 3 ) )
continue;
if( !( j % 7 ) )
break;
if( i * j )
{
a = i * j;
goto P1;
}
// 其他语句
}
上面那样书写goto语句是不被推荐的,因为其破坏了循环,不符合人的思维习惯。在此只是要说明,for或while、do while等都不是循环,只是它们各自的用处最后表现出来好象是循环,实际只是程序执行位置的变化。应清楚语句的实现,这样才能清楚地了解各种语句的实际作用,进而明确他人写的代码的意思。而对于自己书写代码,了解语句的实现,将有助于进行一定的优化。但当你写出即精简又执行效率高的程序时,保持其良好的可读性是一个程序员的素养,应尽量培养自己书写可读性高的代码的习惯。
上面的long j = 0在第一个循环的循环体内,被多次执行岂不是要多次定义?这属于变量的作用域的问题,下篇将说明。
本篇的内容应该是很简单的,重点只是应该理解源代码编译成机器指令后,在执行时也放在内存中,故每条语句都对应着一个地址,而通过跳转语句即可改变程序的运行顺序。下篇将对此提出一系列的概念,并重点说明类型的意义。
[1] [2]
