C++ Primer 第三章 学习笔记及习题答案

知识点

string、vector、数组初始化:

  • string初始化:

    string s1; //默认初始化,s1为一个空串
    string s2(s1); string s2 = s1; //s2是s1的副本
    string s3("value"); //s3是字面值"value"的副本(除字符串字面值最后那个空字符) 
    string s4(n,'c'); //把s4初始化为由连续n个字符c组成的字符串
    
    tips:使用等号('=')初始化一个变量,执行的是拷贝初始化;如果不使用等号,直接初始化,使用的是直接初始化。
  • vector初始化:

    vector<T> v1; //v1是一个空vector,类型为T,执行默认初始化
    vector<T> v2(v1); vector<T> v2 = v1; //v2中包含有v1所有元素的副本
    vcetor<T> v3(n, val); //v3中包含n个重复的元素,每个元素值均为val
    vector<T> v4(n); //v4包含n个重复执行值初始化的对象
    vector<T> v5{a,b,c,..}; vector v5{a,b,c}; //v5包含初始值个数的元素,每个元素赋予相应值
    tips:
    · 列表初始化的形式可以确保不会发生类型转换
    · 学会区分花括号{}和圆括号()初始的不同;前后顺序:编译器会在确认无法执行列表初始化后,尝试用默认值初始化vector对象。
  • 数组初始化:

    T arr[d]; //数组声明方式(类型为T 名字为arr 含有d个元素),默认初始化,T不可用auto推断
    //显示初始化方式
    int a1[] {0,1,2};  //编译器根据初始值数量推测维度,等价于int a1[3] {0,1,2};
    init a2[5]{0,1,2}; //用提供的初始值初始化靠前元素,剩下元素被默认初始化
    
    //字符数组
    char a1[] {'c','+','+','\0'}; <==> 等价 char a2[] = "c++";
    //利用字符串字面值进行初始化时,'\0'保留(与string初始化相反)
    
    //数组不允许拷贝和赋值
    // int a1[] {0,1,2};
    // int a2[] = a1; //错误
    // a2 = a1; 错误

string、vector操作:

string操作含义vector操作含义
os << s将s写到输出流os当中vi.push_back(1)在vector对象尾端加入元素1
is >> s从is中读取字符串赋给s,遇到空白符停止读入
getline(is,s)从is中读取一行赋给s
s.empty()s为空返回true,否则返回falsevi.empty()如果v中不含有任何元素,返回true,否则返回false
s.size()返回s中字符的个数vi.size()返回v中元素个数
s[n]返回第n个字符的引用,从0开始记数vi[n]返回vi中第n个元素的引用
s1 + s2返回s1和s2连接后的结果
<, <=, >, >=利用字符在字典中的顺序进行比较,大小写敏感<, <=, >, >=利用字典顺序比较
while(cin>> s1)读取未知数量的string对象
  • tips:

    • string:

      • getline遇到换行符就结束读取操作并返回结果,触发getline函数的换行符会被丢弃,得到的string对象并不包含换行符
      • s.size()的返回类型为string::size_type(无符号类型的值),混用有符号和无符号数可能产生错误,因此如果一个表达式中size()和int不要混用
      • 当字面值和string对象相加时,必须保证每个加法运算符('+')两侧的运算对象至少有一个是string;
    • vector:

      • vector和string对象的下标运算符可用于访问已存在的元素,不能用于添加元素,vector用push_back()添加元素
  • string/vector/数组访问元素的方式:

    • 三种访问元素的方式:

      • 范围for:
      for(declaration: expression)
          statement
      // 如果想要改变string对象中字符的值,必须把循环变量定义成引用类型
      • 下标访问:注意下标不要越界,也不可使用下标访问空string
      • 迭代器访问
    • string和vector以上三种方式均可访问元素;数组可以利用范围for语句和下标访问,不可用迭代器访问

