习题 1.1
查看所用的编译器文档,了解它所用的文件命名规范。编译并运行本节的main 程序。 【解答】
一般而言,C++编译器要求待编译的程序保存在文件中。C++程序中一般涉及两 类文件:头文件和源文件。大多数系统中,文件的名字由文件名和文件后缀(又 称扩展名)组成。文件后缀通常表明文件的类型,如头文件的后缀可以是.h 或.hpp 等;源文件的后缀可以是.cc 或.cpp 等,具体的后缀与使用的编译器有
关。通常可以通过编译器所提供的联机帮助文档了解其文件命名规范。 习题1.2
修改程序使其返回-1。返回值-1 通常作为程序运行失败的指示器。然而,系统 不同,如何(甚至是否)报告main 函数运行失败也不同。重新编译并再次运行 程序,看看你的系统如何处理main 函数的运行失败指示器。 【解答】
笔者所使用的Windows操作系统并不报告main 函数的运行失败,因此,程序返 回-1 或返回0 在运行效果上没有什么区别。但是,如果在DOS 命令提示符方式 下运行程序,然后再键入echo %ERRORLEVEL%命令,则系统会显示返回值-1。 习题1.3
编一个程序,在标准输出上打印“Hello, World”。
#include
{ system(\ cout<<\ return 0; }
习题1.4
我们的程序利用内置的加法操作符“+”来产生两个数的和。编写程序,使用乘 法操作符“*”产生两个数的积。
#include
{ system(\ cout<<\ int v1,v2; cin>>v1>>v2; cout<<\ return 0; }
习题1.5
我们的程序使用了一条较长的输出语句。重写程序,使用单独的语句打印每一
个操作数。
#include
system(\
cout << \int v1, v2;
cin >> v1 >> v2;
cout << \cout << v1;
cout << \cout << v2; cout << \cout << v1 + v2 ; cout << endl; return 0; }习题1.6
解释下面的程序段:
std::cout << \<< \<< \<< std::endl;
这段代码合法吗?如果合法,为什么?如果不合法,又为什么? 【解答】
这段代码不合法。
注意,第1、2、4 行的末尾有分号,表示这段代码包含三条语句,即第1、2 行 各为一个语句,第3、4 行构成一个语句。“<<”为二元操作符,在第2、3 两 条语句中,第一个“<<”缺少左操作数,因此不合法。 在第2、3 行的开头加上“std::cout”,即可更正。 习题1.7
编译有不正确嵌套注释的程序。 【解答】
由注释对嵌套导致的编译器错误信息通常令人迷惑。例如,在笔者所用的编译 器中编译1.3 节中给出的带有不正确嵌套注释的程序: #include
* comment pairs /* */ cannot nest.
* \* as is the rest of the program */
int main() {
return 0; }
编译器会给出如下错误信息:
error C2143: syntax error : missing ';' before '<'
error C2501: 'include' : missing storage-class or type specifiers warning C4138: '*/' found outside of comment (第6 行)
error C2143: syntax error : missing ';' before '{' (第8 行)
error C2447: '{' : missing function header (old-style formal list?)(第 8 行) 习题1.8
指出下列输出语句哪些(如果有)是合法的。 std::cout << \std::cout << \
std::cout << /* \
预测结果,然后编译包含上述三条语句的程序,检查你的答案。纠正所遇到的 错误。 【解答】
第一条和第二条语句合法。
第三条语句中<<操作符之后至第二个双引号之前的部分被注释掉了,导致<<操 作符的右操作数不是一个完整的字符串,所以不合法。在分号之前加上一个双 引号即可更正。 习题1.9
下列循环做什么? sum 的最终值是多少? int sum = 0;
for (int i = -100; i <= 100; ++i) sum += i; 【解答】
该循环求-100~100 之间所有整数的和(包括-100 和100)。 sum 的最终值是0。 习题1.10
用for 循环编程,求从50~100 的所有自然数的和。然后用while 循环重写该程 序。
【解答】
用for 循环编写的程序如下: #include
int sum = 0; system(\
for (int i = 50; i <= 100; ++i) sum += i;
std::cout << \<< sum << std::endl;
return 0; }
用while 循环编写的程序如下: #include
int sum=0, i=50; system(\ while(i<=100) {
sum+=i; i++; }
cout<<\ return 0; }
习题1.11
用while 循环编程,输出10~0 递减的自然数。然后用for 循环重写该程序。 【解答】
用while 循环编写的程序如下: #include
system(\ while(i>=0) {
cout<
cout< 用for 循环编写的程序如下: #include { system(\ for (int i=10;i>=0;i--) { cout< cout< 习题1.12 对比前面两个习题中所写的循环。两种形式各有何优缺点? 【解答】 在for 循环中,循环控制变量的初始化和修改都放在语句头部分,形式较简洁, 且特别适用于循环次数已知的情况。在while 循环中,循环控制变量的初始化 一般放在while 语句之前,循环控制变量的修改一般放在循环体中,形式上不 如for 语句简洁,但它比较适用于循环次数不易预知的情况(用某一条件控制 循环)。两种形式各有优点,但它们在功能上是等价的,可以相互转换。 习题1.13 编译器不同,理解其诊断内容的难易程度也不同。编写一些程序,包含本小节 “再谈编译”部分讨论的那些常见错误。研究编译器产生的信息,这样你在编 译更复杂的程序遇到这些信息时不会陌生。 【解答】 对于程序中出现的错误,编译器通常会给出简略的提示信息,包括错误出现的 文件及代码行、错误代码、错误性质的描述。如果要获得关于该错误的详细信 息,一般可以根据编译器给出的错误代码在其联机帮助文档中查找。 习题1.14 如果输入值相等,本节展示的程序将产生什么问题? 【解答】 sum 的值即为输入值。因为输入的v1 和v2 值相等(假设为x),所以lower 和 upper 相等,均为x。for 循环中的循环变量val 初始化为lower,从而val<=upper 为真,循环体执行一次,sum 的值为val(即输入值x);然后val 加1,val 的 值就大于upper,循环执行结束。 习题1.15 用两个相等的值作为输入编译并运行本节中的程序。将实际输出与你在习题 1.14 中所做的预测相比较,解释实际结果和你预计的结果间的不相符之处。 【解答】 运行1.4.3 节中给出的程序,输入两个相等的值(例如3,3),则程序输出为: Sum of 3 to 3 inclusive is 3 与习题1.14 中给出的预测一致。 习题1.16 编写程序,输出用户输入的两个数中的较大者。 #include system(\ cout<<\ int v1,v2; cin>>v1>>v2; if(v1>=v2) cout<<\ else cout<<\ return 0; } 习题1.17 编写程序,要求用户输入一组数。输出信息说明其中有多少个负数。 #include int amount = 0, value; system(\ // 读入数据直到遇见文件结束符,计算所读入的负数的个数 while (cin >> value) if (value <= 0) ++amount; cout << \ << amount << std::endl; return 0; } 习题1.18 编写程序,提示用户输入两个数并将这两个数范围内的每个数写到标准输出。 #include system(\ cout<<\ int v1,v2; cin>>v1>>v2; int lower,upper; lower=v1; upper=v2; if(v1>=v2) { upper=v1; lower=v2; } cout<<\< for(;lower<=upper;lower++) cout< cout< return 0; } 习题1.19 如果上题给定数1000 和2000,程序将产生什么结果?修改程序,使每一行输出 不超过10 个数。 【解答】 所有数的输出连在一起,不便于阅读。 程序修改如下: #include system(\ cout<<\ int v1,v2; cin>>v1>>v2; int lower,upper; lower=v1; upper=v2; if(v1>=v2) { upper=v1; lower=v2; } cout<<\< for(int count=1;lower<=upper;lower++,count++) { cout< } cout< return 0; } 习题1.20 编写程序,求用户指定范围内的数的和,省略设置上界和下界的if 测试。假定 输入数是7 和3,按照这个顺序,预测程序运行结果。然后按照给定的数是7 和 3 运行程序,看结果是否与你预测的相符。如果不相符,反复研究关于for 和 while 循环的讨论直到弄清楚其中的原因。 #include system(\ cout<<\ int v1,v2,sum=0; cin>>v1>>v2; for(int val=v1;val<=v2;val++) { sum+=val; } cout<<\<< \endl; return 0; } 如果输入数据为7 和3,则v1 值为7,v2 值为3。for 语句头中将val 的初 始值设为7,第一次测试表达式val <= v2 时,该表达式的值为false,for 语 句的循环体一次也不执行,所以求和结果sum 为0。 习题1.21 本书配套网站(http://www.awprofessional.com/cpp_primer)的第1 章的代 码目录下有Sales_ item.h 源文件。复制该文件到你的工作目录。编写程序, 循环遍历一组书的销售交易,读入每笔交易并将交易写至标准输出。 #include #include \#include %using namespace std; int main() { Sales_item book; system(\ cout << \ while (cin >> book) { // 输出ISBN,售出书的本数,总收入,平均价格 cout << \average price are: \ cout << book < return 0; } 习题1.22 编写程序,读入两个具有相同ISBN 的Sales_item 对象并产生它们的和。 #include #include \#include %using namespace std; int main() { Sales_item trans1, trans2; // 读入交易 system(\ cout << \ cin >> trans1 >> trans2; if (trans1.same_isbn(trans2)) cout << \ << \ << \ << endl << trans1 + trans2; else cout << \ return 0; } 习题1.23 编写程序,读入几个具有相同ISBN 的交易,输出所有读入交易的和。 #include #include \#include %using namespace std; int main() { Sales_item total, trans; system(\ cout << \ if (cin >> total) { while (cin >> trans) if (total.same_isbn(trans)) // ISBN 相同 total = total + trans; } else { // ISBN 不同 cout << \ return -1; } // 输出交易之和 cout << \ << \ << \ < else { cout << \ return -1; } return 0; } 习题1.24 编写程序,读入几笔不同的交易。对于每笔新读入的交易,要确定它的ISBN 是 否和以前的交易的ISBN 一样,并且记下每一个ISBN 的交易的总数。通过给定 多笔不同的交易来测试程序。这些交易必须代表多个不同的ISBN,但是每个ISBN 的记录应分在同一组。 #include #include \#include %using namespace std; int main() { Sales_item trans1, trans2; int amount; system(\ cout << \ cin >> trans1; amount=1; while (cin >> trans2) if (trans1.same_isbn(trans2)) ++amount; else { cout << \ << amount < cout << \ << amount < /*习题1.25 使用源自本书配套网站的Sales_item.h 头文件,编译并执行1.6 节给出的书店 程序。*/ 习题1.26 在书店程序中,我们使用了加法操作符而不是复合赋值操作符将trans 加到 total 中,为什么我们不使用复合赋值操作符? 【解答】 因为在1.5.1 节中提及的Sales_item 对象上的操作中只包含了+和=,没有包含 +=操作。(但事实上,使用Sales_item.h 文件,已经可以用+=操作符取代=和+ 操作符的复合使用。) 习题2.1 int、long 和short 类型之间有什么差别? 【解答】 它们的最小存储空间不同,分别为16 位、32 位和16 位。一般而言,short 类 型为半个机器字(word)长,int 类型为一个机器字长,而long 类型为一个或 两个机器字长(在32 位机器中,int 类型和long 类型的字长通常是相同的)。 因此,它们的表示范围不同。 习题2.2 unsigned 和signed 类型有什么差别? 【解答】 前者为无符号类型,只能表示大于或等于0 的数。后者为带符号类型,可以表 示正数、负数和0。 习题2.3 如果在某机器上short 类型占16 位,那么可以赋给short 类型的最大数是什么? unsigned short 类型的最大数又是什么? 【解答】 若在某机器上short 类型占16 位,那么可以赋给short 类型的最大数是215-1, 即32767;而unsigned short 类型的最大数为216-1,即65535。 习题2.4 当给16 位的unsigned short 对象赋值100000 时,赋的值是什么? 【解答】 34464。 100000 超过了16 位的unsigned short 类型的表示范围,编译器对其二进制表 示截取低16 位,相当于对65536 求余(求模,%),得34464。 习题2.5 float 类型和double 类型有什么差别? 【解答】 二者的存储位数不同(一般而言,float 类型为32 个二进制位,double 类型为 64 个二进制位),因而取值范围不同,精度也不同(float 类型只能保证6 位 有效数字,而double 类型至少能保证10 位有效数字)。 习题2.6 要计算抵押贷款的偿还金额,利率、本金和付款额应分别选用哪种类型?解释 你选择的理由。 【解答】 利率可以选择float 类型,因为利率通常为百分之几。一般只保留到小数点后 两位,所以6 位有效数字就足以表示了。 本金可以选择long 类型,因为本金通常为整数。long 类型可表示的最大整数一 般为231-1(即2147483647),应该足以表示了。 付款额一般为实数,可以选择double 类型,因为float 类型的6 位有效数字可 能不足以表示。 习题2.7 解释下列字面值常量的不同之处。 (a) 'a',L'a',\ (b) 10,10u,10L,10uL,012,0xC (c) 3.14,3.14f,3.14L 【解答】 'a',L'a',\ 'a'为char 型字面值,L'a'为wchar_t 型字面值,\为字符串字面值,L\为 宽字符串字面值。 (b) 10,10u,10L,10uL,012,0xC 10 为int 型字面值,10u 为unsigned 型字面值,10L 为long 型字面值,10uL 为unsigned long 型字面值,012 为八进制表示的int 型字面值,0xC 为十六 进制表示的int 型字面值。 (c) 3.14,3.14f,3.14L 3.14 为double 型字面值,3.14f 为float 型字面值,3.14L 为long double 型 字面值。 习题2.8 确定下列字面值常量的类型: (a) –10 (b) -10u (c) -10. (d) -10e-2 【解答】 (a) int 型 (b) unsigned int 型 (c) double 型 (d) double 型 习题2.9 下列哪些(如果有)是非法的? (a) \(b) 3.14e1L (c) \(d) 1024f (e) 3.14UL (f) \comment\【解答】 (c) 非法。因为字符串字面值与宽字符串字面值的连接是未定义的。 (d) 非法。因为整数1024 后面不能带后缀f。 (e) 非法。因为浮点字面值不能带后缀U。 (f) 非法。因为分两行书写的字符串字面值必须在第一行的末尾加上反斜线。 习题2.10 使用转义字符编写一段程序,输出2M,然后换行。修改程序,输出2,跟着一 个制表符,然后是M,最后是换行符。 【解答】 输出2M、然后换行的程序段: // 输出\和换行字符 std::cout << \修改后的程序段: // 输出'2', '\\t', 'M'和换行字符 std::cout << '2' << '\\t' << 'M' << '\\n'; 习题2.11 编写程序,要求用户输入两个数——底数(base)和指数(exponent),输出 底数的指数次方的结果。 #include int base,exponent; long result=1; system(\ cout<<\ cin>>base>>exponent; if(exponent<0) { cout<<\ return -1; } if(exponent>0) { for(int cnt=1;cnt<=exponent;cnt++) result*=base; } cout< return 0; } 习题2.12 区分左值和右值,并举例说明。 【解答】 左值(lvalue)就是变量的地址,或者是一个代表“对象在内存中的位置”的 表达式。 右值(rvalue)就是变量的值,见2.3.1 节。 变量名出现在赋值运算符的左边,就是一个左值;而出现在赋值运算符右边的 变量名或字面常量就是一个右值。 例如: val1=val2/8 这里的val1 是个左值,而val2 和8 都是右值。 习题2.13 举出一个需要左值的例子。 【解答】 赋值运算符的左边(被赋值的对象)需要左值,见习题2.12。 习题2.14 下面哪些(如果有)名字是非法的?更正每个非法的标识符名字。 (a) int double = 3.14159; (b) char _; (c) bool catch-22; (d) char 1_or_2 ='1'; (e) float Float = 3.14f; 【解答】 (a) double 是C++语言中的关键字,不能用作用户标识符,所以非法。此语句 可改为:double dval = 3.14159;。 (c) 名字catch-22 中包含在字母、数字和下划线之外的字符“-”,所以非法。 可将其改为:catch_22;。 (d) 名字1_or_2 非法,因为标识符必须以字母或下划线开头,不能以数字开头。 可将其改为:one_or_two;。 习题2.15 下面两个定义是否不同?有何不同? int month = 9, day = 7; int month =09, day = 07; 如果上述定义有错的话,那么应该怎样改正呢? 【解答】 这两个定义不同。前者定义了两个int 型变量,初值分别为9 和7;后者也定义 了两个int 型变量,其中day 被初始化为八进制值7;而month 的初始化有错: 试图将month 初始化为八进制值09,但八进制数字范围为0~7,所以出错。可 将第二个定义改为: int month =011, day = 07; 习题2.16 假设calc 是一个返回double 对象的函数。下面哪些是非法定义?改正所有的 非法定义。 (a) int car = 1024, auto = 2048; (b) int ival = ival; (c) std::cin >> int input_value; (d) double salary = wage = 9999.99; (e) double calc = calc(); 【解答】 (a) 非法:auto 是关键字,不能用作变量名。使用另一变量名,如aut 即可更 正。 (c) 非法:>>运算符后面不能进行变量定义。改为: int input_value; std::cin >> input_value; (d) 非法:同一定义语句中不同变量的初始化应分别进行。改为: double salary = 9999.99, wage = 9999.99; 注意,(b)虽然语法上没有错误,但这个初始化没有实际意义,ival 仍是未初始化的。 习题2.17 下列变量的初始值(如果有)是什么? std::string global_str; int global_int; int main() { int local_int; std::string local_str; // ... return 0; } 【解答】 global_str 和local_str 的初始值均为空字符串,global_int 的初始值为0, local_int 没有初始值。 习题2.18 解释下列例子中name 的意义: extern std::string name; std::string name(\ extern std::string name(\【解答】 第一条语句是一个声明,说明std::string 变量name 在程序的其他地方定义。 第二条语句是一个定义,定义了std::string 变量name,并将name 初始化为 \。 第三条语句也是一个定义,定义了std::string 变量name,并将name 初始化为 \,但这个语句只能出现在函数外部(即,name 是一个全局变量)。 习题2.19 下列程序中j 的值是多少? int i = 42; int main() { int i = 100; int j = i; // ... } 【解答】 j 的值是100。j 的赋值所使用到的i 应该是main 函数中定义的局部变量i,因 为局部变量的定义会屏蔽全局变量的定义。 习题2.20 下列程序段将会输出什么? int i = 100, sum = 0; for (int i = 0; i != 10; ++i) sum += i; std::cout << i << \【解答】 输出为: 100 45 for 语句中定义的变量i,其作用域仅限于for 语句内部。输出的i 值是for 语 句之前所定义的变量i 的值。 习题2.21 下列程序合法吗? int sum = 0; for (int i = 0; i != 10; ++i) sum += i; std::cout << \ << \【解答】 不合法。因为变量i 具有语句作用域,只能在for 语句中使用,输出语句中 使用i 属非法。 习题2.22 下列程序段虽然合法,但是风格很糟糕。有什么问题呢?怎样改善? for (int i = 0; i < 100; ++i) // process i 【解答】 问题主要在于使用了具体值100 作为循环上界:100 的意义在上下文中没有体现 出来,导致程序的可读性差;若100 这个值在程序中出现多次,则当程序的需 求发生变化(如将100 改变为200)时,对程序代码的修改复杂且易出错,导致 程序的可维护性差。 改善方法:设置一个const 变量(常量)取代100 作为循环上界使用,并为该 变量选择有意义的名字。 习题2.23 下列哪些语句合法?对于那些不合法的使用,解释原因。 (a) const int buf; (b) int cnt = 0; const int sz = cnt; (c) cnt++; sz++; 【解答】 (a) 不合法。因为定义const 变量(常量)时必须进行初始化,而buf 没有初 始化。 (b) 合法。 (c) 不合法。因为修改了const变量sz 的值。 习题2.24 下列哪些定义是非法的?为什么?如何改正? (a) int ival = 1.01; (b) int &rval1 = 1.01; (c) int &rval2 = ival; (d) const int &rval3 = 1; 【解答】 (b)非法。 因为rval1 是一个非const引用,非const 引用不能绑定到右值,而1.01 是一 个右值。可改正为: int &rval1 = ival; (假设ival 是一个已定义的int 变量)。 习题2.25 在习题2.24 给出的定义下,下列哪些赋值是非法的?如果赋值合法,解释赋值 的作用。 (a) rval2 = 3.14159; (b) rval2 = rval3; (c) ival = rval3; (d) rval3 = ival; 【解答】 (d)非法。因为rval3 是一个const引用,不能进行赋值。 合法赋值的作用: (a)将一个double 型字面值赋给int 型变量ival,发生隐式类型转换,ival 得 到的值为3。 (b)将int 值1 赋给变量ival。 (c)将int 值1 赋给变量ival。 习题2.26 (a)中的定义和(b)中的赋值存在哪些不同?哪些是非法的? (a) int ival = 0; (b) ival = ri; const int &ri = 0; ri = ival; 【解答】 int ival = 0; 定义ival 为int 变量,并将其初始化为0。 const int &ri = 0; 定义ri 为const 引用,并将其绑定到右值0。 ival = ri; 将0 值赋给ival。 ri = ival; 试图对ri 赋值,这是非法的,因为ri 是const 引用, 不能赋值。 习题2.27 下列代码输出什么? int i, &ri = i; i = 5; ri =10; std::cout << i << \【解答】 输出: 10 10 ri 是i 的引用,对ri 进行赋值,实际上相当于对i 进行赋值,所以输出i 和 ri 的值均为10。 习题2.28 编译以下程序,确定你的编译器是否会警告遗漏了类定义后面的分号。 class Foo { // empty } // Note: no semicolon int main() { return 0; } 如果编译器的诊断结果难以理解,记住这些信息以备后用。 【解答】 在笔者所用的编译器中编译上述程序,编译器会给出如下错误信息: error C2628: 'Foo' followed by 'int' is illegal (did you forget a ';'?) (第4 行) warning C4326: return type of 'main' should be 'int or void' instead of 'Foo' (第5 行) error C2440: 'return' : cannot convert from 'int' to 'Foo' (第 6 行) 也就是说,该编译器会对遗漏了类定义后面的分号给出提示。 习题2.29 区分类中的public 部分和private 部分。 【解答】 类中public 部分定义的成员在程序的任何部分都可以访问。通常在public 部 分放置操作,以便程序中的其他部分可以执行这些操作。 类中private 部分定义的成员只能被作为类的组成部分的代码(以及该类的友 元)访问。通常在private 部分放置数据,以对对象的内部数据进行隐藏。 习题2.30 定义表示下列类型的类的数据成员: (a) 电话号码 (b) 地址 (c) 员工或公司 (d) 某大学的学生 【解答】 (a) 电话号码 class Tel_number { public: //...对象上的操作 private: std::string country_number; std::string city_number; std::string phone_number; }; (b) 地址 class Address { public: //...对象上的操作 private: std::string country; std::string city; std::string street; std::string number; }; (c) 员工或公司 class Employee { public: // ...对象上的操作 private: std::string ID; std::string name; char sex; Address addr; Tel_number tel; }; class Company { public: // ...对象上的操作 private: std::string name; Address addr; Tel_number tel; }; (d) 某大学的学生 class Student { public: // ...对象上的操作 private: std::string ID; std::string name; char sex; std::string dept; // 所在系 std::string major; Address home_addr; Tel_number tel; }; 注意,在不同的具体应用中,类的设计会有所不同,这里给出的只是一般性的 简单例子。 习题2.31 判别下列语句哪些是声明,哪些是定义,请解释原因。 (a) extern int ix = 1024 ; (b) int iy ; (c) extern int iz ; (d) extern const int &ri ; 【解答】 (a)是定义,因为extern 声明进行了初始化。 (b)是定义,变量定义的常规形式。 (c)是声明,extern 声明的常规形式。 (d)是声明,声明了一个const 引用。 习题2.32 下列声明和定义哪些应该放在头文件中?哪些应该放在源文件中?请解释原 因。 (a) int var ; (b) const double pi = 3.1416; (c) extern int total = 255 ; (d) const double sq2 = squt (2.0) ; 【解答】 (a)、(c)、(d)应放在源文件中,因为(a)和(c)是变量定义,定义通常应放在源 文件中。(d)中的const 变量sq2 不是用常量表达式初始化的,所以也应该放在 源文件中。 (b)中的const 变量pi 是用常量表达式初始化的,应该放在头文件中。 // 把vector 对象的所有元素复制给新数组 #include vector //读入元素数据并建立vector cout << \while (cin >> ival) ivec.push_back(ival); //动态创建数组 int *pia = new int[ivec.size()]; //复制元素 int *tp = pia; for (vector //释放动态数组的内存 delete [] pia; return 0; } 习题4.29 对本节第5 条框中的两段程序: (a) 解释这两段程序实现的功能。 (b) 平均来说,使用string 类型的程序执行速度要比用C 风格字符串的快很多, 在我们用了5 年的PC 机上其平均执行速度分别是: user 0.47 # string class user 2.55 # C-style character string 你预计的也一样吗?请说明原因。 【解答】 (a) 这两段程序的功能是:执行一个循环次数为1000000 的循环,在该循环的 循环体中:创建一个新字符串,将一个已存在的字符串复制给新字符串,然后 比较两个字符串,最后释放新字符串。 (b) 使用C 风格字符串的程序需要自己管理内存的分配和释放,而使用string 类型的程序由系统自动进行内存的分配和释放,因此比使用C 风格字符串的程 序要简短,执行速度也要快一些。 习题4.30 编写程序连接两个C 风格字符串字面值,把结果存储在一个C 风格字符串中。 然后再编写程序连接两个string 类型字符串,这两个string 类型字符串与前 面的C 风格字符串字面值具有相同的内容。 【解答】 连接两个C 风格字符串字面值的程序如下: // 连接两个C 风格字符串字面值, // 把结果存储在一个C 风格字符串中 #include const char *cp1 = \const char *cp2 = \ size_t len = strlen(cp1) + strlen(cp2); char *result_str = new char[len+1]; strcpy(result_str, cp1); strcat(result_str, cp2); delete [] result_str; return 0; } 相应的连接两个string 类型字符串的程序如下: // 连接两个string 类型字符串 #include const string str1(\const string str2(\string result_str; result_str = str1; result_str += str2; return 0; } 习题4.31 编写程序从标准输入设备读入字符串,并把该串存放在字符数组中。描述你的 程序如何处理可变长的输入。提供比你分配的数组长度长的字符串数据测试你 的程序。 【解答】 // 从标准输入设备读入字符串,并把该串存放在字符数组中 #include string in_str;// 用于读入字符串的string 对象 const size_t str_size = 10; char result_str[str_size+1]; // 读入字符串 cout << \<< \cin >> in_str; // 计算需复制的字符的数目 size_t len = strlen(in_str.c_str()); if (len > str_size) { len = str_size; cout << \<< \<< str_size << \} // 复制len 个字符至字符数组result_str strncpy(result_str, in_str.c_str(), len); // 在末尾加上一个空字符(null 字符) result_str[len+1] = '\\0'; return 0; } 为了接受可变长的输入,程序中用一个string 对象存放读入的字符串,然后使 用strncpy 函数将该对象的适当内容复制到字符数组中。因为字符数组的长度 是固定的,因此首先计算字符串的长度。若该长度小于或等于字符数组可容纳 字符串的长度,则复制整个字符串至字符数组,否则,根据数组的长度,复制 字符串中前面部分的字符,以防止溢出。 注意,上述给出的是满足题目要求的一个解答,事实上,如果希望接受可变长 的输入并完整地存放到字符数组中,可以采用动态创建数组来实现。 习题4.32 编写程序用int 型数组初始化vector 对象。 【解答】 // 用int 型数组初始化vector 对象 #include const size_t arr_size = 8; int int_arr[arr_size]; // 输入数组元素 cout << \for (size_t ix = 0; ix != arr_size; ++ix) cin >> int_arr[ix]; // 用int 型数组初始化vector 对象 vector 习题4.33 编写程序把int 型vector 复制给int 型数组。 【解答】 // 把int 型vector 复制给int 型数组 #include vector // 输入vector 元素 cout << \while (cin >> ival) ivec.push_back(ival); // 创建数组 int *parr = new int[ivec.size()]; // 复制元素 size_t ix = 0; for (vector delete [] parr; return 0; } 习题4.34 编写程序读入一组string 类型的数据,并将它们存储在vector 中。接着,把 该vector 对象复制给一个字符指针数组。为vector 中的每个元素创建一个新 的字符数组,并把该vector 元素的数据复制到相应的字符数组中,最后把指向 该数组的指针插入字符指针数组。 【解答】 //4-34.cpp //读入一组string 类型的数据,并将它们存储在vector 中。 //接着,把该vector 对象复制给一个字符指针数组。 //为vector 中的每个元素创建一个新的字符数组, //并把该vector 元素的数据复制到相应的字符数组中, //最后把指向该数组的指针插入字符指针数组 #include vector string str; // 输入vector 元素 cout << \while (cin >> str) svec.push_back(str); // 创建字符指针数组 char **parr = new char*[svec.size()]; // 处理vector 元素 size_t ix = 0; for (vector char *p = new char[(*iter).size()+1]; // 复制vector 元素的数据到字符数组 strcpy(p, (*iter).c_str()); // 将指向该字符数组的指针插入到字符指针数组 parr[ix] = p; } // 释放各个字符数组 for (ix =0; ix != svec.size(); ++ix) delete [] parr[ix]; // 释放字符指针数组 delete [] parr; return 0; } 习题4.35 输出习题4.34 中建立的vector 对象和数组的内容。输出数组后,记得释放字 符数组。 【解答】 //4-35.cpp //读入一组string 类型的数据,并将它们存储在vector 中。 //接着,把该vector 对象复制给一个字符指针数组: //为vector 中的每个元素创建一个新的字符数组, //并把该vector 元素的数据复制到相应的字符数组中, //然后把指向该数组的指针插入字符指针数组。 //输出建立的vector 对象和数组的内容 #include vector