C++primer第二章习题

练习2.1:类型int、long、long long和short的区别是什么?无符号类型和带符号类型的区别是什么?float和double的区别是什么?

答:

int、long、long long和short都属于整型。区别是类型在内存中所占的最小 比特数不同。

    short 16位,int 16位, long 32位, long long 64位。

②无符号型中所有的比特数都用来存储数值,仅能表示大于等于0的值。

   带符号型可表示正数 负数或 0.

③float和double分别是单精度浮点数和双精度浮点数。区别主要是在内存中所占的位数不同,还有,它们的有效位数不同。


练习2.2:计算按揭贷款时,对于利率 本金 和付款分别应选择何种数据类型?

double。


练习2.3: 读程序写结果.

#include

/*统计输入流中每个值连续出现出现了多少次*/
int main()
{
    unsigned u = 10, u2 = 42;

    std::cout << u2 - u << std::endl;  //32
    std::cout << u - u2 << std::endl;  //4294967264

    int i = 10, i2 = 42;

    std::cout << i2 - i <
这里解释一下第二个输出:

首先这里的unsigned其实就是unsigned int. 总共有 32 位。由于类型是无符号的,所以负值会被转化:

4294967264 = (-32(计算结果)+ 2的32次方)% 2的32次方。

无符号型的超出范围的转换都可以用上面的思路计算。


练习2.4:编写程序检查你的估计是否正确。

正确。


练习2.5:

(a) 'a' 表示字符a,L'a'表示宽字符型字面值a,类型是wchar_t, "a"表示字符串a, L"a"表示宽字符型字符串a。

(b)10普通整型,10u 无符整型, 10L 长整型,10uL 无符号长整型, 012八进制数(数值转换成十进制为10),0xC 十六进制数(数值转换成十进制为12)。

(c)3.14普通浮点型, 3.14f 单精度浮点, 3.14L long double型的扩展精度浮点数。

(d)10 普通整型, 10u无符号整型, 10. 浮点数, 10e-2 科学计数法表示的浮点数(10*10^-2)


练习2.6:下面两组定义是否有区别?

有区别。

第一行赋值十进制数。

第二行赋值八进制数。 但是int month = 09;无法通过编译,因为9超出八进制表示范围。



练习2.7:下面的字面值表示何种含义?他们各自的数据类型是什么?

(a)"Who goes with F\145rgus?\012"

含义:Who goed with Fergus?(换行符)

(b)科学计数法表示的扩展精度浮点数。(3.14*10^1)

(c)错误。应该表示为 1024.f

(d)扩展精度的浮点数。



练习2.8:请利用转义序列编写一段程序,要求先输出2M,然后转到新一行。修改程序使其先输出2,然后输出制表符,再输出M,最后转到新一行。

#include

int main()
{
    std::cout << "2\x4d\012";

    std::cout << "2\t\115\n";

    return 0;
}

练习2.9:解释下列定义的含义。对于非法的定义,请说明错在何处并将其改正。

(a)错误。应该改成:

int input_value;

std::cin >> input_value;

(b)int i = {3.14}; 引发警告,浮点数赋值给整型的i造成精度损失。

(c)错误。改成:

double salary,wage;

salary = wage = 9999.99;

(d)int i = 3.14;引发警告。理由同(b)


练习2.10:下列变量的初始值是什么?

global_str 、local_str都被默认初始化为空串。

global_int定义在所有函数体之外,被默认初始化为0;

local_int定义在main函数体之内,不被默认初始化,即local_int是未定义的。


练习2.11:指出下面的语句是声明还是定义:

(a)定义

(b)声明并且定义

(c)声明


重点:

声明和定义的区别实际上很重要。如果要在多个文件中使用同一个变量,就必须将声明和定义分离。此时,变量的定义出现在且只能出现在一个文件中,而其他用到该变量的文件必须对其进行声明,却绝对不能重复定义。

如果想声明一个变量而非定义它,就在变量名前面添加关键字extern,而且不要显式地初始化变量。



练习1.12:请指出下面的名字中哪些是非法的?

(a)非法  (b)合法

(c)非法  (d)非法

(e)合法


练习1.13:下面程序中j的值是多少?

int i = 42;

int main()

{

int i = 100;

int j = i;

}

程序中的j的值为100. 内层的局部作用域会覆盖全局作用域。


练习2.14:下面的程序合法吗?如果合法,它将输出什么?

int i = 100,sum = 0;

for( int i =0; i != 10; ++i )

sum += i;

std::cout << i <<  " " << sum << std::endl;


合法。输出: 100 45


练习2.15:下面的哪个定义是不合法的?为什么?

(a)int ival = 1.01;  //合法。但是会损失精度。

(b)int &rval1 = 1.01; //非法。普通引用不能指向常量。

(c)int &rval2 = ival; //合法。

(d)int &rval3; //非法,引用必须初始化。


练习2.16:考察下面所有赋值然后回答:哪些赋值是不合法的?为什么?哪些赋值是合法?他们执行了什么样的操作?

都是合法的。(c)(d)有精度损失。窄化操作。


练习2.17:执行下面的代码段将输出什么结果。

int i, &ri = i;

i = 5; ri = 10;

std::cout <<  i  << " " << ri << std::endl;


输出结果: 10 10


练习2.18:编写代码分别更改指针的值以及指针所指对象的值。

#include

int main()
{
    int i = 5, j = 10;
    int *p = &i;

    std::cout << p << " " << *p << std::endl;
    p = &j;
    std::cout << p << " " << *p << std::endl;
    *p = 20;
    std::cout << p << " " << *p << std::endl;

    return 0;
}

练习2.19:请说明指针和引用的区别。

指针“指向”内存中的某个对象,而引用“绑定到”内存中的某个对象,他们都实现了对其他对象的间接访问,二者的区别主要有两方面:

第一,指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内它可以指向几个不同的对象;引用不是一个对象,无法令引用重新绑定到另外一个对象。

第二,指针无需在定义时赋初值,和其他内置类型一样,在块作用域内定义的指针如果没有初始化,也将拥有一个不确定的值;引用则必须在定义时赋初值


练习2.20:请叙述下面这段代码的作用。

int i = 42;

int *p = &i;

*p1 = *p1 * *p1;


通过指针把 变量 i 存储的值变为原来的平方。


练习2.21:请解释下述定义。在这些定义中有非法的吗?如果有,为什么?

int i = 0;

(a) double *dp = &i;  //非法。dp是一个double指针,i是int型变量,类型不匹配。

(b) int *ip = i;   //非法。不能把int型变量赋值给一个指针。

(c) int *p = &i; //合法。


练习2.22:假设p是一个int型指针,请说明下述代码的含义。

if( p )    // 当p是一个有效指针时条件为真。

if( *p )  // 当p所指的对象的值非0的时候,条件为真。


练习2.23:给定指针p,你能知道它是否指向了一个合法对象吗?如果能,叙述判断的思路;如果不能,也请说明原因。

如果定义指针时 都先初始化为nullptr,可以用if语句判断。


练习2.24:在下面这段代码中为什么p合法而lp非法?

int i = 42;

void *p = &i;  //void*指针可以存放任意对象的地址。

long *lp = &i; //类型不匹配


练习2.25:说明下列变量的类型和值。

(a) int *ip, i, &r = i;  // ip是指向int型变量的指针, i是int型变量,r是对i的引用。

(b) int i,*ip = 0; // i是int型变量, ip是指向int变量的指针。


练习2.26:下面那些句子是合法的?如果有不合法的句子,请说明为什么?

(a)const int buf;  //非法。因为const对象在创建后其值就不能改变,所以const对象必须初始化。

(b)int cnt = 0; //合法。

(c)const int sz = cnt;  //合法。

(d)++cnt; //合法。

     ++sz; //非法。sz对象是const对象,不能改变其值。


练习2.27:下面的哪些初始化时合法的?请说明原因。

(a) int i = -1, &r = 0; //非法。r是一个普通的引用,不能绑定到字面值常量值0。

(b) int *const p2 = &i2;  //合法。p2是常量指针,永远指向变量i2。

(c)const int i = -1, &r = 0;  //合法。此时的r是常量引用,可以绑定到字面值常量0上。

(d)const int *const p3 = &i2; //合法。p3是常量指针,永远指向i2。同时,p3是指向常量的指针,也就是说我们            //不能通过p3改变i2的值。

(e)const int *p1 = &i2;  //合法。但不能通过p1改变i2的值。

(f) const int &const r2;  //非法。引用本身不是对象,不能让引用恒定不变!!

(g)const int i2 = i, &r = i; //合法。i2是常量,r是常量引用。


练习2.28:说明下面这些定义是什么意思,挑出其中不合法的。

重要的事先重复三遍:

(a)int i, *const cp; // 非法。cp是常量指针,必须初始化。

(b) int *p1, *const p2; //非法。p2是常量指针,必须初始化。

(c) const int ic, &r = ic;  //非法。ic是一个常量,必须初始化。

(d) const int *const p3;  //非法。p3是常量指针,必须初始化。

(e) const int *p;  //合法。但p没有指向实际对象。


练习2.29:假设已有上一个练习中定义的那些变量,则下面的哪些语句是合法的?请说明原因。

一定要注意:

const对象一定要初始化,不能对const对象赋值!!!

(a) i = ic;  //合法。常量ic的值赋值给变量i。

(b) p1 = p3; //非法。普通指针p1指向了一个常量,能随意改变指向对象的值,所以非法。

(c) p1 = ⁣  //非法。普通指针p1不能指向一个const对象。

(d) p3 = ⁣  // 非法。p3是一个常量指针,不能对其赋值。(本就初始化了)

(e) p2 = p1;  //非法。p2是一个常量指针,不能赋值。

(f) ic = *p3;  //非法。 ic是一个常量,不能赋值。


练习2.30:对于下面的这些语句,请说明对象被声明成顶层const还是低沉const?

const int v2 = 0; //v2是顶层const

int v1 = v2; int *p1 = &v1, &r1 = v2;

const int *p2 = &v2,  *const p3 = &i, &r2 = v2; 

 //p2是底层const,p3既是顶层const又是底层const,r2是顶层const。


练习2.31:假设已有上一个练习中所做的那些声明,则下面的哪些语句是合法的?请说明顶层const和底层const在每个例子中有何体现。

r1 = v2;  //合法。

p1 = p2; // 非法。普通指针p1不能指向常量。

p2 = p1;//合法。

p1 = p3; //非法。普通指针p1不能指向常量。

p2 = p3;  //合法。


练习2.32:下面的代码是否合法?如果非法,请设法将其修改正确。

int null = 0, *p = null;

非法。

不能将int值直接赋值给int型指针,故应该改成:

int null = 0, *p = &null;


练习2.33:利用本节定义的变量,判断下列语句的运行结果。

a = 42;//合法

b = 42;//合法

c = 42;//合法

d = 42;//非法。d的类型是一个整型指针。不能对其赋值常数。

e = 42; //非法。e的类型是一个指向整型常量的指针。

g = 42;//非法。g是一个整型常量的引用,不能赋值。



练习2.34:基于上一个练习中的变量和语句编写一段程序,输出赋值前后变量的内容,你刚才的推断正确吗?

正确。


练习2.35:判断下列定义推断出的类型是什么?然后编写程序进行验证。

const int i = 42;

auto j = i; // j是int型变量。auto一般会忽略顶层const

const auto &k = i; //k是int型常量的引用。

auto *p = &i;  // p是指向int型常量的指针

const auto j2 = i, &k2 = i; //j2是int型常量,k2是整型常量的引用。


练习2.36:关于下面的代码,请指出每一个变量的类型以及程序结束时它们各自的值。

注意:

decltype的表达式如果是加上了括号的变量,结果将是引用。

int a = 3, b = 4;  // a,b都是int型变量

decltype ( a ) c = a; //c为int型变量

decltype ( ( b ) ) d = a; //d为int型变量的引用

++c;

++d;

程序结束时,

a = 4

b = 4

c = 4

d = 4


练习2.37:赋值是会产生引用的一类典型表达式,引用的类型就是左值的类型。也就是说,如果i是int,则表达式 i = x 的类型是int &。根据这一特点,请指出下面的代码中每一个变量的类型和值。

int a = 3, b = 4;

decltype ( a ) c = a;  // c是int型变量

decltype( a = b ) d = a; //d是int型变量的引用


注意:decltype括号中的表达式 a = b 作为decltype的参数,编译器分析表达式并得到它的类型作为d的推断类型,但是不实际计算该表达式,所以a的值不发生改变


所以,最终 a c d的值都为3,b 的值为4。


练习2.38:说明由decltype指定类型和由auto指定类型有何区别?请举出一个例子,decltype指定的类型和auto指定的类型一样;再举一个例子,decltype指定的类型和auto指定的类型不一样。

auto 和 decltype的区别主要有三个方面:

第一,auto类型说明符用编译器计算变量的初始值来推断其类型,而decltype虽然也让编译器分析表达式并得到它的类型,但是不实际计算表达式的值

第二,编译器推断出来的auto类型有时候和初始值的类型并不完全一样,编译器会适当地改变结果类型使其更符合初始化规则。例如,auto一般会忽略掉顶层const,而把底层const留下来。与之相反,decltype会保留变量的顶层const。

第三,与auto不同,decltype的结果类型与表达式形式密切相关,如果变量名加上了一对括号,则得到的类型与不加括号时会有不同。如果decltype使用的是一个不加括号的变量,则得到的结果就是该变量的类型;如果给变量加上了一层或多层括号,则编译器将推断得到引用类型。


练习2.39:编译下面的程序观察其运行结果,注意,如果忘记写类定义体后面的分号会发生什么情况?记录下相关信息,以后可能会有用。

error: expected ';' after struct definition|


练习2.40:根据自己的理解写出Sales_data类,最好与书中的例子有所区别。

//新增 零售价,实售价,折扣

struct Sales_data{
	std::string bookNo;
	unsigned units_sold = 0;
	double selling_price = 0.0;
	double sale_price = 0.0;
	double discount = 0.0;
};


练习2.41:使用你的Sales_data类重写1.5.1节、1.5.2节和1.6节的练习。眼下先把Sales_data类的定义和main函数放在同一个文件里。

#include
#include


class Sales_data{

    //友元函数
    friend std::istream& operator >> ( std::istream&, Sales_data& );
    friend std::ostream& operator << ( std::ostream&, const Sales_data& );
    friend bool operator < ( const Sales_data&, const Sales_data& );
    friend bool operator == ( const Sales_data&, const Sales_data& );

    public:  //构造函数
        Sales_data() = default;
        Sales_data( const std::string &book ): bookNo( book ) { }
        Sales_data( std::istream &is ) { is >> *this; }

    public:
        Sales_data& operator += ( const Sales_data& );
        std::string isbn() const { return bookNo; }

    private:
        std::string bookNo;
        unsigned units_sold = 0;
        double selling_price = 0.0;
        double sale_price = 0.0;
        double discount = 0.0;
};


inline bool compareIsbn( const Sales_data&lhs, const Sales_data&rhs )
{
    return lhs.isbn() == rhs.isbn();
}

Sales_data operator + ( const Sales_data&, const Sales_data& );//声明

//友元函数定义
inline bool operator == ( const Sales_data &lhs, const Sales_data &rhs )
{
    return lhs.units_sold == rhs.units_sold && lhs.sale_price == rhs.sale_price &&
            lhs.isbn() == rhs.isbn();
}

inline bool  operator != ( const Sales_data &lhs, const Sales_data &rhs )
{
    return !( lhs == rhs );
}

//成员函数
Sales_data& Sales_data::operator += ( const Sales_data &rhs )
{
    sale_price = ( rhs.sale_price * rhs.units_sold + sale_price * units_sold )
                /( rhs.units_sold + units_sold );
    units_sold += rhs.units_sold;

    if( sale_price != 0 )
        discount = sale_price / selling_price;

    return *this;
}


Sales_data operator + ( const Sales_data &lhs, const Sales_data &rhs )
{
    Sales_data tmp( lhs );
    tmp += rhs;

    return tmp;
}


std::istream& operator >> ( std::istream &in, Sales_data &s )
{
    in >> s.bookNo >> s.units_sold >> s.selling_price >> s.sale_price;

    if( in && s.selling_price != 0 )
        s.discount = s.sale_price / s.selling_price;
    else
        s = Sales_data();

    return in;
}


std::ostream& operator << ( std::ostream &out, const Sales_data &s )
{
    out << s.isbn() << " " << s.units_sold << " "
        << s.selling_price << " " << s.sale_price << " " << s.discount;

    return out;
}


int main()
{
    /* 练习1.20 读取一组销售记录,将每条记录打印到标准输出 */
    Sales_data book;
    std::cout << "请输入销售记录(序号,销量,零售价,实售价):" << std::endl;
    while( std::cin >> book )
        std::cout << "ISBN、售出本数、原始价格、实售价格、折扣为" << book << std::endl;
    std::cin.clear();//拟用错误输入退出循环,clear()用以消除流的错误状态
    std::cin.ignore();//略过错误输入


    /* 练习1.21 读取两个ISBN相同的Sales_data对象,输出它们的和 */
    Sales_data trans1,trans2;
    std::cout << "请输入两条ISBN相同的销售记录:" << std::endl;
    std::cin >> trans1 >> trans2;
    if( compareIsbn( trans1, trans2 ) )
        std::cout << "汇总信息:ISBN、售出本数、原始价格、实售价格、折扣为"
                  << trans1 + trans2 << std::endl;
    else
        std::cout << "两条销售记录的ISBN不同!" << std::endl;

    std::cin.clear();
    std::cin.ignore();


    /*练习1.22 读取多个具有相同ISBN的销售记录,输出所有记录的和 */
    Sales_data total,trans;
    std::cout << "请输入几条相同ISBN的销售记录:" << std::endl;
    if( std::cin >> total )    //读入第一条记录
    {
        while( std::cin >> trans )
        {
            if( compareIsbn( total, trans ) )
                total += trans;
            else
            {
                std::cout << "当前的ISBN不同" << std::endl;
                break;
            }
        }
        std::cout << "汇总信息:ISBN、售出本数、原始价格、实售价格、折扣为"
                  << total << std::endl;
    }
    else
    {
        std::cout << "没有数据" << std::endl;
        return -1;
    }


    int num = 1;
    std::cout << "请输入若干销售记录:" << std::endl;
    if( std::cin >> trans1 )
    {
        while( std::cin >> trans2 )
        {
            if( compareIsbn( trans1, trans2 ) )
                num++;
            else
            {
                std::cout << trans1.isbn() << "共有"
                          << num << "条销售记录" << std::endl;
                trans1 = trans2;
                num = 1;
            }
        }
        std::cout << trans1.isbn() << "共有"
                  << num << "条销售记录" << std::endl;

    }
    else
    {
        std::cout << "没有数据" << std::endl;
        return -1;
    }
    return 0;
}


练习2.42:

略了。只是上题的类声明放在头文件里。



 







你可能感兴趣的:(C++primer习题)