迭代器(string和vector对象均可用)

  • 迭代器有有效和无效之分,有效迭代器指向某个元素或者指向容器中尾元素的下一位置,其他所有情况均为无效。
  • 迭代器有begin和end成员:begin负责返回指向第一个元素(或第一个字符)的迭代器,end成员负责返回指向容器(或string对象)尾元素的下一位置,end成员返回的迭代器常被称为尾后迭代器(off-the-end iterator)。
  • 标准迭代器支持的运算:

    • *iter:返回迭代器iter所指元素的引用
    • iter->mem:解引用iter并获取该元素的名为mem的成员,等价于(*iter.mem)
    • ++iter:令iter指示容器中的下一个元素
    • --iter:令iter指示容器中的上一个元素
    • iter1 == iter2/iter1 != iter2:判断两个迭代器是否相等,如果两个迭代器指向的是同一个元素或者它们是同一个容器的尾后迭代器,则相等,否则不等
  • vector和string迭代器支持的运算(vector和string的迭代器在标准迭代器上进行扩充):

    • iter + n; //迭代器向前移动n个位置
    • iter - n; //迭代器向后移动n个位置
    • iter1 += n; //将iter1加n的结果赋给iter1
    • iter1 -= n; //将iter1减n的结果赋给iter1
    • iter1 - iter2; //两个迭代器相减的结果是它们的距离,返回类型为difference_type(带符号类型)
    • >, >=, <, <=; //如果迭代器指向的容器位置在另一个迭代器位置之前,则说前者小于后者
  • 迭代器类型:iterator和const_iterator(要在前面加上具体域,如string::iterator/vector<int>::const_iterator)。iterator的对象可读可写,const_iterator的对象只可读但不能修改它所指的元素值。为了得到const_iterator类型的返回值,c++11引入了cbegin和cend。
  • 箭头运算符(->):it->mem等价(*it).mem
  • 凡是使用了迭代器的循环体,不要向迭代器所属的容器添加元素
  • 养成循环判断中迭代器和!=一起使用的习惯

数组

  • 数组内的元素本身没有名字,通过其所在位置访问,数组大小确定不变,不能随意像数组中增加元素s
  • 指针和数组:

    • 在大部分情况下,使用数组类型的对象其实是使用一个指向该数组首元素的指针
    • 对数组的元素使用取地址符就可以得到该元素的指针

      string nums[] {"one", "two", "three"};
      string *p = &nums[0];
      string *p = nums; //等价于指向该数组首元素的指针
    • vector和string迭代器支持的运算,数组的指针都支持
    • 为了指针的使用更简单安全,可以将C++11中的begin和end函数作为数组的参数:

      int ia[]{0, 1, 2, 3, 4, 5, 6};
      int *beg = begin(ia);
      int *last = end(ia);
    • 解引用和指针:

      int ia[]{0, 1, 2, 3, 4, 5, 6, 7, 8};
      *(ia + 4); //*(ia+4)等价于ia[4]
      *ia + 4; //等价于ia[0]+4
  • tips:

    • 数组下标类型为size_t(无符号类型)
    • 使用auto推断数组时得到指向数组的指针 T*;使用decltype推断数组时得到同类型的数组:

      int arr[] {1, 2, 3};
      auto(arr) arr1; //arr1类型为int*
      decltype(arr) arr2 = {4, 5, 6}; //arr2类型为含有3个整数的数组
    • 两个指针相减为ptrdiff_t(带符号类型)
    • string和vector的下表必须是无符号类型,但数组的下标是内置的,不是无符号类型,可以处理负值

C风格字符串

  • 在C中,把字符串放在字符数组中并以空字符结束('0')
  • C风格字符串函数含义
    strlen(p)返回p的长度,空字符不计在内
    strcmp(p1, p2)比较p1和p2的相等性。如果p1 == p2,返回0;如果p1 > p2, 返回一个正值;如果p1 < p2,返回一个负值
    strcat(p1, p2)将p2附加到p1之后,返回1
    strcpy(p1,p2)将p2拷贝给p1,返回p1
  • 可以使用数组初始化vector,只需指明要拷贝区域的首元素地址和尾后地址即可;但不可以使用vector或数组初始化数组。

