C++primer习题集(第二章)
2.1 类型int,long,longlong,short的区别是什么? 无符号和带符号的区别是什么?float和double的区别是什么?
- int和long的区别,下面的图片展示的很清晰了。一般情况下,int是32位,long是32位,long long是64位,short是16位。
- 无符号和带符号的区别在于他们的范围不同,int(32位)的范围是-231~231-1,unsigned int 的范围是0~232-1
- float是32位的单精度浮点型,double是64位的双精度浮点型,表示范围更大,也更加精确。

图片来源:(29条消息) long型和int型的区别_long型与int型的区别_Leisure -_-的博客-CSDN博客(如有侵权请联系我删除)
2.2 计算按揭贷款时,对于利率、本金和付款应该分别选择什么数据类型?说明理由。
应该都选择double型(尽可能高精度)
2.3 读程序写结果
#include<iostream> using namespace std; int main() { unsigned u=10,u2=42; cout<<"unsigned u=10,u2=42"<<endl; cout<<"u2-u="<<u2-u<<endl; cout<<"u-u2="<<u-u2<<endl<<endl; int i=10,i2=42; cout<<"int i=10,i2=42"<<endl; cout<<"i2-i="<<i2-i<<endl; cout<<"i-i2="<<i-i2<<endl<<endl; //这里转换为无符号数 cout<<"i-u="<<i-u<<endl; cout<<"u-i="<<u-i<<endl; return 0; }
输出结果如下(建议最后两行换成i-u2和u-i2看看结果):
unsigned u=10,u2=42 u2-u=32 u-u2=4294967264 int i=10,i2=42 i2-i=32 i-i2=-32 i-u=0 u-i=0
2.4 编写程序检查你的估计是否正确。如果不正确,自己去看。
见2.3。
2.5 指出下面字面值的类型并说明每一组中几种字面值的区别。
#include <iostream> int main() { char c1='a'; wchar_t c2=L'a'; //宽字符类型,支持更多的字符 const char s1[]="a"; //字符串 const wchar_t s2[] = L"a"; //宽类型字符串 int n1 = 10; unsigned n2 = 10u; long n3 = 10L; unsigned long n4 = 10UL; int n5 = 012; //八进制整型 int n6 = 0xC; //十六进制整型 double d1 = 3.14; float d2 = 3.14F; long double d3 = 3.14L; int x1 = 10; unsigned int x2 = 10u; double x3 = 10.; double x4 = 10e-2; return 0; }
2.6 下面两组定义是否有区别,如果有请进行论述。
#include<iostream> using namespace std; int main() { /*0打头代表八进制*/ int month=6,day=17; int month2=06,day2=017; cout<<"month="<<month<<",day="<<day<<endl; cout<<"month2="<<month2<<",day2="<<day2<<endl; return 0; } /* 原书中month是9和09,day是7和07 int month=9,day=7; int month=09,day=07; */
输出的是十进制 month=6,day=17 month2=6,day2=15
2.7 下述字面值表示何种含义?他们各自的数据类型是什么?
#include <iostream> using namespace std; int main() { char s[]="Who goes with F\145rgus?\012"; cout<<s<<endl; long double i1 = 3.14e1L; // i2 = 1024f; //应该改为1024.f long double i3 = 3.14L; return 0; }
Who goes with Fergus?
2.8 利用转义序列编写一段程序,输出2M。修改程序使他输出2,然后输出制表符,再输出M,最后换行。
#include <iostream> using namespace std; int main() { cout<<"2\tM\n"; return 0; }
2.9 解释下列定义的含义。对于非法的定义进行改正。
#include <iostream> using namespace std; #include<stdio.h> int main() { //a:错误,必须先定义 int input_value; cin>>input_value; //b:{}要求比较严格,去掉大括号,或者int改成double double i={3.14}; cout<<i<<endl; //c double salary=9999.99,wage=salary; cout<<salary<<"\t"<<wage<<endl; //d:会损失小数部分,但不会报错 int d=3.14; cout<<d<<endl; return 0; }
5(自己输入的) 3.14 9999.99 9999.99 3
2.10 下列变量的初值分别是什么?
#include <iostream> std::string global_str; int global_int; int main() { int local_int; std::string local_str; std::cout<<"global_str:"<<global_str<<std::endl; std::cout<<"global_int:"<<global_int<<std::endl; std::cout<<"local_str:"<<local_str<<std::endl; std::cout<<"local_int:"<<local_int<<std::endl; return 0; }
global_str: global_int:0 local_str: local_int:4201472
//str均为空串,全局变量默认为0,局部变量未定义,数值与地址相关
2.11 指出下面的语句是声明还是定义。
#include <iostream> using namespace std; //警告:'ix'初始化并声明为'extern' extern int ix=1024; //声明,并自动初始化定义为0 int iy; //声明 extern int iz; int main() { return 0; }
2.12 指出下面哪些名字是非法的?
//错误,double是关键字 int double=3.14 //正确,但是无意义,不建议 int _; //错误,-是非法符号 int catch-22; //错误,不能数字作为变量开头 int 1_or_2; //正确,但是易混淆,不建议 double Double=3.14;
2.13 下面程序中j的值是多少?
#include <iostream> using namespace std; int i=42; int main() { int i=100; int j=i; cout << "j=" <<j<< endl; return 0; }
j=100
2.14 下面的程序合法吗?如果合法会输出什么?
是合法的,循环里面声明的 i 的作用域也仅在循环内。
#include <iostream> using namespace std; int main() { int i=100, sum=0; for(int i=0;i!=10;++i) sum+=i; cout << "i=" <<i<<"\tsum="<<sum<<endl; return 0; }
i=100 sum=45
2.15 下面哪个定义是不合法的?为什么?
//a:合法 int ival=1.01; //b:非法,引用的初始值必须是一个对象 int &rval1=1.01; //c:合法 int &rval2=ival; //d:非法,引用必须初始化 int &rval3;
2.16 考察下面所有赋值然后回答:哪些赋值是非法的,为什么?哪些赋值是合法的?他们执行了哪些操作?
#include <iostream> using namespace std; int main() { int i=0,&r1=i; double d=0,&r2=d; r2=3.14159; cout<<"d="<<d<<"\tr2="<<r2<<endl; r2=r1; cout<<"d="<<d<<"\tr2="<<r2<<endl; d=3.14; //我自己加的 i=r2; cout<<"i="<<i<<"\tr1="<<r1<<endl; r1=d; cout<<"i="<<i<<"\tr1="<<r1<<endl; return 0; }
d=3.14159 r2=3.14159 d=0 r2=0 i=3 r1=3 i=3 r1=3
都是合法的,基本就是等于来回赋值。
2.17 执行下面的代码将输出什么结果?
#include <iostream> using namespace std; int main() { int i=0,&ri=i; i=5; ri=10; cout<<i<<" "<<ri<<endl; return 0; }
10 10
2.18 编写代码,分别修改指针的值以及指针所指对象的值。
#include <iostream> using namespace std; int main() { int i=0,j=10; int *ri=&i; cout<<"最开始的值和地址"<<endl; cout<<*ri<<"\t"<<ri<<endl; cout<<"换指针的值"<<endl; ri=&j; cout<<*ri<<"\t"<<ri<<endl; cout<<"换指针所指对象的值"<<endl; *ri=100; cout<<*ri<<"\t"<<ri<<endl; return 0; }
最开始的值和地址 0 0x7ffd804a4134 换指针的值 10 0x7ffd804a4130 换指针所指对象的值 100 0x7ffd804a4130
2.19 说明指针和引用的主要区别。
1. 定义和使用方式:指针是一个变量,用于存储另一个变量的内存地址。它需要使用星号(*)来声明指针类型,并使用取地址符(&)来获取变量的地址。而引用是一个别名,用于给已存在的变量起一个新的名称。它使用变量的名称来声明引用,没有自己的内存地址。
2. 空值:指针可以具有空值(NULL或nullptr),表示指向无效内存地址。而引用必须始终引用有效的对象,不能为null。
3. 变量绑定:指针可以在声明后重新指向其他变量或空值。而引用在声明后必须始终引用同一个对象,不能重新绑定到其他对象。
4. 内存访问:指针可以通过解引用操作符(*)来访问所指向的对象的值。而引用直接访问所引用对象的值,无需解引用。
5. 空间占用:指针本身需要占用一定的内存空间来存储地址值。而引用本身不需要额外的内存空间,它只是变量的别名。
6. 传递方式:指针可以作为函数参数进行传递,可以传递地址或者通过指针修改原始变量的值。而引用也可以作为函数参数进行传递,可以直接修改原始变量的值,但不需要使用指针操作符。
2.20 叙述下面这段代码的作用
#include <iostream> using namespace std; int main() { int i=42; int *p1=&i; *p1=*p1 * *p1; cout<<*p1<<endl; return 0; }
1764 作用是让i=i*i
2.21 请解释下面的定义?他们有非法的吗?如果有,为什么?
#include <iostream> using namespace std; int main() { int i=2; //a 非法,指针和对象的数据类型不一致 //double *dp=&i; //b 非法,ip是一个指针,右边应该也是对应的类型,而非int,报错为error: invalid conversion from ‘int’ to ‘int*’ //int *ip=i; //c 合法 int *p=&i; cout<<p<<" "<<*p<<endl; return 0; }
0x7ffd6a359b44 2
2.22 假设P是一个int类型的指针,说明下述代码的含义。
if(p) //...p是一个指针,所以这里判断的是,指针p是否指向NULL if(*p) //...*p是一个指针指向的值/数据结构,这里判断的是,*p指的对象是否为0
2.23 给定指针*p,你能知道它是否指向了一个合法的对象吗?无论能不能,请叙述你的思路。
这个问题,我个人只能想到通过判断指针是否为空来判断是否指向了对象,但是无法确定对象的合法性。
下面是GPT给出来的答案,感觉比较全面:
给定指针*p,我们可以通过以下几种方法来判断它是否指向了一个合法的对象:
1. 空指针检查:首先,我们可以检查指针*p是否为NULL。如果指针为NULL,表示它没有指向任何对象,因此不是一个合法的对象。
2. 野指针检查:我们可以尝试访问指针所指向的内存区域。如果访问该内存区域时发生了访问违例(如访问非法地址、访问未初始化的内存等),则可以判断指针指向的对象不是合法的。
3. 动态内存分配检查:如果指针*p是通过动态内存分配函数(如malloc、new等)分配的内存,则可以通过检查分配函数的返回值是否为NULL来判断分配是否成功。如果分配失败,指针指向的对象不是合法的。
4. 对象类型检查:如果我们知道指针*p应该指向的对象类型,可以使用类型检查来判断指针是否指向了一个合法的对象。例如,可以使用dynamic_cast或者typeid来检查指针所指向对象的类型是否与预期的类型匹配。
需要注意的是,以上方法并非绝对可靠,因为在某些情况下,指针可能指向已经释放的内存或者已经销毁的对象。因此,在判断指针是否指向合法对象时,需要综合考虑多种因素,并根据具体情况进行判断。
2.24 下面这段代码中为什么指针p合法而指针ip非法?
int i=42; void *p=&i; long *ip=&i;
因为指针和值的数据类型,必须一致,所以ip是错的。
但是void是一个例外,可以用void型的指针指向任何类型的值
2.25 说明下列变量的类型和值
//a 指针,int,绑定i的引用 int *ip,i,&r=i; //b int,指针指向null int i,*ip=0; //c 指针,int int *ip,ip2;
2.26 下面哪些句子是合法的?如果有不合法的句子,请说明理由。
const int buf; //a int cnt=0; //b const int sz=cnt; //c ++cnt;++sz; //d /* a:合法 b:合法 c:合法 d:++cnt合法,++sz非法,因为sz被定义为const int型,不能修改 */
2.27 下面哪些初始化是合法的?请说明原因。
int i=-1,&r=0; //a int *const p2=&i2; //b const int i=-1,&r=0; //c const int *const p3=&i2; //d const int *p1=&i2; //e const int const &r2; //f const int i2=i,&r=i; //g /* a:非法。非常量引用的必须为左值 b:如果i2是int则合法,如果是const int则非法 c:合法 d:合法 e:合法 f:非法,const必须初始化 g:合法 */
2.28 说明下面这些定义是什么意思?挑出里面不合法的。
int i,*const cp; //a int *p1,*const p2; //b const int ic,&r=ic; //c const int *const p3; //d const int *p; //e /* a:非法。cp要初始化 b:非法。p2要初始化 c:非法。ic要初始化 d:非法。P3要初始化 e:合法。 */
2.29 假定已有上一个练习中定义的变量,则下面哪些语句是正确的?请说明理由。
i=ic; //a p1=p3; //b p1=⁣ //c p3=⁣ //d p2=p1; //e ic=*p3; //f /* a:合法 b:非法。p3是const型,p1也是才行 c:非法。p1要是const int的指针才行 d:合法 e:合法 f:非法,ic是const型,不能修改它的值了 */
2.30 对于下面这些对象,请说明被声明成了底层还是顶层const。
const int v2=0; //底层 int v1=v2; //都不是 int *p1=&v1,&r1=v1; //p1,r1都不是 const int *p2=&v2,*const p3=&i,&r2=v2; //p2是底层,p3是底层+顶层(指向常亮的常量指针),r2是底层
2.31 假设已有上一个练习所做的那些声明,则下面哪些语句是合法的?说明顶层const和底层const在下面的例子中有何体现。
r1=v2; //非法,v2是const常亮,而r1不是const型的引用 p1=p2;p2=p1; //不能p1=p2,因为p2是const而p1不是,但是反过来可以 p1=p3;p2=p3; //不能p1=p3,但是可以p2=p3
2.32 下面代码是否合法?如果非法请修正。
int null=0, *p=null; //非法 int null=0, *p=nullptr;
2.33 利用本节定义的变量,判断下列语句的运行结果。
#include <iostream> using namespace std; int main() { /*---------------------定义的变量-----------------------*/ int i=0,&r=i; auto a=r; //a是一个整数 const int ci=i,&cr=ci; auto b=ci; //b是一个整数 auto c=cr; //c是一个整数 auto d=&i; //d是一个整型指针 auto e=&ci; //e是一个指向整数常量的指针 const auto f=ci;//ci的推演类型是int,f是const int auto &g=ci; //g是一个整型常量引用,绑定到ci /*------------------判断下面的结果--------------------*/ a=42;b=42;c=42; d=42;e=42;g=42; /* a为int的42 b为int的42 c为int的42 d为一个int的指针,会报错 e为const int的指针,会报错 g为const int的引用,会报错 */ return 0; }
2.34 基于上一个练习中的变量和语句编写一段程序,输出赋值前后的内容,你刚才的推断正确吗?不对就去学。
#include <iostream> using namespace std; int main() { /*------------------------------------------*/ int i=0,&r=i; auto a=r; //a是一个整数 const int ci=i,&cr=ci; auto b=ci; //b是一个整数 auto c=cr; //c是一个整数 auto d=&i; //d是一个整型指针 auto e=&ci; //e是一个指向整数常量的指针 const auto f=ci;//ci的推演类型是int,f是const int auto &g=ci; //g是一个整型常量引用,绑定到ci cout<<"a:"<<a<<endl; cout<<"b:"<<b<<endl; cout<<"c:"<<c<<endl; /*--------------------------------------*/ a=42;b=42;c=42; //d=42;e=42;g=42; cout<<"new a:"<<a<<endl; cout<<"new b:"<<b<<endl; cout<<"new c:"<<c<<endl; return 0; }
a:0 b:0 c:0 new a:42 new b:42 new c:42
2.35 判断下列定义推断出来的类型是什么,然后编写程序进行验证。
#include <iostream> using namespace std; int main() { const int i=42; auto j=i; const auto &k=i; auto *p=&i; const auto j2=i,&k2=i; /* i:const int j:int k:const int 的引用 p:const int 的指针 j2:const int k2:const int 的引用 */ return 0; }
2.36 关于下面的代码,请指出每一个变量的类型以及程序结束的时候它们各自的值。
这里d是对a的引用,所以改变d等于改变a的值。
#include <iostream> #include<typeinfo> using namespace std; int main() { int a=3,b=4; decltype(a) c=a; decltype((b)) d=a; ++c; ++d; cout<<"a:"<<a<<endl; cout<<"b:"<<b<<endl; cout<<"c:"<<c<<endl; cout<<"d:"<<d<<endl; return 0; }
a:4 b:4 c:4 d:4
2.37 赋值是会产生引用的一类典型表达式,引用的类型就是左值的类型。也就是说,如果i是int,则表达式i=x的类型是int&。根据这一特点,请指出下面的代码中每一个变量的类型和值。
#include <iostream> #include<typeinfo> using namespace std; int main() { int a=3,b=4; //a:int b:int decltype(a) c=a; //a:int& c:int decltype(a=b) d=a; //这里不会改变a的值 printf("%d %d %d %d\n", a, b, c, d); return 0; }
3 4 3 3
2.38 说明由 decltype 指定类型和由 auto 指定类型有何区别。请举出一个例子,decltype指定的类型与auto指定的类型一样;再举一个例子,decltype指定的类型与auto指定的类型不一样。
decltype用于从表达式中推导出变量的类型,而auto用于从变量的初始值推导出变量的类型。
一样:
int x = 10; decltype(x) y = x; // y的类型被推导为int auto z = x; // z的类型也被推导为int
不一样:
const int x = 10; decltype(x) y = x; // y的类型被推导为const int auto z = x; // z的类型被推导为int
2.39 编译下面的程序观察运行结果。注意如果忘记写类定义体后面的分号会发生什么情况?
#include <iostream> struct Foo { } int main() { return 0; } /** * [2_39.cpp 2021-02-17 09:53:32.462] ,,2_39.cpp:3:15: error: expected ';' after struct definition struct Foo { } ^ ; */
2.40 根据自己的理解写出sales_data类,最好和书中的例子有所区别。
struct Sales_data{ //编号 std::string bookNo[20]; //价格 double price; //总收入 double sum=0; //数目 int num=0; };
2.41 使用你自己的Sales_data类重写1.5.1节(第20页)、1.5.2节(第21页)和1.6节(第22页)的练习。眼下先把Sales_data类的定义和main函数放在同一个文件里
2.42 根据你自己的理解重写一个Sales_data.h头文件,并以此为基础重做2.6.2节(第67页)的练习。
#include <iostream> #include<time.h> #include<vector> #include<string.h> using namespace std; struct Sales_book{ //编号 char bookNo[20]; //价格 double price; //总收入 double sum; //数目 int num=0; }; int main() { vector<Sales_book> data; Sales_book book; /*----------------1.20---------------------*/ cout<<"1.20"<<endl; while(cin>>book.bookNo) { cin>>book.price; cin>>book.sum; cin>>book.num; data.push_back(book); if (strcmp(book.bookNo,"0")==0) { break; } } for(auto &i:data) { cout<<"part1: " <<i.bookNo<<", " <<i.price<<", " <<i.sum<<", " <<i.num<<endl; } /*----------------1.21---------------------*/ cout<<"1.21"<<endl; Sales_book data1,data2; printf("请输入:\n"); char c=getchar(); c=getchar(); while (true) { cout<<"input:"<<endl; cin >> data1.bookNo >> data1.price >> data1.sum >> data1.num; cin >> data2.bookNo >> data2.price >> data2.sum >> data2.num; if (data1.bookNo == data2.bookNo) { std::cout << data1.bookNo << ", " << data1.price<<", " << data1.sum + data2.sum << ", " << data1.num + data2.num << std::endl; break; } else { cerr << "No不一致,重新输入!" << endl; } } /*----------------1.22---------------------*/ cout<<"1.22"<<endl; //... /*----------------1.23---------------------*/ cout<<"1.23"<<endl; Sales_book cur_item, item; if (cin >>cur_item.bookNo){ cin>>cur_item.price; cin>>cur_item.sum; cin>>cur_item.num; int cnt=1; while(cin >> item.bookNo){ if(strcmp(trans.bookNo,"0")==0) break; cin>>item.price; cin>>item.sum; cin>>item.num; if (item.bookNo ==cur_item.bookNo) ++cnt; //将cnt加1 else { cout << cur_item.bookNo <<" occurs " << cnt <<" times "<< endl; cur_item=item; //记住新值 cnt=1; //重置计数器 } } //while循环在这里结束 cout << cur_item.bookNo <<" occurs " << cnt <<" times "<< endl; } //最外层的if语句在这里结束 /*----------------1.24---------------------*/ cout<<"1.24"<<endl; //... /*----------------1.25---------------------*/ cout<<"1.25"<<endl; Sales_book total;//保存和的变量 //读入第一条交易记录,并确保有数据可以处理 if (cin >> total.bookNo) { cin>>total.price>>total.sum>>total.num; Sales_book trans; //保存下一条交易记录的变量 //读入并处理剩余交易记录 while (cin >> trans.bookNo) { if(strcmp(trans.bookNo,"0")==0) break; cin>>trans.price>>trans.sum>>trans.num; //如果仍在处理相同的书 if (total.bookNo == trans.bookNo) { total.num+=trans.num; total.sum+=trans.sum; total.price=total.sum/total.num; } else { //打印前一本书的结果 cout<< total.bookNo <<", " <<total.price <<", " <<total.sum <<", " <<total.bookNo <<", " << endl; strcpy(total.bookNo,trans.bookNo); //total现在表示下一本书的销售额 total.price=trans.price; total.sum=trans.sum; total.num=trans.num; } } cout<< total.bookNo <<", " <<total.price <<", " <<total.sum <<", " <<total.bookNo <<", " << endl; } else { //没有输入!警告 cerr << "No data!" << endl; return -1; } return 0; }

浙公网安备 33010602011771号