第3章 运算符、控制结构与指针 C++语言中集成了大量的运算符,运算符直接作用在数据上,与数据一起构成表达式。C++语言的语句由表达式构成,语句必须以分号结尾。C++语言的最小功能单元为函数,函数由语句组成。程序中语句或函数的组织方式由控制结构决定,程序有3种常见的控制结构,即顺序执行、分支执行和循环执行结构。指针是一种重要的数据类型,与第2章所介绍的各种数据类型不同的是,指针保存的是变量或函数的地址。本章将详细介绍C++的运算符、控制结构和指针。 本章的学习目标: 了解程序语句的3种控制方式 熟练掌握两种分支控制和4种循环控制方式 掌握指针的定义与调用方法 学习应用分支控制和循环控制进行程序设计 3.1运算符 在C++语言中,运算符与数据结合在一起,构成表达式。在计算表达式的值时,按照运算符的优先级顺序计算,即先计算优先级高的运算符,再计算优先级低的运算符; 当运算符的优先级相同时,按照规定的次序(又称结合次序)进行计算,一般按从左向右的顺序,但对于赋值运算符,则按照从右向左的顺序。 当表达式中运算符的优先级和结合性不确定时,可以将需要优先计算的部分用圆括号“()”括起来,此时将优先计算圆括号中的表达式。 C++语言的全部运算符及其优先级如表31所示。 表31C++语言的运算符及其优先级 优先级 运算符 备注 1 ( ),[ ],>,. ,::,!,~,++,-- 后置++和后置--运算符的优先级高于前置++和前置-- 2 -,*,&,sizeof -负号、*取内容、&取地址 3 (强制类型转换) 4 >*,.* 5 *,/,% *乘、/除、%取模 6 +,- +加、-减续表 优先级 运算符 备注 7 <<,>> 8 <,<=,>,>= 9 ==,!= 10 & &按位与 11 ^ 12 | 13 && 14 || 15 ? : 条件运算符,是唯一的三目运算符 16 =,+=,-=,*=,/=,%=,<<=,>>=,&=,^=,|= 赋值或复合赋值运算符 17 , 逗号运算符 下面讨论表31中常用的运算符的使用方法。 3.1.1算术运算符 算术运算符包括加、减、乘、除和求余,依次用符号+、-、*、/和%表示。下面在程序段31中用整数演示了算术运算符的用法。 视频讲解 程序段31算术运算符的用法 1#include <iostream> 2#include <cmath> 3using namespace std; 4 5int main() 6{ 7int a1 = 12, a2 = 83, a3 = 4; 8cout << "a1 + a2 = " << a1 + a2 << endl; 9cout << "a1 - a2 = " << a1 - a2 << endl; 10cout << "a1 * a2 = " << a1 * a2 << endl; 11cout << "a1 / a2 = " << a1 / a2 << endl; 12cout << "a2 % a1 = " << a2 % a1 << endl; 13cout << "a1 + a2 / a3 = " << a1 + a2 / a3 << endl; 14cout << "(a1 + a2) / a3 = " << (a1 + a2) / a3 << endl; 15cout << "pow(a1,a3) = " << pow(a1, a3) << endl; 16} 在程序段31中,第2行的预编译指令“#include <cmath>”将头文件cmath包括到程序中,是因为第15行使用其中的函数pow(),pow()函数用于求乘方运算。 在main()函数中,第7行的语句“int a1=12,a2=83,a3=4;”定义了3个整型变量a1、a2和a3,分别赋了初值12、83和4。 第8行的语句“cout << "a1+a2=" << a1+a2 << endl;”输出字符串“"a1+a2="”和a1加a2的和。 第9行的语句“cout << "a1-a2=" << a1-a2 << endl;”输出字符串“"a1-a2="”和a1减去a2的差。 第10行的语句“cout << "a1 * a2=" << a1 * a2 << endl;”输出字符串“"a1 * a2="”和a1乘以a2的积。 第11行的语句“cout << "a1 / a2=" << a1 / a2 << endl;”输出字符串“"a1 / a2="”和a1除以a2的商。注意,两个整数的除法运算,其结果仍为整数。 第12行的语句“cout << "a2 % a1=" << a2 % a1 << endl;”输出字符串“"a2 % a1="”和a2除以a1的余数。 第13行的语句“cout << "a1+a2 / a3=" << a1+a2 / a3 << endl;”输出字符串“"a1+a2 / a3="”以及a2除以a3的商加上a1的和。这里,先算除法再算加法。 第14行的语句“cout << "(a1+a2) / a3=" << (a1+a2) / a3 << endl;”输出字符串“"(a1+a2) / a3="”和(a1+a2) / a3的值。这时先算括号内的加法,再计算除法运算。 第15行的语句“cout << "pow(a1,a3)=" << pow(a1,a3) << endl;”调用系统函数(或称库函数)pow计算a1a3的值,并输出在计算机显示器上。函数pow为求乘方运算。系统函数pow位于头文件cmath中,故需要在第2行将头文件cmath包括到程序中。 算术运算符+、-、*、/均可以应用于整数和浮点数; 但是求余运算%只能应用于整数,而不能应用于浮点数。由于现有的计算机均为64位机器,并且Windows操作系统也为64位系统,因此,建议使用长整型long long和双精度浮点型double的数据进行算法运算,可避免数据类型的隐式转换,从而提高运算速度。如果使用了其他的类型,那么在计算中含有长整型的表达式将统一转化为64位进行运算,含有双精度浮点型的表达式统一转化为64位进行运算,运算结果再转换为所需要的数据类型存储。 程序段31的执行结果如图31所示。 图31程序段31的执行结果 3.1.2关系运算符 关系运算符连接的式子称为关系表达式,关系表达式的返回值为逻辑值(或称布尔值)。关系运算符包括大于、小于、等于、大于或等于、小于或等于和不等于,对应的算符依次为>、<、==、>=、<=和!=。注意,关系运算符的等于为“==”(两个等号,中间无空格),而“=”在C++语言中为赋值运算符。 关系运算符中,“大于”“小于”“大于或等于”“小于或等于”的优先级相同,其优先级比“等于”和“不等于”的优先级高,而“等于”和“不等于”的优先级相同。 下面程序段32详细说明各个关系运算符的用法。 视频讲解 程序段32关系运算符用法实例 1#include <iostream> 2using namespace std; 3 4int main() 5{ 6int a1 = 3, a2 = 5; 7cout << "a1 = " << a1 << endl; 8cout << "a2 = " << a2 << endl; 9bool b1, b2, b3, b4, b5, b6; 10b1 = a1 > a2; 11b2 = a1 < a2; 12b3 = a1 >= a2; 13b4 = a1 <= a2; 14b5 = a1 == a2; 15b6 = a1 != a2; 16if (b1) 17cout << "a1>a2" << endl; 18if (b2) 19cout << "a1<a2" << endl; 20if (b3) 21cout << "a1>=a2" << endl; 22if (b4) 23cout << "a1<=a2" << endl; 24if (b5) 25cout << "a1==a2" << endl; 26if (b6) 27cout << "a1!=a2" << endl; 28} 在程序段32的main函数中,第6行的语句“int a1=3,a2=5;”定义了两个整型变量a1和a2,分别赋初值为3和5。 第7行的语句“cout << "a1=" << a1 << endl;”输出字符串"a1="和a1的值。 第8行的语句“cout << "a2=" << a2 << endl;”输出字符串"a2="和a2的值。 第9行的语句“bool b1,b2,b3,b4,b5,b6;”定义6个逻辑型变量b1、b2、b3、b4、b5和b6。 第10行的语句“b1=a1 > a2;”将关系表达式“a1 > a2”的逻辑值赋给变量b1。 第11行的语句“b2=a1 < a2;”将关系表达式“a1 < a2”的逻辑值赋给变量b2。 第12行的语句“b3=a1 >=a2;”将关系表达式“a1 >=a2”的逻辑值赋给变量b3。 第13行的语句“b4=a1 <=a2;”将关系表达式“a1 <=a2”的逻辑值赋给变量b4。 第14行的语句“b5=a1 ==a2;”将关系表达式“a1 ==a2”的逻辑值赋给变量b5。 第15行的语句“b6=a1 !=a2;”将关系表达式“a1 !=a2”的逻辑值赋给变量b6。 第16行和第17行为一个if结构,第16行判断b1的值是否为真,如果为真,则执行第17行,输出字符串“a1>a2”。 第18行和第19行为一个if结构,第18行判断b2的值是否为真,如果为真,则执行第19行,输出字符串“a1<a2”。 第20行和第21行为一个if结构,第20行判断b3的值是否为真,如果为真,则执行第21行,输出字符串“a1>=a2”。 第22行和第23行为一个if结构,第22行判断b4的值是否为真,如果为真,则执行第23行,输出字符串“a1<=a2”。 第24行和第25行为一个if结构,第24行判断b5的值是否为真,如果为真,则执行第25行,输出字符串“a1==a2”。 第26行和第27行为一个if结构,第26行判断b6的值是否为真,如果为真,则执行第27行,输出字符串“a1!=a2”。 程序段32的执行结果如图32所示。 图32程序段32的执行结果 3.1.3逻辑运算符 逻辑运算符只能对逻辑值true或false进行运算,由于关系表达式的值为逻辑值,所以,逻辑运算符可以连接关系表达式。逻辑运算符包括逻辑与&&、逻辑或||和逻辑非!。其中,逻辑非的优先级最高,逻辑与的优先级次之,逻辑或的优先级最低。 当逻辑与连接多个关系表达式时,例如,(关系表达式1) && (关系表达式2) && (关系表达式3) && (关系表达式4),若“关系表达式1”的值为假,则后续的关系表达式不再计算,整个表达式直接返回逻辑值假。当逻辑或连接多个关系表达式时,例如,(关系表达式1) || (关系表达式2) || (关系表达式3) || (关系表达式4),若“关系表达式1”的值为真,则后续的关系表达式不再计算,整个表达式直接返回逻辑值真。 下面的程序段33介绍了逻辑运算符的用法。 视频讲解 程序段33逻辑运算符的用法 1#include <iostream> 2using namespace std; 3 4int main() 5{ 6bool b1=false, b2=false, b3=true, b4=true; 7bool b5, b6, b7; 8b5 = !(b1 && b2) || !(b3 && b4); 9b6 = !b1 || !b2; 10b7 = (12 > 7) && (10 > 3); 11if (b5) 12cout << "b5 = true" << endl; 13if (b6) 14cout << "b6 = true" << endl; 15if (b7) 16cout << "b7 = true" << endl; 17} 在程序段33的main()函数中,第6行的语句“bool b1=false,b2=false,b3=true,b4=true;”定义了4个逻辑型变量b1、b2、b3和b4,分别赋初值为false、false、true、true。 第7行的语句“bool b5,b6,b7;”定义了3个逻辑型变量b5、b6和b7。 第8行的语句“b5=!(b1 && b2) || !(b3 && b4);”计算b1和b2的逻辑与,然后,对这个结果取逻辑非,如果为真,则将逻辑真赋给b5; 如果“!(b1 && b2)”的逻辑值为假,则计算“!(b3 && b4)”的逻辑值,即将b3与b4进行逻辑与操作,再对其值取逻辑非。如果“!(b3 && b4)”的逻辑值为真,则将逻辑真赋给b5; 否则,将逻辑假赋给b5。这里根据b1~b4的逻辑值,可知b5为真。 第9行的语句“b6=!b1 || !b2;”先计算b1的逻辑非,如果结果为真,则将逻辑真赋给b6; 否则,计算b2的逻辑非,如果结果为真,将逻辑真赋给b6,否则,将逻辑假赋给b6。这里根据b1和b2的逻辑值,可知b6为真。 第10行的语句“b7=(12>7) && (10>3);”先计算关系表达式“(12>7)”的值,如果该结果为假,则将逻辑假赋给b7; 这里,关系表达式“(12>7)”的值为真,因此,需要继续计算关系表达式“(10>3)”的值,由于“(10>3)”的值为真,故整个表达式的返回值为真,即将逻辑真赋给b7。 第11行和第12行的语句为一个if结构,第11行判断b5是否为真,如果为真,则执行第12行,输出“b5=true”。 第13行和第14行的语句为一个if结构,第13行判断b6是否为真,如果为真,则执行第14行,输出“b6=true”。 第15行和第16行的语句为一个if结构,第15行判断b7是否为真,如果为真,则执行第16行,输出“b7=true”。 程序段33的执行结果如图33所示。 图33程序段33的执行结果 3.1.4位运算符 支持位运算是C++语言的特色,这使C++语言广泛应用于微控制器的程序设计中。位运算符包括按位取反~、按位与&、按位或 |、按位异或 ^、按位左移 << 和按位右移 >>。例如,设一个16比特的无符号整型变量a的值为0x7755,即0111 0111 0101 0101B,执行下面的位运算: (1) ~a 将a的各位取值,原来的0变为1,原来的1变为0,则得到0x88aa,即1000 1000 1010 1010B。 (2) a & 0xFF 这里与0xFF相与,即保留a的低8位不变,其高8位清0,得到0x0055,即0000 0000 0101 0101B。 (3) a | 0xFF 这里与0xFF相或,即保留a的高8位不变,其低8位全部置1,得到0x77FF,即0111 0111 1111 1111B。 (4) a ^0xFF 将a与0xFF相异或,即保留a的高8位不变,其低8位中的0变为1、1变为0,得到0x77AA,即0111 0111 1010 1010B。 (5) a>>2 将a向右移动2位,相当于a/4,得到0x1DD5,即0001 1101 1101 0101B。 (6) a<<2 将a向左移动2位,相当于a乘以4,得到0xDD54,即1101 1101 0101 0100B。 上述位运算可通过下面的程序段34验证。 视频讲解 程序段34位运算符的用法示例 1#include <iostream> 2using namespace std; 3 4#define Int16U unsigned short 5 6int main() 7{ 8Int16U a = 0x7755; 9Int16U b1, b2, b3, b4, b5, b6; 10b1 = ~a; 11b2 = a & 0xFF; 12b3 = a | 0xFF; 13b4 = a ^ 0xFF; 14b5 = a >> 2; 15b6 = a << 2; 16cout << hex << "b1 = 0x" << b1 << endl; 17cout << "b2 = 0x" << b2 << endl; 18cout << "b3 = 0x" << b3 << endl; 19cout << "b4 = 0x" << b4 << endl; 20cout << "b5 = 0x" << b5 << endl; 21cout << "b6 = 0x" << b6 << endl; 22} 在程序段34中,第4行的宏定义指令“#define Int16U unsigned short”将数据类型unsigned short宏定义为Int16U,在程序中出现Int16U的地方,在编译时自动替换为unsigned short。宏定义指令也是一种预编译指令,在程序的编译阶段进行解析,常用宏定义指令定义一些常量,例如“#definePI3.14159”,这样程序中可以使用PI,相当于将PI视为常量。 在main()函数中,第8行的语句“Int16U a=0x7755;”定义无符号短整型a,并赋初值0x7755,这里的0x7755表示这是一个十六进制数的形式。 第9行的语句“Int16U b1,b2,b3,b4,b5,b6;”定义6个无符号短整型变量b1、b2、b3、b4、b5和b6。 第10行的语句“b1=~a;”将a按位取反,取反后的值赋给b1。 第11行的语句“b2=a & 0xFF;”将a与0xFF取与,即保留a的低8位不变,将高8位清0,之后的结果赋给b2。 第12行的语句“b3=a | 0xFF;”将a与0xFF取或,即保留a的高8位不变,将其低8位全部置为1,之后的结果赋给b3。 第13行的语句“b4=a ^0xFF;”将a与0xFF取异或,即保留a的高8位不变,将其低8位中的0变成1、1变成0,之后的结果赋给b4。 第14行的语句“b5=a >> 2;”将a右移2位后的结果赋给b5。 第15行的语句“b6=a << 2;”将a左移2位后的结果赋给b6。 第16行的语句“cout << hex << "b1=0x" << b1 << endl;”中,“hex”表示以十六进制形式输出数值,这里输出字符串“b1=0x”和b1的十六进制值。“hex”是一个全局作用符,其后的所有数值输出都将采用十六进制形式,若需要改为十进制形式输出,则需要指示符“dec”。 第17~21行以十六进制形式输出b2~b6的值。 程序段34的执行结果如图34所示。 图34程序段34的执行结果 3.1.5自增自减运算符 自增自减运算符为单目运算符,只有一个操作数。自增运算符表示为“++”(中间无空格),对于整型变量i而言,“i++”或“++i”都相当于i=i+1。自减运算符表示为“--”(中间无空格),对于整型变量i而言,“i--”或“--i”都相当于i=i-1。 如果将包含自增运算符或自减运算符的表达式用于赋值时,操作数位于自增自减运算符的前面和后面,其表达式的计算结果不相同。例如,对于整型变量i=5和j而言,执行表达式“j=i++”时,先取出i的值赋给j,然后,i自增1,即执行后,i的值为6,而j的值为5。同样地,对于整型变量i=5和j而言,执行表达式“j=++i”时,先将i的值自增1,然后,将i的值赋给j,即执行后,i和j的值都为6。对于自减运算符是同样的道理。 下面的程序段35详细说明了自增和自减运算符的用法。 视频讲解 程序段35自增与自减运算符的用法 1#include <iostream> 2using namespace std; 3 4typedef unsigned int Int32U; 5 6int main() 7{ 8Int32U i, j, k, u, v; 9i = 10; 10cout << "i = " << i << endl; 11j = i++; 12cout << "i = " << i << ", j = " << j << endl; 13k = ++i; 14cout << "i = " << i << ", k = " << k << endl; 15u = --i; 16cout << "i = " << i << ", u = " << u << endl; 17v = i--; 18cout << "i = " << i << ", v = " << v << endl; 19} 在程序段35中,第4行的语句“typedef unsigned int Int32U;”借助于typedef关键字自定义变量类型,这里将已有的变量类型unsigned int自定义为一个新的变量类型Int32U,这种自定义变量类型的作用类似于程序段34中的宏定义方式,可以把较长的变量类型缩短为新的变量类型名称,但是,这里的“typedef unsigned int Int32U;”是一条C++语句,而不是预编译指令。自定义后的新类型Int32U和unsigned int完全相同。 在main()函数中,第8行的语句“Int32U i,j,k,u,v;”定义了5个无符号整型变量i、j、k、u、v。 第9行的代码“i=10;”将i赋为10。 第10行的代码“cout << "i=" << i << endl;”输出字符串“i=”和i的值。 第11行的语句“j=i++;”先将i的值赋给j,然后,i再自增1。 第12行的语句“cout << "i=" << i << ",j=" << j << endl;”输出字符串"i="、i的值和字符串"j="、j的值。 第13行的语句“k=++i;”先将i自增1,然后,将i的值赋给k。 第14行的语句“cout << "i=" << i << ",k=" << k << endl;” 输出字符串"i="、i的值和字符串"k="、k的值。 第15行的语句“u=--i;”先将i的值自减1,然后,将i的值赋给u。 第16行的语句“cout << "i=" << i << ",u=" << u << endl;” 输出字符串"i="、i的值和字符串"u="、u的值。 第17行的语句“v=i--;”先将i的值赋给v,然后,先将i的值自减1。 第18行的语句“cout << "i=" << i << ",v=" << v << endl;” 输出字符串"i="、i的值和字符串"v="、v的值。 程序段35的执行结果如图35所示。 图35程序段35的执行结果 3.1.6赋值运算符与sizeof运算符 赋值运算符形式为“=”,其将赋值运算符右边的表达式的值赋给左边的变量。赋值运算符和算术运算符以及位运算符可以合并为复合赋值运算符,即+=、-=、*=、/=、%=、<<=、>>=、&=、|=、^=,复合运算符中的两个运算符间不能添加空格。 复合赋值运算符主要是为了书写方便,例如,对于两个整型变量a和b,表达式a +=b等价于a=a+b,b也可以为一个表达式。同样地,a -=b等价于a=a – b; a *=b等价于a=a * b; a /=b等价于a=a / b; a %=b等价于a=a % b; a <<=b等价于a=a << b; a >>=b等价于a=a >> b; a &=b等价于a=a & b; a |=b等价于a=a | b; a ^=b等价于a=a ^ b。 sizeof运算符用于统计变量或数据类型所占的存储空间大小,以字节为单位。例如,sizeof(int)返回int类型的长度,为4(个字节); 若有双精度浮点型变量d,则sizeof(d)返回d在存储器中的长度,即8(个字节)。 下面的程序段36说明了赋值运算符和sizeof运算符的用法。 视频讲解 程序段36赋值运算符和sizeof运算符的用法 1#include <iostream> 2using namespace std; 3 4int main() 5{ 6int a = 0, b = 10, c = 11, d = 12, e = 13; 7a += (b -= (c *= (d /= (e %= 5)))); 8cout << "a = " << a << endl; 9unsigned short u = 0xFFFF, v = 0x6033, w = 0x3AA, x = 0x3C, y = 5; 10u &= (v |= (w ^= (x <<= (y >>= 1)))); 11cout << hex << "u = 0x" << u << dec << endl; 12double score[10] = {0.1,0.2}; 13int n = sizeof(score); 14cout << "Size of double:" << sizeof(double) << ", n = " << n << endl; 15struct Student 16{ 17char name[20]; 18char gender; 19double score[10]; 20}st[10]; 21cout << "Size of st[0]:" << sizeof(st[0]) << 22", Size of st:" << sizeof(st) << endl; 23} 在程序段36的main()函数中,第6行的语句“int a=0,b=10,c=11,d=12,e=13;”定义了5个整型变量a、b、c、d、e,并分别赋初值0、10、11、12、13。 第7行的语句“a +=(b -=(c *=(d /=(e %=5))));”先算最里面括号的表达式,再依次向外层计算,最后得到a的值为-34。 第8行的语句“cout << "a=" << a << endl;”输出字符串“a=”和a的值。 第9行的语句“unsigned short u=0xFFFF,v=0x6033,w=0x3AA,x=0x3C,y=5;”定义无符号短整型u、v、w、x和y,并分别赋值为0xFFFF、0x6033、0x3AA、0x3C、5。 第10行的语句“u &=(v |=(w ^=(x <<=(y >>=1))));”先计算最里层括号的表达式,再依次向外层计算,最后得到u的值为0x637B。 第11行的语句“cout << hex << "u=0x" << u << dec << endl;”以十六进制形式输出u的值,然后恢复十进制输出格式。 第12行的语句“double score[10]={0.1,0.2};”定义双精度浮点型数组score,包含10个元素,前两个元素分别初始化为0.1和0.2,其余元素初始化为0。 第13行的语句“int n=sizeof(score);”使用sizeof运算符得到score数组的长度(即在存储器中占有的字节数),将其赋给变量n。 第14行的语句“cout << "Size of double: " << sizeof(double) << ",n=" << n << endl;”输出double类型的长度和数组score的长度(即n的大小)(均为字节为单位)。 第15~20行声明了结构体类型Student,并定义了Student类型的数组st,长度为10。 第21行和第22行为一条语句,输出了结构体数组的一个元素st[0]的长度以及结构体数组st的长度。这里,需要注意,直观上Student类型占101个字节(因为char占1个字节、double占8个字节),但是存储Student类型的字段name和gender将使用21个字节,这里,存储后续的score将从第22个字节开始存储,然后,score是double型(占8字节),它的存储首地址的低3位必须为0,所以,将空出3个字节,从第25个字节开始存储。所以,一个Student结构体将占据104个字节的空间。为不失一般性,设name[0]的首地址为0x1000 0000 0000,则gender的存储地址将为0x1000 0000 0014,此时,score的存储首地址必须为0x1000 0000 0018。故第21行和第22行输出的st[0]的长度为104字节,而st的长度为1040字节。 程序段36的执行结果如图36所示。 图36程序段36的执行结果 3.1.7条件运算符 大部分运算符具有两个操作数,称为双目运算符; 一少部分运算符具有一个操作数,称为单目运算符; C++语言中存在一个唯一个三目运算符,具有3个操作数,称为条件运算符,用符号“? :”表示。 设条件运算符连接操作数得到的表达式为“(关系表达式或逻辑表达式)?(表达式1): (表达式2)”,则其执行的操作为: 首先判断“关系表达式或逻辑表达式”的值,如果该值为真,则计算“表达式1”并返回其结果; 否则,计算“表达式2”并返回其结果。 下面程序段37借助于条件运算符实现下面的分段线性映射: F(x)=xp,x∈[0,p] x-p0.5-p,x∈(p,0.5] F(1-x,p),x∈(0.5,1] 视频讲解 程序段37条件运算符的用法 1#include <iostream> 2#include <iomanip> 3using namespace std; 4 5int main() 6{ 7double x, p; 8cout << "Input x(0<x<1,x!=0.5):"; 9cin >> x; 10cout << "Input p(0<p<0.5):"; 11cin >> p; 12double y; 13x = (x > 0.5) ? 1 - x : x; 14y = (x < p) ? x / p : (x - p) / (0.5 - p); 15cout << "y = " <<setprecision(3)<< y << endl; 16} 在程序段37中,第2行的预编译指令“#include <iomanip>”将头文件iomanip包括在程序中,这是因为程序中使用了库函数setprecision(),其中的“setprecision(3)”表示设定小数位数为3位。 在main()函数中,第7行的语句“double x,p;”定义了双精度浮点型变量x和p。 第8行的语句“cout << "Input x(0<x<1,x!=0.5): ";”输出提示信息“Input x(0<x<1,x!=0.5):”,这里要求输入x,x的值为0~1间,且不能等于0.5。 第9行的语句“cin >> x;”输入变量x的值。 第10行的语句“cout << "Input p(0<p<0.5): ";”输出提示信息“Input p(0<p<0.5):”,这里要求输入p,且要求p的值为0~0.5。 第11行的语句“cin >> p;”输入变量p的值。 第12行的语句“double y;”定义双精度浮点型变量y。 第13行的语句“x=(x > 0.5) ? 1-x : x;”使用条件运算符,首先判断x的值是否大于0.5,如果是,则返回1-x的值(覆盖原来的x); 如果x的值不大于0.5,则返回x的值。 第14行的语句“y=(x < p) ? x / p : (x-p) / (0.5-p);”使用条件运算符,首先判断x的值是否大于p,如果是,则返回x/p的值给y; 如果x的值不大于p,则返回(x-p)/(0.5-p)的值给y。 第15行的语句“cout << "y=" <<setprecision(3)<< y << endl;”输出字符串"y="和y的值,其中,y的值保留3位小数。 程序段37的运行结果如图37所示。 图37程序段37的运算结果 3.1.8逗号运算符 逗号运算符是优先级最低的运算符,比赋值运算符还低一级。在定义多个变量时,例如“inta,b,c;”中出现的逗号,并非逗号运算符,这里的逗号只起到分隔符的作用。在可执行语句中出现的逗号,则是逗号运算符。逗号运算符可以出现多个,例如,“a=(3*5,5*6,7*8,8*9);”中,出现了3个逗号运算符,按照从左向右的次序依次计算各个表达式,最后一个表达式的值为整个括号中的逗号表达式的值,即这里的a的值为72。 程序段38演示了逗号运算符的用法。 视频讲解 程序段38逗号运算符的用法 1#include <iostream> 2using namespace std; 3 4int main() 5{ 6int a, b=3; 7a = (b * 4, 6 * 9); 8cout << "a = " << a << endl; 9a = b * 4, 6 * 9; 10cout << "a = " << a << endl; 11a = b * 4, b = 6 * 9; 12cout << "a = " << a << ", b = " << b<< endl; 13} 在程序段38的main()函数中,第6行的语句“int a,b=3;”定义了变量a和b,并将b赋初值3。 第7行的语句“a=(b * 4,6 * 9);”中使用了逗号运算符,由于使用括号,先算括号里面的,逗号运算符的结合性为自左向右,即先计算“b * 4”,再计算“6 * 9”,并将后者的结果作为逗号运算符连接的表达式的值,故a的值为54。 第8行的语句“cout << "a=" << a << endl;”输出字符串"a="和a的值。 第9行的语句“a=b * 4,6 * 9;”使用了逗号运算符,由于逗号运算符的优先级比赋值运算符低,所以,先计算“a=b * 4”,再计算“6 * 9”,即得到a为12(b的值为4),而“6 * 9”的结果没有保存。 第10行的语句“cout << "a=" << a << endl;” 输出字符串"a="和a的值。 第11行的语句“a=b * 4,b=6 * 9;”使用了逗号表达式,由于逗号运算符的优先级比赋值运算符低,所以,先计算“a=b * 4”,再计算“b=6 * 9”,得到a的值为12,b的值为54。 第12行的语句“cout << "a=" << a << ",b=" << b<< endl;”输出字符串"a="和a的值以及字符串"b="和b的值。 程序段38的执行情况如图38所示。 图38程序段38的执行结果 3.2分支控制 程序的执行方式只有3种,即顺序执行、分支执行和循环执行。顺序执行是总的执行方式,是指语句按照先后顺序依次执行。循环执行是指一组语句在特定的条件下反复执行,直到该条件不再满足为止。而分支执行表示在某种情况下将执行这一组语句,而另一种情况下,将执行另一组语句。本节将介绍C++语言的分支控制方法,主要有两种控制结构,即ifelse结构和switchcase结构。 3.2.1ifelse结构 ifelse结构具有以下3种情况: (1) 若条件cond为真,则执行一条语句或一组语句 if(cond) 一条语句; 或者 if(cond) { 一组语句; } 在上述情况下,若cond为真,则执行“一条语句”或“一组语句”,“一组语句”可以为空、空语句、一条语句或多条语句,每条语句均以分号“;”结尾。这里的“空”表示没有语句; “空语句”表示只有分号“;”的语句。在这种情况下,当只有“一条语句”时,可以省略花括号。 这种情况如程序段39和程序段310所示。 视频讲解 程序段39ifelse结构的第一种情况(if结构中只有一条语句) 1int x = 3; 2if (x > 0) 3cout << "x is positive." << endl; 在程序段39中,第1行语句定义了变量x,并赋初值3。第2行语句使用关键字if判断x>0是否为真,如果为真,则执行第3行,输出“x is positive”; 如果为假,无操作。 程序段310ifelse结构的第一种情况(if结构中有一组语句) 1int x = 3; 2if (x > 0) 3{ 4cout << x; 5cout << " is positive." << endl; 6} 在程序段310中,第1行语句定义了变量x,并赋初值3。第2行语句使用关键字if判断x>0是否为真,如果为真,则执行第3~6行的语句组; 如果x>0为假,则无操作。 (2) 若条件cond为真,则无操作; 如果cond为假,则执行一条语句或一组语句 if(cond) ; else 一条语句; 或者 if(cond) { } else { 一组语句; } 上述情况的示例如程序段311和程序段312所示。 视频讲解 程序段311ifelse结构的第二种情况(单条语句) 1int x = -3; 2if (x > 0) 3; 4else 5cout << "x is negative." << endl; 在程序段311中,第1行定义了变量x,并赋初值-3。第2行判断x>0是否为真,如果为真,则执行第3行的空语句; 如果x>0为假,则执行第5行,输出“x is negative.”。 程序段312ifelse结构的第二种情况(多条语句) 1int x = -3; 2if (x > 0) 3{ 4} 5else 6{ 7cout << x; 8cout << " is negative." << endl; 9} 在程序段312中,第2行判断x>0是否为真,如果为真,则执行第3~4行的空语句; 如果x>0为假,则执行第5行,输出“x is negative.”。 (3) 若条件cond为真,则执行一组语句; 否则执行另一组语句 if(cond) { 一组语句 } else { 另一组语句; } 这里的语句组可以为空、空语句、一条语句或多条语句,这是ifelse结构的标准形式。 下面的程序段313说明了这种情况。 程序段313ifelse结构的第三种情况 1int x = -3; 2if (x > 0) 3{ 4cout << x; 5cout << " is positive." << endl; 6} 7else 8{ 9cout << x; 10cout << " is negative." << endl; 11} 在程序段313中,第2行判断x>0是否为真,若为真,则执行第3~6行的语句组; 若x>0为假,则执行第8~11行的语句组。 上述3种ifelse的情况均只有两种分支情况,ifelse结构可以实现多分支情况,称为ifelse结构的扩展形式。下面是一种四分支的ifelse扩展结构: if(条件1) { 语句组1; } else if(条件2) { 语句组2; } else if(条件3) { 语句组3; } else { 语句组4; } 在上述结构中,如果“条件k”(k=1,2,3)成立,则执行相应的“语句组k”,否则,当所有条件都不成立时,执行“语句组4”。上述结构中可以添加更多的else if语句,如 else if(条件n) { 语句组n; } 实现多分支的ifelse结构。在多分支结构中,若某一个分支判断为真(而得到执行),则不再判断(和执行)其余的分支了。 下面的程序段314说明了这种多分支ifelse结构的用法。 视频讲解 程序段314多分支ifelse结构的用法 1#include <iostream> 2using namespace std; 3 4int main() 5{ 6cout << "Input the income x(x>0):"; 7int x = 0; 8cin >> x; 9double tax; 10if (x > 10000) 11{ 12tax = x * 0.3; 13cout << "tax = " << tax << endl; 14} 15else if (x > 8000) 16{ 17tax = x * 0.25; 18cout << "tax = " << tax << endl; 19} 20else if (x > 6000) 21{ 22tax = x * 0.2; 23cout << "tax = " << tax << endl; 24} 25else if (x > 4000) 26{ 27tax = x * 0.15; 28cout << "tax = " << tax << endl; 29} 30else if (x > 2000) 31{ 32tax = x * 0.1; 33cout << "tax = " << tax << endl; 34} 35else 36{ 37tax = x * 0.05; 38cout << "tax = " << tax << endl; 39} 40} 在程序段314的main()函数中,第6行的语句“cout << "Input the income x(x>0):";”输出提示信息“Input the income x(x>0):”。 第7行的语句“int x=0;”定义整型变量x,并赋初值0。 第8行的语句“cin >> x;”输入变量x的值。 第9行的语句“double tax;”定义双精度浮点型的变量tax。 第10~39行为一个六分支的ifelse扩展结构。第10行判断x>1000是否为真,如果为真,则执行第11~14行的语句组,即“tax=x * 0.3; cout << "tax=" << tax << endl;”(x乘以0.3的积赋给tax,并输出tax的值)。当第10行判断结果为假时,第15行判断x>8000是否为真,若为真,则执行第16~19行的语句组。若第15行判断为假时,第20行判断x>6000是否为真,若为真,则执行第21~24行的语句组。若第20行判断为假时,第25行判断x>4000是否为真,若为真,则执行第26~29行的语句组。若第25行判断为假时,第30行判断x>2000是否为真,若为真,则执行第31~34行的语句组。若第30判断为假,则执行第36~39行的语句组。注意,当6个分支中某一个分支判断为真时,就不再去判断(和执行)剩余的分支了。 程序段314的执行结果如图39所示。 图39程序段314的执行结果 此外,ifelse结构可以嵌套使用。if部分可以嵌套新的ifelse结构,else部分也可以嵌套新的ifelse结构。下面给出了标准形式的二级嵌套的情况,图310为这一结构的流程图。 if(条件1) { 语句组1; if(条件2) { 语句组2; } else { 语句组3; } 语句组4; } else { 语句组5; if(条件3) { 语句组6; } else { 语句组7; } 语句组8; } 图310标准二级嵌套的流程图 程序段315给出了一种ifelse结构的嵌套用法实例。 视频讲解 程序段315ifelse结构的一种嵌套用法实例 1#include <iostream> 2#include <iomanip> 3using namespace std; 4 5int main() 6{ 7double x, p; 8cout << "Input x(0<x<1,x!=0.5):"; 9cin >> x; 10cout << "Input p(0<p<0.5):"; 11cin >> p; 12double y; 13if (x < 0.5) 14{ 15if (x < p) 16y = x / p; 17else 18y = (x - p) / (0.5 - p); 19} 20else 21{ 22x = 1 - x; 23if (x < p) 24y = x / p; 25else 26y = (x - p) / (0.5 - p); 27} 28cout << "y = " << setprecision(3) << y << endl; 29} 程序段315实现的功能与程序段37完全相同。第2行的预编译指令“#include <iomanip>”将头文件iomanip包括在程序中。 在main()函数中,第7行的语句“double x,p;”定义了双精度浮点型变量x和p。 第8行的语句“cout << "Input x(0<x<1,x!=0.5): ";”输出提示信息“Input x(0<x<1,x!=0.5):”,这里要求输入x,x的值为0~1,且不能等于0.5。 第9行的语句“cin >> x;”输入变量x的值。 第10行的语句“cout << "Input p(0<p<0.5): ";”输出提示信息“Input p(0<p<0.5):”,这里要求输入p,且要求p的值为0~0.5。 第11行的语句“cin >> p;”输入变量p的值。 第12行的语句“double y;”定义双精度浮点型变量y。 第13~27行为二级嵌套的ifelse结构。第13行的语句“if (x < 0.5)”判断x<0.5是否为真,如果为真,则执行第14~19行的语句组; 否则,执行第21~27行的语句组。 第15~18行为内层的ifelse结构,第15行的语句“if (x < p)”判断x<p是否为真,如果为真,则执行第16行的语句“y=x / p;”; 否则,执行第18行的语句“y=(x-p) / (0.5-p);”。 第23~26行的ifelse结构与第15~18行的ifelse结构完全相同。 第28行的语句“cout << "y=" << setprecision(3) << y << endl;” 输出字符串“y=”和y的值,其中,y的值保留3位小数。 程序段315的执行结果如图311所示。 图311程序段315的执行结果 3.2.2switchcase结构 switchcase结构是典型的多分支结构,其形式如下: switch(表达式) { case 值1: 语句组1; break; case 值2: 语句组2; break; case 值3: 语句组3; break; … case 值n: 语句组n; break; default: 语句组n+1; } 这里,“表达式”的值必须为整型或枚举类型,当“表达式”的值与某一个“值k”(k为1~n的值)时,将执行相应的“语句组k”,然后执行“break;”语句跳出switchcase结构; 如果“表达式”的值与所有的“值k” (k为1~n)均不同,则执行“default:”部分的“语句组n+1”。注意,如果某个case部分的“break;”被省略,那么当执行完该case部分的“语句组”后将不会跳转,而是顺序执行下面的case部分。每个“语句组”均可以为空、空语句、一条语句或多条语句。default部分可以省略。 下面的程序段316说明了switchcase结构的用法。 视频讲解 程序段316switchcase结构用法实例 1#include <iostream> 2#include <iomanip> 3using namespace std; 4 5int main() 6{ 7cout << "Input the income x(x>=0):"; 8double x; 9cin >> x; 10int y; 11y = int(x / 1000); 12double tax; 13switch (y) 14{ 15case 1: 16case 2: 17case 3: 18tax = x * 0.05; 19break; 20case 4: 21case 5: 22tax = x * 0.1; 23break; 24case 6: 25case 7: 26tax = x * 0.15; 27break; 28case 8: 29case 9: 30case 10: 31tax = x * 0.2; 32break; 33default: 34tax = x * 0.3; 35} 36cout << "tax = " << fixed << setprecision(2) << tax << endl; 37} 在程序段316的main()函数中,第7行的语句“cout << "Input the income x(x>=0): ";”输出提示信息“Input the income x(x>=0):”,即要求输入一个非负实数。 第8行的语句“double x;”定义双精度浮点型变量x。 第9行的语句“cin >> x;”输入变量x的值。 第10行的语句“int y;”定义整型变量y。 第11行的语句“y=int(x / 1000);”使用强制类型转换将x/1000的值转换为整数赋给变量y。 第12行的语句“double tax;”定义双精度浮点型变量tax。 第13~35行为switchcase结构,第13行判断y的值,当y为1、2或3时(第15~17行),执行第18行“tax=x * 0.05;”得到tax的值,然后执行第19行“break;”跳出switchcase结构; 当y为4或5时(第20~21行),执行第22~23行,然后跳出switchcase结构; 当y为6或7时(第24~25行),执行第26~27行,然后跳出switchcase结构; 当y为8、9或10时(第28~30行),执行第31~32行,然后跳出switchcase结构; 如果上述case情况都不成立,则执行第33~34行,然后,跳出switchcase结构。 第36行的语句“cout << "tax=" << fixed << setprecision(2) << tax << endl;”输出字符串"tax="和tax的值。这里的fixed为数据输出格式控制符,表示不使用科学计数法输出; setprecision(2)表示保留2位小数。 程序段316的输出结果如图312所示。 图312程序段316的执行结果 3.3循环控制 循环控制是高效率编写程序的主要手段。例如,实现1+2+…+100的简单运算,借助于循环控制将变成一件轻松的工作。循环控制主要有4种结构,即for结构、while结构、dowhile结构和foreach结构。其中,foreach结构是一种专门用来遍历数组(或序列)元素的循环结构,其余3种结构都是通用循环结构。 3.3.1for结构 for结构的基本形式为: for(设定循环变量初始值; 循环条件; 调整循环变量) { 循环执行的语句组; } 在上述for结构中,“设定循环变量初始值”部分用于给循环变量赋初始值,这部分在整个for循环中只被执行一次,所以,也可以把一些初始化工作放在此处; “循环条件”是每次执行“循环执行的语句组”前,都要判断的条件,如果“循环条件”为真,则执行这一次循环操作,如果“循环条件”为假,则跳出循环体; “调整循环变量”部分从第二次循环开始,每次都要执行一次“调整循环变量”。 由此可见,for循环的执行过程为: (1) 执行“设定循环变量初始值”,即给循环控制变量赋初值; (2) 判断“循环条件”是否为真,若为真,则执行第(3)步; 否则,跳出循环体,执行第(5)步; (3) 执行“循环执行的语句组”; (4) 执行“调整循环变量”,回到第(2)步; (5) 执行循环体后续的语句。 for结构有如下注意事项: (1) 有可能for循环中“循环执行的语句组”一次也得不到执行。当循环变量的初始值不满足“循环条件”时,将直接跳过循环体而执行后续的语句。 (2) for循环头部“设定循环变量初始值”“循环条件”“调整循环变量”均可以为空,或部分为空,但是分号不能少。“for(;;)”这种形式表示无限循环(俗称死循环),对于无限循环,需要在其中设定跳出循环的语句。 (3) 语句“break;”可以跳出它所在的循环。当有循环嵌套时,语句“break;”仅能跳出它所在的那一层循环,执行循环体后续的语句。 (4) 语句“continue;”用于结束本次循次,回到循环头部继续下一次循环,即“continue;”语句后续的循环体部分不再被执行,而是回到循环开始处执行下一次循环,对于for结构而言,回到“调整循环变量”处执行。 (5) 若for结构的循环体只有一条语句,可以省略花括号。 下面的程序段317给出了计算1+2+…+100的几种for结构。 视频讲解 程序段317for结构的用法实例 1#include <iostream> 2using namespace std; 3 4int main() 5{ 6int i, sum; 7sum = 0; 8for (i = 1; i <= 100; i++) 9{ 10sum += i; 11} 12cout << "1+2+...+100=" << sum << endl; 13 14for (sum = 0, i = 1; i <= 100; sum += i, i++) 15{ 16} 17cout << "1+2+...+100=" << sum << endl; 18 19i = 1; 20sum = 0; 21for (; i <= 100;) 22{ 23sum += i; 24i++; 25} 26cout << "1+2+...+100=" << sum << endl; 27 28i = 1; 29sum = 0; 30for (;;) 31{ 32sum += i; 33i++; 34if (i > 100) 35break; 36} 37cout << "1+2+...+100=" << sum << endl; 38} 程序段317列举了for结构的4种工作方式。在main函数中,第6行的语句“int i,sum;”定义了整型变量i和sum。 第7行的语句“sum=0;”将0赋给变量sum。 第8~11行为一个for结构,表示i从1按步长1累加到100,对于每个i,循环执行一次第10行的语句“sum +=i;”,即将sum与i的和赋给sum。 第12行的语句“cout << "1+2+...+100=" << sum << endl;”输出字符串“1+2+...+100=”和sum的值。 第14~16行为一个for结构,其中,循环体为空,在for结构的初始化部分执行“sum=0,i=1”,在for结构的调整循环变量部分执行“sum +=i,i++”,这样实现了i从1按步长1累加到100,对每个i,循环执行“sum +=i”的目的。 第17行输出字符串“1+2+...+100=”和sum的值。 第19行的语句“i=1;”将1赋给i。 第20行的语句“sum=0;”将0赋给sum。 第21~25行为一个for结构,其中,只有“终止循环条件”部分“i <=100”,表示当i小于等于100时,继续执行循环体。第19~20行为该for结构作了初始化工作,第23行的语句“sum +=i;”为每次循环执行的操作,第24行的语句“i++;”相当于该for结构的“调整循环变量部分”。 第26行输出字符串“1+2+...+100=”和sum的值。 第28行的语句“i=1;”将1赋给i。 第29行的语句“sum=0;”将0赋给sum。 第30~36行为一个for结构,该for结构为一个无限循环结构。每次循环执行第31~36行。其中第32行的语句“sum +=i;”将sum与i的和赋给sum; 第33行的语句“i++;”将i加1的值赋给i; 第34~35行为循环终止语句,当i大于100时,执行语句“break;”跳出该无限循环体。 第37行输出字符串"1+2+...+100="和sum的值。 程序段317的执行结果如图313所示。 图313程序段317的执行结果 for结构可以多级嵌套使用,并且for结构中可以嵌套使用其他的循环结构。 3.3.2while结构 类似于程序段317的第21~25行的这种for结构,当条件满足时,执行循环体; 当条件不满足时,跳出循环体,while结构的语法如下: while(条件) { 语句组; } 在上述while结构中,当“条件”为真时,循环执行“语句组”; 当“条件”为假时,跳出while结构,继续执行后续的语句。 while结构有时称为“当型”循环结构,即当“条件”为真时,循环执行“语句组”,且每次循环前都需要判断“条件”的真假。一旦发现“条件”为假,则跳出循环。 程序段318说明了while结构的用法。 视频讲解 程序段318while结构的用法实例 1#include <iostream> 2using namespace std; 3 4int main() 5{ 6int i = 1, sum = 0; 7while (i <= 100) 8{ 9sum += i; 10i++; 11} 12cout << "1+2+...+100=" << sum << endl; 13} 在程序段318的main()函数中,第6行的语句“int i=1,sum=0;”定义整型变量i和sum,并分别初始化为1和0。 第7~11行为while结构,第7行判定i<=100是否为真,若为真,则执行第8~11行的语句组; 若为假,跳转到第12行执行。第9行的语句“sum +=i;”将sum与i的和赋给sum,第10行的语句“i++;”将i自增1。这个while循环体执行了1+2+…+100的操作。 第12行的语句“cout << "1+2+...+100=" << sum << endl;” 输出字符串"1+2+...+100="和sum的值。 程序段318的执行结果如图314所示。 图314程序段318的执行结果 3.3.3dowhile结构 在while结构中,有可能循环体一次也得不到执行。在dowhile结构中,循环体先执行一次,再判断循环条件是否满足,如果满足,则继续执行循环体; 如果不满意,则跳出循环体。dowhile结构如下所示: do { 语句组; }while(条件); 在dowhile结构中,先执行“语句组”一次,然后再判断“条件”是否为真,如果为真,则循环执行“语句组”,直到“条件”为假后,跳出循环体。dowhile循环也称“直到型”循环,即循环执行“语句组”,直接“条件”为假。与while循环不同的是,dowhile循环至少会执行一次“语句组”。 程序段319说明了dowhile结构的用法。 视频讲解 程序段319dowhile结构的用法实例 1#include <iostream> 2using namespace std; 3 4int main() 5{ 6int i = 1, sum = 0; 7do 8{ 9sum += i; 10i++; 11} while (i <= 100); 12cout << "1+2+...+100=" << sum << endl; 13} 在程序段319的main()函数中,第6行的语句“int i=1,sum=0;”定义整型变量i和sum,并分别初始化为1和0。 第7~11行为一个dowhile结构,循环执行第9~10行直到“i<=100”为假。 第12行输出字符串"1+2+...+100="和sum的值。 程序段319的执行情况与程序段318相同,执行结果也如图314所示。 dowhile结构、while结构和for结构均可互相嵌套使用。 3.3.4foreach结构 foreach结构用于遍历数组这类数据序列的元素,典型结构如下所示: for(数组元素类型 变量名: 数组名) { 语句组; } 在上述foreach结构中,“变量名”对应的变量将依次取遍“数组名”表示的数组的各个元素(按索引号递增顺序),可以在“语句组”中使用“变量名”对应的变量。 程序段320介绍了foreach结构的用法。 视频讲解 程序段320foreach结构的用法实例 1#include <iostream> 2using namespace std; 3 4int main() 5{ 6int a[10] = { 1,2,3,4,5,6,7,8,9,10 }; 7int sum = 0; 8for (int e : a) 9{ 10sum += e * e; 11} 12cout << "Square sum of 1 to 10 is: " << sum << endl; 13} 在程序段320的main()函数中,第6行的语句“int a[10]={ 1,2,3,4,5,6,7,8,9,10 };”定义了一维整型数组a,具有10个元素,并用列表{1,2,3,4,5,6,7,8,9,10}中的元素依次初始化每个元素。 第7行的语句“int sum=0;”定义整型变量sum,并赋初值0。 第8~11行为一个foreach结构,对于数组a的每个元素(用e表示),循环执行第10行语句“sum +=e * e;”将每个元素的平方值加到sum上。这个foreach结构实现了1~10的平方和计算。 第12行的语句“cout << "Square sum of 1 to 10 is: " << sum << endl;”输出字符串"Square sum of 1 to 10 is: "和sum的值。 程序段320的执行结果如图315所示。 图315程序段320的执行结果 程序段320的第8行,常写作“for (auto e : a)”,这里的auto类型根据变量的值自动变换为相应的类型,例如,“auto x=3;”,auto相当于int。在“for (auto e : a)”中,auto将根据a的类型设定e的类型。 在使用foreach结构时,必须使用foreach结构专用的局部变量,例如,下面这种方式是错误的: int e; for(e : a) { 语句组; } 3.4指针 指针是一种特殊的数据类型,可以借助于指针直接访问物理存储器,指针的值为存储器的地址。指针是C++语言优于其他高级语言的主要体现。计算机操作系统或硬件设备的驱动程序,都需要直接访问硬件接口,因此这类软件(或程序)只能借助C++语言实现。 3.4.1常量、变量与指针 在C++语言中,数值、字符和字符串,例如,3、4.5e2、8.07、'd'、"Hello"等均为常量。而下述的语句定义的均为变量,例如: int x = 0; double y; char c; 这里的x、y和c为变量,是因为x、y和c的值可以改变。x、y和c称为变量名,本质上是这些变量在存储器中的地址。通过取地址符“&”可以得到这些变量的地址值,例如,“&x”将得到变量x的地址值,“&y”将得到变量y的地址值,“&c”将得到变量c的地址值。 可以像定义变量一样定义常量,例如: const int k = 100; const double pi = 3.14159; const char ch = 'Z'; 上述3条语句通过添加const限定符将k、pi和ch定义为常量,定义常量时必须给这些量赋初值,因为后续不能再改变这些量的值,即后续无法再对这些量进行赋值操作。 指针用于保存变量的地址,定义指针的方式为: 变量类型声明符 * 指针名=&变量; 例如: int a = 30; int* p = &a; int b; b = *p; 上述代码定义了整型变量a,并赋初始值30; 然后,定义了指向整型变量的指针p,指向变量a; 接着,定义了整型变量b; 最后,将*p赋给变量b。这里的“*”表示取指针指向的地址中的值,“*p”表示取p指向的地址中的值,这里,*p为30。于是,语句“*p=10;”将10赋给指针p指向的地址,这里赋值后,a的值也为10。 一般地,在定义指针时给指针赋初始地址。一个指针在被赋值前,称为悬浮指针,这类指针不能使用。例如: int* q; *q = 20; 上述语句中“int* q;”定义了指向整数类型的指针q,有时简称整型指针; “*q=20;”向q指向的地址赋值20。由于q没有指向任何地址,故q为悬浮指针,所以“*q=20;”是错误的。为了避免出现悬浮指针,一般在定义指针时给指针初始化。空指针用NULL表示。 下面为正确的指针用法: int* q=NULL; int d; q = &d; *q = 20; 在上述语句中,“int* q=NULL;”定义整型指针q为空指针; “int d;”定义整型变量d; “q=&d;”使指针q指向整型变量d,即指针q的值为变量d的地址; “*q=20;”将20赋给指针q指向的地址,赋值后变量d的值也为20。 程序段321说明了指针变量与变量地址的关系。 视频讲解 程序段321指针的定义与用法实例 1#include <iostream> 2using namespace std; 3 4int main() 5{ 6int a = 30; 7int* p = &a; 8cout << "a = " << a << endl; 9cout << "Address of a: " << &a << endl; 10cout << "Value of p:" << p << endl; 11cout << "*p = " << *p << endl; 12*p = 100; 13cout << "*p = " << *p << endl; 14cout << "a = " << a << endl; 15} 在程序段321的main()函数中,第6行的语句“int a=30;”定义整型变量a,并赋初值30。 第7行的语句“int* p=&a;”定义指向整型变量的指针p,p指向变量a的地址。 第8行的语句“cout << "a=" << a << endl;”输出字符串"a="和a的值。 第9行的语句“cout << "Address of a: " << &a << endl;”输出字符串"Address of a: "和变量a的地址。 第10行的语句“cout << "Value of p: " << p << endl;”输出字符串"Value of p: "和p的值。 第11行的语句“cout << "*p=" << *p << endl;”输出字符串"*p="和*p的值。 第12行的语句“*p=100;”将100赋给指针p指向的地址。 第13行的语句“cout << "*p=" << *p << endl;”输出字符串"*p="和*p的值。 第14行的语句“cout << "a=" << a << endl;”输出字符串"a="和a的值。 程序段321的执行结果如图316所示。 图316程序段321的执行结果 由图316可知,地址的长度为64位,即指针的存储长度为8个字节,可以在程序段321中添加一条语句“cout << sizeof(p) << endl;”显示指针p的存储长度。 3.4.2动态数组 在对指针进行赋值时,一般不使用固定的地址,而是将变量的地址赋给指针。然而,在微控制器编程时,将会出现大量使用地址直接赋给指针的用法,例如,“int * p=(int *)0x0E0000000000”,表示指针p的值为0x0E0000000000,即指针p指向地址0x0E0000000000。这种直接将地址赋给指针的方式在面向计算机的C++语言中一般不用。 有时只想定义一个指针,并为指针开辟一块存储空间,而不想定义变量。这时需要用到动态内存分配技术,相关的两个操作符为new和delete。new用于开辟存储空间,delete用于释放new开辟的空间,以避免空间浪费而造成内存的碎片化。 程序段322说明了new和delete的用法。 视频讲解 程序段322动态内存配置的用法实例 1#include <iostream> 2#include <iomanip> 3using namespace std; 4 5int main() 6{ 7int* p = new int; 8*p = 10; 9cout << "*p = " << *p << endl; 10delete p; 11 12int* q = new int(100); 13cout << "*q = " << *q << endl; 14delete q; 15 16int* w = new int[10]; 17for (int i = 0; i < 10; i++) 18*(w + i) = i + 1; 19for (int i = 0; i < 10; i++) 20cout << *(w + i) << " "; 21cout << endl; 22for (int i = 0; i < 10; i++) 23cout << w[i] << " "; 24cout << endl; 25delete[] w; 26 27int* v = new int[10]{ 11,12,13,14,15,16,17,18,19,20 }; 28for (int i = 0; i < 10; i++) 29cout << *(v + i) << " "; 30cout << endl; 31delete[] v; 32 33int(*u)[3] = new int[2][3]{ {9,7,5},{10,12,14} }; 34for (int i = 0; i < 2; i++) 35{ 36for (int j = 0; j < 3; j++) 37{ 38cout << left << setw(8) << u[i][j]; 39} 40cout << endl; 41} 42int* s = u[0]; 43for (int i = 0; i < 2; i++) 44{ 45for (int j = 0; j < 3; j++) 46{ 47cout << left << setw(8) << *(s + 3 * i + j); 48} 49cout << endl; 50} 51delete[] u; 52} 在程序段322的main()函数中,第7行的语句“int* p=new int;”使用new开辟一个整型存储空间(即4个字节的存储空间),并将该空间的地址赋给新定义的指针p。这里的“new int”为动态开辟一个整型存储空间,与第10行的语句“delete p;”相对应,“delete p;”用于释放new开辟的空间。 第8行的语句“*p=10;”将10赋给指针p指向的空间。 第9行的语句“cout << "*p=" << *p << endl;”输出字符串"*p="和*p的值。 第12行的语句“int* q=new int(100);”使用new开辟一个整型存储空间(即4个字节的存储空间),并赋初值100,然后,将新定义的指针q指向该存储空间。这一行与第14行对应,第14行的语句“delete q;”用于释放该存储空间。 第13行的语句“cout << "*q=" << *q << endl;”输出字符串"*q="和*q的值。 上述为使用new开辟一个存储空间的方法。下面为使用new开辟一块连续存储空间的方法。 第16的语句“int* w=new int[10];”使用new开辟一个长度为10个整数的连续的存储空间,使新定义的指针w指向该空间的首地址。这里的w相当于指向一个长度为10的整型数组,*w或*(w+0)表示第1个元素,*(w+1)表示第2个元,以此类推,*(w+9)表示第10个元素。事实上,指针与一维数组近似等价,也可以用w[0]表示第1个元素,w[1]表示第2个元素,以此类推,w[9]表示第10个元素。 第16行与第25行对应,第25行的语句“delete[] w;”释放new开辟的空间。 第17行和第18行为一个for结构,向w指向的各个元素赋值,这里使用*(w+i)表示第i+1个元素。 第19行和第20行为一个for结构,将w指向的各个元素输出,这里使用*(w+i)表示第i+1个元素。 第21行的语句“cout << endl;”输出一个回车换行符。 第22行和第23行为一个for结构,将w指向的各个元素输出,这里使用w[i]表示第i+1个元素。 第24行的语句“cout << endl;”输出一个回车换行符。 第27行的语句“int* v=new int[10]{ 11,12,13,14,15,16,17,18,19,20 };”使用new开辟一个可存储10个整数的连续的存储空间,并使用列表“{11,12,13,14,15,16,17,18,19,20}”对其赋初值,将新定义的指针v指向该存储空间的首地址。 第27行与第31行对应,第31行的语句“delete[] v;”释放v指向的连续空间。 第28行和第29行为一个for结构,输出指针v指向的空间中的所有元素,这里使用*(v+i)表示第i+1个元素。 第30行的语句“cout << endl;”输出一个回车换行。 第33行的语句“int(*u)[3]=new int[2][3]{ {9,7,5},{10,12,14} };”使用new开辟一个二维的整型存储空间,即2行3列的存储空间,以整型数据为存储单位,并用列表“{ {9,7,5},{10,12,14} }”对该存储空间初始化。二维空间与二维数组类似,将其每行视为一个一维数组,对于2行3列的存储空间可视为两个一维数组,每个一维数组有3个元素,因此,需要使用“int(*u)[3]”这种形式定义指向这个二维存储空间的指针。不建议使用new开辟二维及二维以上的空间,参考第42~50行的解释。这里访问二维空间的元素的方法为: 使用u[i][j]或*(*(u+i)+j)表示第i+1行和第j+1列的元素,即指向二维存储空间的指针与二维数组是近似等价的。 注意: 定义“int(*u)[3]”和定义“int * u[3]”完全不同; 后者“int * u[3]”表示数组u的每个元素为“int *”类型,因此,可以称之为指针的数组。前者“int(*u)[3]”是一种指向二维数组的指针,且数组的第二维必须有3个元素。 第34~41行一个二级嵌套的for结构,输出指针u指向的二维空间的各个元素,先输出第一行元素,再输出一个回车换行符,接着,输出第二行元素。 第42行的语句“int* s=u[0];”定义一个指针s,指向u指针指向的二维空间的首地址。这里的“u[0]”是u指针指向的二维空间的第一行(即一维行向量)的首地址。由于二维空间中各个元素的地址是顺序排列的,可以使用指针按一维向量的形式访问,因此,不建议定义指向二维或二维以上的指针。 第43~50为二级嵌套的for结构,借助于指针s依次输出u指针指向的二维空间的各个元素,这里第47行的“*(s+3 * i+j)”表示第i+1行和第j+1列的元素。 第51行的语句“delete[] u;”释放u指针指向的空间。 程序段322的执行结果如图317所示。 图317程序段322的执行结果 由程序段322可知,指针支持加上(或减去)一个整数的运算,例如,设: int* v = new int[10]{ 11,12,13,14,15,16,17,18,19,20 }; 则v指向new开辟的空间的首地址,即指针“11”所在的地址,不妨设为0x2000 0000 0000; 则v+1指向“12”所在的地址,即0x2000 0000 0004,v+1是v所指的地址加上4个字节; v+2指向“13”所在的地址,即0x2000 0000 0008; v+3指向“14”所在的地址,即0x2000 0000 000C; 以此类推,v+9指向“20”所在的地址,即0x2000 0000 0000+9*4。指针的步进1相当于地址步进4个字节,这是因为指针指向的变量类型(int)占4个字节。 3.4.3数组与指针 一维数组和指针近似等价,例如,定义如下一维数组 inta[10]={ 3,5,7,9,11,13,15,17,19,21 }; 可以使用类似指针的方法访问数组元素,例如,*(a+3)将访问数组的第4个元素,即元素“9”。这说明,对于一维数组而言,数组名就是数组的首地址,也可视为指向数组首地址的指针。因此对于上述的一维数组“int a[10]={ 3,5,7,9,11,13,15,17,19,21 };”,定义指向该数组的指针时,使用如下语句: int* p=a; 或 int* p =&a[0]; 上述指针p和a的唯一区别在于,p是变量,可以指向新的地址,或者说可以赋给它新的地址,而a是数组名,a是固定的地址,不能改变a的值,可以视a为一个地址常量。 对于二维数组或二维以上的高维数组而言,和一维数组的情况稍有不同。这里以三维数组为例进行讨论。设有如下三维数组: int b[2][3][4]={ 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24 }; 这里数组名b是数组b的首地址,但是数组名b本身是一种指向三维数组的指针,而不是一般意义上的指针,下面的w指针指向了数组b: int(*q)[3][4]=b; 这里三维数组b可以视为2个二维数组,而每个二维数组又可以按行划分视为3个一维数组,这样,“b、b[0]、b[0][0]、&b、&b[0]、&b[0][0]、&b[0][0][0]”都相同,即都指向同一个地址。但是,只有“&b[0][0][0]”才可以赋给普通意义上的指针; 其他的地址均为指向数组的指针形式。 由于数组(包括高维数组)的元素是按顺序存储的,因此,习惯上使用普通指针访问高维数组元素,例如定义: int* w=&b[0][0][0]; 可以使用w指针访问三维数组b的各个元素,其中,*(w+i * 12+j * 4+k)即为b[i][j][k]。 下面的程序段323说明了数组与指针的关系。 视频讲解 程序段323借助指针访问数组元素的实例 1#include <iostream> 2#include <iomanip> 3using namespace std; 4 5int main() 6{ 7int a[10] = { 3,5,7,9,11,13,15,17,19,21 }; 8for (int i = 0; i < 10; i++) 9cout << left << setw(5) << *(a + i); 10cout << endl; 11int* p = a; 12for (int i = 0; i < 10; i++) 13cout << left << setw(5) << *(p + i); 14cout << endl; 15 16int b[2][3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12,13, 1714,15,16,17,18,19,20,21,22,23,24 }; 18int(*q)[3][4] = b; 19cout << b << " " << b[0] << " " << b[0][0] << endl; 20cout << &b << " " << &b[0] << " " << &b[0][0] << endl; 21cout << &b[0][0][0] << endl; 22for (int i = 0; i < 2; i++) 23{ 24for (int j = 0; j < 3; j++) 25{ 26for (int k = 0; k < 4; k++) 27{ 28cout << left << setw(6) << *(*(*(q + i) + j) + k); 29} 30cout << endl; 31} 32cout << endl; 33} 34int* w = &b[0][0][0]; 35for (int i = 0; i < 2; i++) 36{ 37for (int j = 0; j < 3; j++) 38{ 39for (int k = 0; k < 4; k++) 40{ 41cout << left << setw(6) << *(w + i * 12 + j * 4 + k); 42} 43cout << endl; 44} 45cout << endl; 46} 47} 在程序段323的main()函数中,第7行的语句“int a[10]={ 3,5,7,9,11,13,15,17,19,21 };”定义一维整型数组a,具有10个元素,并使用列表“{ 3,5,7,9,11,13,15,17,19,21 }”初始化数组a。 第8行和第9行为一个for结构,用“*(a+i)”这种方式输出数组a的各个元素。 第10行的语句“cout << endl;”输出一个回车换行。 第11行的语句“int* p=a;”定义整型指针p,指向数组a的首地址。 第12行和第13行为一个for结构,用“p[i]”这种方式输出数组a的各个元素。 第14行的语句“cout << endl;”输出一个回车换行。 第16行和第17行的语句“int b[2][3][4]={ 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24 };”定义一个三维数组b并作了初始化,这种初始化方式中,根据三维数组b的元素存储顺序依次实始化各个元素。 第18行的语句“int(*q)[3][4]=b;”定义一个指向三维数组的指针q,并初始化为b,即q指向三维数组b的首地址。 第19行的语句“cout << b << "" << b[0] << "" << b[0][0] << endl;”输出b、b[0]和b[0][0]的内容,这3个值相同,都是三维数组b的首地址。 第20行的语句“cout << &b << "" << &b[0] << "" << &b[0][0] << endl;”输出&b、&b[0]、&b[0][0]的内容,这3个值相同,都是三维数组b的首地址。 第21行的语句“cout << &b[0][0][0] << endl;”输出b[0][0][0]的地址,该地址为三维数组b的首地址。 第22~33行为一个三级嵌套的for结构,使用指向三维数组的指针“*(*(*(q+i)+j)+k)”的形式输出三维数组b的各个元素,这里的“*(*(*(q+i)+j)+k)”就是b[i][j][k]。 第34行的语句“int* w=&b[0][0][0];”定义整型指针w,该指针指向三维数组b的首元素的地址。 第35~46行为一个三级嵌套的for结构,这里使用了普通指针“*(w+i * 12+j * 4+k)”的形式输出三维数组b的各个元素,这里的“*(w+i * 12+j * 4+k)”就是b[i][j][k]。 推荐使用第34~46行这种方式访问高维数组。 程序段323的执行结果如图318所示。 图318程序段323的执行结果 3.5引用 一个变量的引用定义为该变量的别名,引用不占用新的存储空间。引用仅针对单个变量。定义引用时必须初始化,因为引用是作为变量的别名存在的,必须有它的载体变量。引用的两个重要作用在于: (1) 作为函数的返回值; (2) 作为函数的形参变量。 由于函数在定义时不会为形参分配空间(在函数调用时为形参指定实参),故引用作为函数形参时无须初始化。这部分内容将在第4章详细介绍。 程序段324说明了引用的用法。 视频讲解 程序段324引用的用法实例 1#include <iostream> 2using namespace std; 3 4int main() 5{ 6int a = 5; 7int& r1 = a; 8cout << "a = " << a << endl; 9cout << "r1 = " << r1 << endl; 10cout << "Addr of a: " << &a << endl; 11cout << "Addr of r1: " << &r1 << endl; 12 13int& r2 = r1; 14cout << "r2 = " << r2 << endl; 15cout << "Addr of r2: " << &r2 << endl; 16 17int b[5] = { 3,5,7,9,11 }; 18int& r3 = b[4]; 19cout << "r3 = " << r3 << endl; 20 21int* p = b; 22int& r4 = p[3]; 23cout << "r4 = " << r4 << endl; 24} 在程序段324的main()函数中,第6行的语句“int a=5;”定义整型变量a,并赋初值5。 第7行的语句“int& r1=a;”定义引用r1,r1作为变量a的引用。定义引用用符号“&”表示。 第8行的语句“cout << "a=" << a << endl;”输出字符串"a="和a的值。 第9行的语句“cout << "r1=" << r1 << endl;”输出字符串"r1="和r1的值。r1为a的引用,r1的值等于a的值。 第10行的语句“cout << "Addr of a: " << &a << endl;”输出字符串"Addr of a: "和a的地址。 第11行的语句“cout << "Addr of r1: " << &r1 << endl;”输出字符串"Addr of r1: "和r1的地址,r1的地址和a的地址相同。 第13行的语句“int& r2=r1;”定义引用r2作为r1的引用,即可以定义引用的引用。 第14行的语句“cout << "r2=" << r2 << endl;” 输出字符串"r2="和r2的值。r2为r1的引用,r1为a的引用,所以,r2的值等于a的值。 第15行的语句“cout << "Addr of r2: " << &r2 << endl;”输出字符串"Addr of r2: "和r2的地址,r2的地址和r1与a的地址相同。 第17行的语句“int b[5]={ 3,5,7,9,11 };”定义一维整型数组b,并对b进行初始化。 第18行的语句“int& r3=b[4];”定义引用r3作为b[4]的引用,即可以定义对数组元素的引用。 第19行的语句“cout << "r3=" << r3 << endl;”输出字符串"r3="和r3的值。 第21行的语句“int* p=b;”定义指针p,指向数组b的首地址。 第22行的语句“int& r4=p[3];”定义引用r4作为p[3]的引用。 第23行的语句“cout << "r4=" << r4 << endl;”输出字符串"r4="和r4的值。 程序段324的执行结果如图319所示。 图319程序段324的执行结果 3.6排序实例 数组中的数据排序是常见的一种操作,常用的排序方法有冒泡法和选择法。 不妨设有整型数组a[n],数组名为a,具有n个元素{a[0], a[1], …, a[n-1]}。下面依次介绍冒泡法排序和选择法排序。 1. 冒泡法排序 冒泡法排序(按升序排列)的工作原理为: (1) 令循环变量i从0按步长1递增到n-2,依次比较a[i]与a[i+1],如果a[i]大于a[i+1],则将a[i]与a[i+1]对换。这一步将数组a中最大的元素保存在a[n-1]中。 (2) 令循环变量i从0按步长1递增到n-3,依次比较a[i]与a[i+1],如果a[i]大于a[i+1],则将a[i]与a[i+1]对换。这一步将数组a中次大的元素保存在a[n-2]中。 (3) 令j从n-3按步长1递减到1,令循环变量i从0按步长1递增到j-1,按照上述方法,依次将a[0]至a[j]中最大的元素保存在a[j]中。 可见,冒泡法排序的每一步均找出数组中没有排序的元素中的值最大的元素,并将所有比该元素小的元素“冒泡”到该元素的左边。 2. 选择法排序 选择法排序(按升序排列)的工作原理为: (1) 令变量i为0,令变量k为0,令循环变量j从1按步长1递增到n-1,依次比较a[k]与a[j]的值,如果a[k]大于a[j],则将j赋给k。循环结束后,如果k不为0,则将a[k]与a[0]对换。这一步将数组a中最小的值保存在a[0]中。 (2) 令变量i为1,令变量k为1,令循环变量j从2按步长1递增到n-1,依次比较a[k]与a[j]的值,如果a[k]大于a[j],则将j赋给k。循环结束后,如果k不为1,则将a[k]与a[1]对换。这一步将数组a中次小的值保存在a[1]中。 (3) 令变量i从2按步长1递增到n-2,令k=i,令循环变量j从i+1按步长1递增到n-1,依次比较a[k]和a[j]的值,如果a[k]大于a[j],则将j赋给k。每次循环结束后,如果k不等于i,则将a[k]与a[i]对换。 可见,选择法排序的每一步都将选出数组中没有排序的元素中的最小元素的下标,将所有比它大的元素保存在该元素的右侧。 程序段325给出了冒泡法和选择法排序的实现代码。 视频讲解 程序段325冒泡法和选择法排序实例 1#include <iostream> 2using namespace std; 3 4int main() 5{ 6int a[10] = { 45,23,90,18,56,71,15,39,61,80 }; 7cout << "Original a:" << endl; 8for (int i = 0; i < 10; i++) 9cout << a[i] << " "; 10cout << endl; 11 12int n = 10; 13for (int j = n - 1; j > 0; j--) 14{ 15for (int i = 0; i <= j - 1; i++) 16{ 17if (a[i] > a[i + 1]) 18{ 19int t = a[i]; 20a[i] = a[i + 1]; 21a[i + 1] = t; 22} 23} 24} 25cout << "Sorted a:" << endl; 26for (int i = 0; i < 10; i++) 27cout << a[i] << " "; 28cout << endl<<endl; 29 30int b[10] = { 45,23,90,18,56,71,15,39,61,80 }; 31cout << "Original b:" << endl; 32for (int i = 0; i < 10; i++) 33cout << b[i] << " "; 34cout << endl; 35 36n = 10; 37int k; 38for (int i = 0; i <= n - 2; i++) 39{ 40k = i; 41for (int j = i + 1; j <= n - 1; j++) 42{ 43if (b[k] > b[j]) 44{ 45k = j; 46} 47} 48if (k != i) 49{ 50int t = b[i]; 51b[i] = b[k]; 52b[k] = t; 53} 54} 55cout << "Sorted b:" << endl; 56for (int i = 0; i < 10; i++) 57cout << b[i] << " "; 58cout << endl << endl; 59} 在程序段325的main()函数中,第6行的语句“int a[10] = { 45,23,90,18,56,71,15,39,61,80 };”定义整型一维数组a,并初始化为列表“{45,23,90,18,56,71,15,39,61,80}”。 第7行的语句“cout << "Original a: " << endl;”输出提示信息“Original a:”。 第8行和第9行为一个for结构,输出数组a的全部元素。 第12行的语句“int n = 10;”定义整型变量n,并赋初值10。这里的n为数组a的长度。 第13~24行为一个嵌套的for结构,实现了冒泡法排序,将数组a的元素按升序排列。 第25行的语句“cout << "Sorted a:" << endl;”输出提示信息“Sorted a:”。 第26行和第27行为一个for结构,输出升序排列好的数组a的全部元素。 第30行的语句定义数组b,这里数组b和原始的数组a相同。 第32行和第33行为一个for结构,输出数组b的全部元素。 第37行的语句“int k;”定义整型变量k。 第38~54行为一个嵌套的for结构,实现了选择排序,将数组b中的元素按升序排列。 第55行的语句“cout << "Sorted b: " << endl;”输出提示信息“Sorted b:”。 第56行和第57行为一个for结构,输出升序排列后的数组b的全部元素。 程序325的执行结果如图320所示。 图320程序段325的执行结果 3.7本章小结 本章详细介绍了C++语言常用的9类运算符,即算术运算符、关系运算符、逻辑运算符、位运算符、自增自减运算符、赋值运算符、sizeof运算符、条件运算符和逗号运算符。C++语言的运算符大多需要连接两个操作数,这类运算符称为双目运算符; 少数运算符只需要一个操作数,称为单目运算符; 只有条件运算符是唯一的三目运算符。然后,本章讨论了C++语言程序的控制结构。只有3种程序控制方式,即顺序方式、分支(或称选择)方式和循环方式,这里重点阐述了ifelse和switchcase两种分支结构以及for、while、dowhile、foreach四种循环结构。接着,讨论了指针的用法和动态创建数组的方法。最后,分析了引用及其用法。 习题 1. 编写程序,输入两个整数及一个双目算术运算符,计算这两个整数经该运算符处理后的结果。 2. 编写程序,输入两个实数,输出这两个实数及其大小关系。 3. 编写程序证明德·摩根定律,即 !(P&&Q)=!P‖!Q; !(P‖Q)=!P&&!Q。 4. 使用位运算符实现16位无符号整数0x389A的循环右移5位和循环左移12位。 5. 使用位运算符实现16位无符号整数0xA3D8的第3、5、7、9位置位,第4、8、12、16清零,且同时第1、6、10位由原来的0变为1或由原来的1变为0,输出变换后的整数。 6. 使用ifelse结构编写程序,输入x和p的值,输出长度为100的状态序列,使用的公式为: Fx=xp,x∈0,p x-p0.5-p,x∈p,0.5 F1-x,p,x∈0.5,1 7. 已知Fibonacci序列满足下式: F0=0 F1=1 Fn=Fn-1+Fn-2,n≥2 计算n=10、20、30时的Fibonacci数(使用long整型)。 8. 借助于for结构生成高为5行的杨辉三角形,即 1 11 121 1331 14641 15101051 9. 编写程序,分别使用for结构、while结构和dowhile结构生成九九乘法口诀表。 10. 设有二维数组a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12},使用普通指针和指向二维数组的指针实现对数组a元素的输出,要求输出为3行4列的矩阵形式。