多维数组

  • 多维数组即数组的数组
  • 使用范围for语句处理多维数组时,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型,这样可以避免数组被自动转成指针。(auto arr[10]{1,2,3}; -> auto推断类型为int*)

练习题

3.1节练习

  • 练习3.1:使用恰当的using申明重做1.4.1节(第11页)和2.6.2节(第67页)练习。

    加入using namespace std;

3.2节练习

3.2.2节练习

  • 练习3.2:编写一段程序从标准输入中一次读入一整行,然后修改该程序使其一次读入一个词。

    读入一整行:
        //getline接受两个参数:输入流和string对象,每次读入一整行
        string s;
        while (getline(cin,s)) {
        cout << s << endl;
    }
    一次读入一个词:
        //使用cin读取遇到空格停止
        string s1;
        while (cin>>s1) {
        cout << s1 << endl;
    }
  • 练习3.3:请说明string类的输入运算符和getline函数分别是如何处理空白字符的。

    在执行读取操作时string类的输入运算符会自动忽略开头的空白后开始读取,并在遇到下一个空白字符时停止,
    getline函数从给定的输入流中读取字符直到遇到换行符\n为止(换行符也读取进来),然后把所读的内容存入到string对象中去(存的过程不包含换行符)。因此,string对象遇到换行符停止,且得到的string对象不包含换行符,在换行符出现前的空白字符均正常读取。
  • 练习3.4:编写一段程序读入两个字符串,比较其是否相等 并输出结果。如果不相等,输出较大的那个字符串。改写上述程序,比较输入的两个字符串是否等长,如果不等长,输出长度较大的那个字符串。

    判断字符串大小:
    string s, s1;
    cin >> s >> s1;
    if (s1 >= s)
        cout << s1;
    else
        cout << s;
    
    判断字符串等长:
    string s, s1;
    cin >> s >> s1;
    if (s1.size() == s.size())
        cout << "the length of two strings is equal.";
    else if (s1.size() >= s.size())
        cout << s1;
    else
        cout << s;
    
  • 练习3.5:编写一段程序从标准输入中读入多个字符串并将他们连接在一起,输出连接成的大字符串。然后修改上述程序,用空格把输入的多个字符串分隔开来。

    连接成大字符串:
    string s, s1;
    while (cin >> s) {
        s1 += s;
    }
    cout << s1;
    
    用空格分割:
    string s, s1;
    while (cin >> s) {
        s1 += s + ' ';
    }
    cout << s1;

3.2.3节练习

  • 练习3.6:编写一段程序,使用范围for语句将字符串内所有的字符用X代替。

    string s, s1;
    while (cin >> s) {
        for (auto &c : s) //传引用!保证可以修改字符串中的值,而不是拷贝出来赋值
            c = 'X';
    }
    cout << s << endl;
    
  • 练习3.7:就上一题完成的程序而言,如果将循环控制变量的类型设为char将发生什么?先估计一下结果,然后实际编程进行验证。

    没有变化,字符串的每一位中存放的为字符(char)类型
  • 练习3.8:分别用while循环和传统的for循环重写第一题的程序,你觉得哪种形式更好呢?为什么?

    while循环:    
    string s, s1;
    cin >> s;
    int len = s.size();
    while (((--len) >= 0)) {
        s[len] = 'X';
    }
    cout << s << endl;
    
    for循环:
    string s, s1;
    cin >> s;
    int len = s.size();
    for (int i = 0; i != len; ++i)
        s[i] = 'X';
    cout << s << endl;
    
    for循环可读性好些
  • 练习3.9:下面的程序有何作用?它合法吗?如果不合法,为什么?

    string s;
    cout << s[0] <<endl;
    
    不合法,s为空字符串,使用下标访问空string会引发不可预知的错误。
  • 练习3.10:编写一段程序,读入一个包含标点符号的字符串,将标点符号去除后输出字符串剩下的部分。

    string s, s1;
    cin >> s;
    for (auto c : s) {
        if (!(ispunct(c)))
            s1 += c;
    }
    cout << s1 <<endl;
  • 练习3.11:下面的范围for语句合法吗?如果合法,c的类型是什么?

    const string s = "Keep out!";
    for (auto c : s) { /* ... */ }
    
    合法,c的类型为char,因为auto会忽略顶层const。

3.3节练习

3.3.1节练习

  • 练习3.12:下列vector对象的定义有不正确的吗?如果有,请指出来。对于正确的,描述其执行结果,对于不正确的,说明其错误的原因。

    (a) vector<vector<int>> ivec;
    (b) vector<string> svec = ivec;
    (c) vector<string> svec(10, "null");
    
    (a) 正确,创建一个vector,该vector的元素是int型的vector对象 
    (b) 错误,ivec和svec对象类型不同,svec的元素是string对象,ivec的元素是int型的vector对象 
    (c) 正确,创建一个vector,svec的元素是string对象,并对其初始化,svec含有10个元素,每个的值都是"null"
  • 练习3.13:下列的vector对象各包含多少个元素?这些元素的值分别是多少?

    (a) vector<int> v1;
    (b) vector<int> v2(10);
    (c) vector<int> v3(10,42);
    (d) vector<int> v4{10};
    (e) vector<int> v5{10,42};
    (f) vector<string> v6{10}; 
    (g) vector<string> v7{10,"hi"};
    
    (a) v1是一个空vector,不含任何元素;
    (b) v2是一个含有10个元素的vector,每个元素的值被默认初始化为0
    (c) v3是一个含有10个元素的vector,每个元素的值为42
    (d) v4是一个含有1个元素的vector,该元素值为10
    (e) v5是一个含有2个元素的vector,元素值分别为10,42
    (f) v6是一个含有1个元素的vector,v6的元素是string对象,值为null
    (g) v7是一个含有10个元素的vector,v6的元素是string对象,每个元素的值为"hi"
    
    tips:v7的花括号中提供的元素值不能作为元素的初始值,在确认无法执行列表初始化后,编译器会尝试用默认值初始化vector对象。
    (g) vector<string> v7{10,"hi"}; -> 无法列表初始化,等价于 <=> vector<string> v7(10,"hi");
    

3.3.2节练习

  • 练习3.14:编写一段程序,用cin读入一组整数并把它们存入一个vector对象。

    int n;
    vector<int> ivec;
    while (cin >> n) {
        ivec.push_back(n);
    }
  • 练习3.15:改写上题的程序,不过这次读入的是字符串。

    string s;
    vector<string> svec;
    while (cin >> s) {
        svec.push_back(s);
    }

3.3.3节练习

  • 练习3.16:编写一段程序,把练习3.13中vector对象的容量和具体内容输出出来。检验你之前的回答是否正确,如果不对,回过头重新学习3.3.1节(第87页)知道弄明白错在何处为止。

    vector<int> v1;
    vector<int> v2(10);
    vector<int> v3(10, 42);
    vector<int> v4{ 10 };
    vector<int> v5{ 10,42 };
    vector<string> v6{ 10 };
    vector<string> v7{ 10,"hi" };
    
    cout << "v1" << endl;
    for (auto c : v1)
        cout << c << endl;
    
    cout << "v2" << endl;
    for (auto c : v2)
        cout << c << endl;
    
    cout << "v3" << endl;
    for (auto c : v3)
        cout << c << endl;
    
    cout << "v4" << endl;
    for (auto c : v4)
        cout << c << endl;
    
    cout << "v5" << endl;
    for (auto c : v5)
        cout << c << endl;
    
    cout << "v6" << endl;
    for (auto c : v6)
        cout << c << endl;
    
    cout << "v7" << endl;
    for (auto c : v7)
        cout << c << endl;
    
    检验后输出结果正确,注意v6中string对象默认初始化为null
  • 练习3.17:从cin读入一组词并把它们存入一个vector对象,然后设法把所有词都改写成大写形式。输出改变后的结果,每个词占一行。

    string s;
    vector<string> svec;
    while (cin >> s) {
        for (auto &c : s) { //注意传引用
            c = toupper(c); //将改为大写的char赋值给原char
        }
        svec.push_back(s);
    }
    for (int i = 0; i != svec.size(); ++i)
        cout << svec[i] << endl;
  • 练习3.18:下面的程序合法吗?如果不合法,你准备如何修改?

    vector<int> ivec;
    ivec[0] = 42;
    
    不合法,因为ivec为空vector,vector的下标只能用于访问已存在的元素,且不能使用下标形式添加元素,只能使用push_back添加
    
    改为:
    vector<int> ivec;
    ivec.push_back(42);
  • 练习3.19:如果想定义一个含有10个元素的vector对象,所有元素的值都是42,请列举出三种不同的实现方法。那种方法更好呢?为什么?

    vector<int> ivec(10,42);
    
    vector<int> ivec{42,42,42,42,42,42,42,42,42,42};
    
    vector<int> ivec;
    for(int i = 0; i < 10; ++i)
        ivec.push_back(42);
    
    使用圆括号形式初始化固定大小且元素值相同的vector最方便。
  • 练习3.20:读入一组整数并把它们存入一个vector对象,将每对相邻整数的和输出出来。改写你的程序,这次要求先输出第1个和最后1个元素的和,接着输出第2个和倒数第2个元素的和,以此类推。

    //输出相邻整数
    int n;
    vector<int> ivec;
    while (cin >> n) {
        ivec.push_back(n);
    }
    for (int i = 1; i != ivec.size(); ++i)
        cout << ivec[i - 1] + ivec[i] << " ";
    
    //输出首尾元素和
    int n;
    vector<int> ivec;
    while (cin >> n) {
        ivec.push_back(n);
    }
    int len = ivec.size();
    for (int i = 0; i != (len / 2) + 1; ++i)
        cout << ivec[i] + ivec[len - i - 1] << " ";//下标千万不要越界

3.4节练习

3.4.1节练习

  • 练习3.21:请使用迭代器重做3.3.3节(第94页)的第一个练习。

    // 使用!=进行for循环的判断,在标准库提供的所有容器均有效,
    // 养成使用迭代器和!=的习惯,就不用太在意用的到底是哪种容器类型
    vector<int> v1;
    vector<int> v2(10);
    vector<int> v3(10, 42);
    vector<int> v4{ 10 };
    vector<int> v5{ 10,42 };
    vector<string> v6{ 10 };
    vector<string> v7{ 10,"hi" };
    
    cout << "v1" << endl;
    for (auto it = v1.begin(); it != v1.end(); ++it)
        cout << *it << endl;
    
    cout << "v2" << endl;
    for (auto it = v2.begin(); it != v2.end(); ++it)
        cout << *it << endl;
    
    cout << "v3" << endl;
    for (auto it = v3.begin(); it != v3.end(); ++it)
        cout << *it << endl;
    
    cout << "v4" << endl;
    for (auto it = v4.begin(); it != v4.end(); ++it)
        cout << *it << endl;
    
    cout << "v5" << endl;
    for (auto it = v5.begin(); it != v5.end(); ++it)
        cout << *it << endl;
    
    cout << "v6" << endl;
    for (auto it = v6.begin(); it != v6.end(); ++it)
        cout << *it << endl;
    
    cout << "v7" << endl;
    for (auto it = v7.begin(); it != v7.end(); ++it)
        cout << *it << endl;
  • 练习3.22:修改之前那个输出text第一段的程序,首先把text的第一段全都改成大写形式,然后再输出它。

    vector<string> text;
    string str;
    while (cin >> str) {
        text.push_back(str);
    }
    for (auto it = text.begin(); it != text.end() && !it->empty(); ++it)    {
        for (auto &c : *it)
            c = toupper(c);
        cout << *it << endl;
    }
  • 练习3.23:编写一段程序,创建一个含有10个整数的vector对象,然后使用迭代器将所有元素的值都变成原来的两倍。输出vector对象的内容,检验程序是否正确。

    vector<int> ivec;
    int n;
    while(cin>>n)
        ivec.push_back(n);
    for (auto it = ivec.begin(); it != ivec.end(); ++it) {
        *it *= 2;
        cout << *it << " ";
    }    
    

3.4.2节练习

  • 练习3.24:请使用迭代器重做3.3.3节(第94页)的最后一个练习。

    // 输出相邻整数和
    vector<int> ivec;
    int n;
    while(cin>>n)
        ivec.push_back(n);
    for (auto it = ivec.begin() + 1; it != ivec.end(); ++it) {
        cout << *it + *(it-1) << " ";
    }
    
    // 输出首尾相加和
    // 学会尾部迭代器的使用
    vector<int> ivec;
    int n;
    while(cin>>n)
        ivec.push_back(n);
    auto  beg = ivec.begin();
    auto  end = ivec.end() - 1;
    for (; beg <= end; ++beg, --end) {
        cout << *beg + *end << " ";
    }
  • 练习3.25:3.3.3节(第93页)划分分数段的程序是使用下标运算符实现的,请利用迭代器改写该程序并实现完全相同的功能。

    vector<int> ivec(11);
    int n;
    while (cin >> n) {
        n /= 10;
        ++ivec[n];
    }
    for (auto it = ivec.begin(); it != ivec.end(); ++it ) 
        cout << *it << " ";
  • 练习3.26:在100页的二分搜索程序中,为什么用的是mid = beg + (end - beg) / 2,而非mid = (beg + end) / 2?

    迭代器不支持两个迭代器相加的运算,但支持迭代器加上一个整数的运算
    (end - beg) / 2 两个迭代器相减返回difference_type,是带符号整型数,可以与迭代器相加。

3.5.1节练习

  • 练习3.27:假设txt_size是一个无参数的函数,它的返回值是int。请问下列哪个定义是非法的?为什么?

    unsigned buf_size = 1024;
    (a) int ia[buf_size];
    (b) int ia[4 * 7 - 14];
    (c) int ia[txt_size()];
    (d) char st[11] = "fundamental";
    
    (a) 非法,buf_size为非常量表达式
    (b) 合法
    (c) 若text_size()为常量表达式(constexpr)时,在编译时确定具体指并替换,则正确;否则错误
    (d) 非法,溢出,字符数组利用字符串字面值进行初始化时'\0'会包含
  • 练习3.28:下列数组中的元素值是什么?

    string sa[10];
    int ia[10];
    int main(){
        string sa2[10];
        int ia2[10];
    }
    sa和ia位于定义于任何函数体之外,sa为空字符串;ia数组内每个值均初始化为0。
    sa2和ia2在函数体内部,srting类规定没有指定初值则声称一个空串;ia2不被初始化,其中的值未定义。
    
    综上,sa和sa2初始化为空串;ia数组内每个值均为0,ia2中值未定义。
  • 练习3.29:相比vector,数组有哪些缺点,请列举一些。

    数组没有vector灵活,数组的大小确定不变,不能随意向数组内增加元素。

3.5.2节练习

  • 练习3.30:指出下面代码中的索引错误。

    constexpr size_t array_size = 10;
    int ia[array_size];
    for (size_t ix = 1; ix <= array_size; ++ix)
        ia[ix] = ix;
    
    int ia[10]中存放ia[0]~is[9]的值,for循环中访问了ia[10],导致下标越界。
  • 练习3.31:编写一段程序,定义一个含有10个int的数组,令每个元素的值就是其下标值。

    int a[10];
    for(int i = 0; i < 10; ++i)
        a[i] = i;
  • 练习3.32:将上一题刚刚创建的数组拷贝给另外一个数组。利用vector重写程序,实现类似的功能。

    //数组不允许直接拷贝
    int b[10];
    for(int i = 0; i < 10; ++i)
        b[i] = a[i];
    
    vector<int> vi;
    for (int i = 0; i < 10; ++i)
        vi.push_back(a[i])
  • 练习3.33:对于104页的程序来说,如果不初始化scores将发生什么。

    不初始化scores的话,其内部的值是未定义的,无法实现利用下标统计人数段的功能。

3.5.3节练习

  • 练习3.34:假定p1和p2指向同一个数组中的元素,则下面程序的功能是什么?

    p1 += p2 - p1;
    
    两个指针相减的结果是它们之间的距离,p2 - p1为类型为ptrdiff_t,值为0的数。p1加上这个数指针不变。
    上面程序的功能是p1经过运算后指向元素不变
  • 练习3.35:编写一段程序,利用指针将数组中的元素置为0。

    int a[10];
    for (int i = 0; i < 10; ++i)
        a[i] = i;
    int *beg = &a[0];
    int *end = &a[10];
    for (auto it = beg; it != end; ++it) {
        *it = 0;
        cout << *it << " ";
    }
  • 练习3.36:编写一段程序,比较两个数组是否相等。再编写一段程序,比较两个vector对象是否相等。

    //判断数组是否相等
    int arr1[]{ 0,1,2,3,4 };
    int arr2[]{ 0,1,2,3,6 };
    auto *beg1 = begin(arr1);
    auto *last1 = end(arr1);
    auto *beg2 = begin(arr2);
    auto *last2 = end(arr2);
    auto n1 = *last1 - *beg1;
    auto n2 = *last2 - *beg2;
    decltype(n1) acount = 0;
    if (n1 == n2) {
        for (; beg1 != last1, beg2 != last2; ++beg1, ++beg2); {
            if (beg1 != beg2) {
                cout << "The two array have same size, but they are not equal." << endl;
            }
            if (beg1 == beg2) {
                ++acount;
            }
        }
        if (acount == n1)
            cout << "The two array are equal." << endl;
    }
    else
        cout << "The two array are not equal." << endl;
    
    //判断vector是否相等
    vector<int> v1{ 0,1,2,3,4 };
    vector<int> v2{ 0,1,2,4,3 };
    int vcount = 0;
    if (v1.size() == v2.size()) {
        for (auto i = 0; i != v1.size(); ++i) {
            if (v1[i] != v2[i]) {
                cout << "The two vector have same size, but they are not equal." << endl; break;
            }
            else
                ++vcount;
        }
        if (vcount == v1.size())
            cout << "The two vector are equal." << endl;
    }
    else
        cout << "The two vector are not equal." << endl;
    

3.5.4节练习

  • 练习3.37:下面的程序是何定义,程序的输出结果是什么?

    const char ca[] = {'h', 'e', 'l', 'l', 'o'};
    const char *cp = ca;
    while(*cp){
        cout << *cp <<endl;
        ++cp;
    }
    
    程序输出会报错,ca为一个c风格的字符串,必须以'\0'结束,
    正确写法为const char ca[] = {'h', 'e', 'l', 'l', 'o', '\0'};
    这样会依次输出字符串每一位(除\0结束符),即输出 h e l l o
  • 练习3.38:在本节中我们提到,将两个指针相加不但是非法的,而且也没什么意义。请问为什么两个指针相加没什么意义?

    指针表示地址值,两个地址相加没有实际意义
  • 练习3.39:编写一段程序,比较两个string对象。再编写一段程序,比较两个C风格字符串的内容。

    //比较两个string对象
    string s1 = "abcde";
    string s2 = "abded";
    if (s1 > s2)
        cout << "s1 is bigger than s2." << endl;
    else if (s1 < s2)
        cout << "s1 is smaller than s2." << endl;
    else
        cout << "s1 and s2 are equal." << endl;
    
    //比较两个C风格字符串
    const char ca1[] = "abcde";
    const char ca2[] = "abced";
    if (strcmp(ca1, ca2) > 0)
        cout << "ca1 is bigger than ca2." << endl;
    else if (strcmp(ca1, ca2) < 0)
        cout << "ca1 is smaller than ca2." << endl;
    else
        cout << "ca1 and ca2 are equal." << endl;
    
  • 练习3.40:编写一段程序,定义两个字符数组并用字符串字面值初始化它们;接着再定义一个字符数组存放前两个数组连接后的结果。使用strcpy和strcat把两个数组的内容拷贝到第三个数组。

    //注意要给出被操作数组的具体大小
    char ca1[100] = "abcde";
    const char ca2[] = "feagr";
    char ca3[100] = "";
    strcat_s(ca1, ca2);
    strcpy_s(ca3, ca1);
    cout << ca3 << endl;
    

3.5.5节练习

  • 练习3.41:编写一段程序,用整型数组初始化一个vector对象。

    int arr[]{1, 2, 3, 4, 5};
    vector<int> vi(begin(arr), end(arr));
  • 练习3.42:编写一段程序,将含有整数元素的vector对象拷贝给一个整型数组。

    //使用begin和end函数得到数组首指针和尾后指针
    vector<int> vi{ 1,2,3,4,5 };
    int arr[5];
    int *beg = begin(arr);
    int *last = end(arr);
    for (auto i : vi) {
        *beg = i;
        ++beg;
    }

3.6节练习

  • 练习3.43:编写3个不同版本的程序,令其均能输出ia的元素。版本1使用范围for语句管理迭代过程;版本2和版本3都使用普通的for语句,其中版本2要求用下标运算符,版本3要求用指针。此外,在所有3个版本的程序中都要直接写出数据类型,而不能使用类型别名、auto关键字和decltype关键字。

    int ia[3][4]{ {0,1,2,3},
              {4,5,6,7},
              {8,9,10,11} };
    //version 1
    for (int (&row)[4] : ia) {
        for (int col : row) {
            cout << col << " ";
        }
        cout << endl;
    }
    
    //version 2
    for (int i = 0; i != 3; ++i) {
        for (int j = 0; j != 4; ++j) {
            cout << ia[i][j] << " ";
        }
        cout << endl;
    }
        
    //version 3
    for (int (*p)[4] = ia; p != ia + 3; ++p) {
        for (int *q = *p; q != *p + 4; ++q) {
            cout << *q << " ";
        }
        cout << endl;
    }
    
  • 练习3.44:改写上一个练习中的程序,使用类型别名来代替循环控制变量的类型。

    int ia[3][4]{ {0,1,2,3},
              {4,5,6,7},
              {8,9,10,11} };
    //version 1
    using elem1_1 = int[4];
    using elem1_2 = int;
    for (elem1_1 &row : ia) {
        for (elem1_2 col : row) {
            cout << col << " ";
        }
        cout << endl;
    }
    
    //version 2
    using elem2 = int;
    for (elem2 i = 0; i != 3; ++i) {
        for (elem2 j = 0; j != 4; ++j) {
            cout << ia[i][j] << " ";
        }
        cout << endl;
    }
        
    //version 3
    using elem3_1 = int(*)[4];
    using elem3_2 = int*;
    for (elem3_1 p = ia; p != ia + 3; ++p) {
        for (elem3_2 q = *p; q != *p + 4; ++q) {
            cout << *q << " ";
        }
        cout << endl;
    }
    
  • 练习3.45:再一次改写程序,这一次使用auto关键字。

    int ia[3][4]{ {0,1,2,3},
              {4,5,6,7},
              {8,9,10,11} };
    //version 1
    for (auto &row : ia) {
        for (auto col : row) {
            cout << col << " ";
        }
        cout << endl;
    }
    
    //version 2
    for (auto i = 0; i != 3; ++i) {
        for (auto j = 0; j != 4; ++j) {
            cout << ia[i][j] << " ";
        }
        cout << endl;
    }
        
    //version 3
    for (auto p = ia; p != ia + 3; ++p) {
        for (auto q = *p; q != *p + 4; ++q) {
            cout << *q << " ";
        }
        cout << endl;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值