*****代码在Debian g++ 5.40 / clang++ 3.8(C++11)下编写调试*****

本章主要是关于字符串、数组的内容,以及一些简单的容器知识。

1.using的声明

using关键字有2种用法:

using std::cin;       //这种叫做using声明,注意分号

using namespace std;         //这种叫做using指示,注意分号

两者的区别是一个using声明一次只引入一个std的成员,而using指示则将整个命名空间的所有成员都引入。

头文件不要包含using声明,否则会导致引用了头文件的文件中包含using声明,产生始料未及的冲突。

虽然直接打开/usr/include/c++/5/iostream头文件看到里面的namespace std只包含了几个对象名,但并不全,因为命名空间可以分离不连续,在多处定义不同的部分。

2.标准库类型string

使用string类型要包含string头文件,string是定义在命名空间std中的。

C++string类的对象初始化的几种方式,如下:

string s1;          //默认初始化

string s2 = s1;
 
string s3 = "hiya";
 
string s4("vlaue");
 
string s5(10, 'c');     //s5 = cccccccccc 初始化为10个c
 
拷贝初始化使用等号“ = ”,直接初始化不用等号,上述s2、s3是拷贝初始化,s4、s5是直接初始化。
 
string对象可以使用字符串字面值进行初始化,但string对象并不包括字符串字面值末尾的空字符'\0'。
 
string对象支持的操作
 1. os<<s  将s写到输出流os当中,返回os 
 2. is>>s 从is中读取字符串赋给s,字符串以空白字符分割,返回is 
 3. getline(is,s) 从is中读取一行赋给s,返回is 
 4. s.empty() s为空返回true,否则返回false 
 5. s.szie() 返回s中字符的个数 
 6. s[n] 返回s中第n个字符的引用,位置从0开始计数 
 7. s1+s2 返回s1和s2连接后的副本 
 8. s1=s2 用s2的副本代替s1中原来的字符
 9. s1==s2 如果s1和s2中所含的字符完全一样,则他们相等,否则
 10. s1!=s2 不相等,string对象的相等性判断对字母大小写敏感 
 11.<,<=,>,>=  利用字符在字典中的顺序进行比较,且对字母的大小写敏感
 
从标准输入流提取内容到string对象时,会自动忽略开头和结尾的空白字符(包括空格、换行、制表符等),要保留输入时的空白字符,需要使用getline来进行读写。
 
string对象允许做加法运算,可以在string对象之间相加,也可以在string对象和字符字面值或字符串字面值之间相加。
 
string类型也支持下标运算符[ ]运算

string类型可以做加法运算,该加法运算的对象可以是两个string对象,也可以是一个string对象和一个字符串字面值,但是不能两个字符串字面值进行加法运算后赋值给string对象,因为字符串字面值直接并没有加法运算。

vector是一种容器,是同种类型对象的有序集合,集合中的元素可以通过下标索引来进行访问。

要使用vector必须包含相应的头文件,另外vector只是一个模板的名字,并非一个具体的类或函数,模板需要提供一些信息来实例化。(这里的vector有多种含义,比如容器类型,模板类)

如书本之前提到的:如何初始化类的对象是由类本身决定的。一个类可以定义很多种初始化对象的方式,只不过这些方式之间必须有所区别:或者是初始值的数量不同,或者是初始值的类型不同。这里的vector类似的具有多种初始化对象的方式:

  1. vector<T> v1;      //默认初始化
  2. vector<T> v2(v1);    //拷贝初始化
  3. vector<T> v3 = v2;    //拷贝初始化
  4. vector<T> v4(n, val);  //制定初始容器元素个数,并将每个元素值指定为val
  5. vector<T> v5(n);     //指定初始容器元素个数
  6. vector<T> v6{a, b, c...};  //列表初始化
  7. vector<T> v6 = {a, b, c...};//列表初始化

定义了一个vector之后,可以使用push_back方法来向其中添加元素,不能直接通过下标索引来向vector添加元素

vector除了push_back操作之外,还提供其他的操作,比较重要的如下

 v.empty()  v为空返回真,否则返回假
v.size()   返回v的元素个数
v.push_back(t)  向v末尾添加一个值为t的元素 
v[n]  访问第n个位置上的元素,位置从0开始计数
v1=v2  用v2副本替换v1 
v1={a,b,c...}  用列表中的元素替换给v1 
v1==v2  比较v1和v2是否相等 
v1!=v2  比较v1和v2是否不等 
 <,<=,>,>= 比较运算 

对于string对象或者vector对象可以使用下表索引来访问其中的元素,还有一种方法也能访问元素,该方法就是迭代器。

所有标准库的容器都有迭代器,string不属于容器,但也可以使用迭代器。

迭代器是对对象的间接访问,迭代器也有有效和无效之分。

另外迭代器可以进行移动。可以使用标准库容器或者string的begin和end方法来获取相应的迭代器。内置数组不具有begin和end方法,可以使用标准库的begin和end运算符来获取。begin和end运算符类似于sizeof运算符。

const vector和vector成员是const的,以及第6章的initializer_list

迭代器

内置数组在很多方面与vector不同,因此vector的方法不适用于内置数组。

1.数组不能直接复制

2.数组在定义时必须指明大小,且该大小再也无法改变(g++/clang++支持c99的扩展特性,允许编译时不指明数组大小,但该特性非c++标准,可以在编译时使用-pedantic选项来查看 )

3.数组定义时不允许使用auto关键字进行推断

4.字符数组允许使用字符串字面值来初始化

5.数组也没有begin,end,size,empty等方法

指针和数组之间有非常密切的联系。很多情况下,使用数组名字时,编译器会自动将其转换为其首元素地址的指针,因此一些情况下数组的操作是指针的操作。

当使用auto去推断数组作为变量的初始值的时候,该变量的类型是指针,而非数组,对于多维数组,则是第一维的数组指针,例如

  1. int array[][] = {};
  2. auto p = array //p是array[4],而不是array[0][0]

这里的p是指向含有4个int元素的数组指针,而非指向一个int元素的指针

指针也是一种迭代器,因此迭代器所支持的操作,指针也全部支持,但指针还拥有更多的功能。如前所述,一些情况下数组的操作是指针的操作,比如有时使用数组的名字时其实用的是一个指向数组首元素的指针,因此对数组执行下标运算符其实是对指向数组元素的指针执行下标运算:

  1. int ia[] = {, , , , }; //ia转换成指向数组首元素指针
  2. int i = ia[]; //ia[2]得到(ia+2)所指的元素
  3. int *p = ia;  //p指向ia的首元素
  4. i = *(p + ); //等价于i=ia[2]
  5. int *p = &ia[]; //p指向索引为2的元素
  6. int j = p[]; //p[1等价于*(p+1),就是ia[3]表示的那个元素
  7. int k = p[-]; //p[-2]是ia[0]表示的那元素

与标准库的string和vector类型不同,虽然这些都支持下标运算符,但内置的数组下标运算符支持负数索引,而标准库的下标索引则是无符号类型,无法处理负值。

C风格字符串:

1. C风格字符串并不是一种类型,是一种约定的写法,表示方法是将字符串放在字符数组中,且在数组中以'\0'作为终结符,一般是使用指针来操作这些字符串。

2. 字符串字面值是一串常量字符,字符串字面值常量用双引号括起来的零个或多个字符表示,为兼容C语言,C++中所有的字符串字面值都由编译器自动在末尾添加一个空字符。字符串是没有变量名字的,自身表示自身。

多维数组:

关于多维数组,在C++中其实并不存在,多维数组其实是数组的数组,理解多维数组是数组的数组有利于对多维数组使用auto和指针时的理解。

使用多维数组的名字时,也会将数组名转换为指向数组首元素的指针。因为多维数组实际上是数组的数组,因此多维数组名转换得到的指针是指向第一个内层数组的指针。

  1. int ia[][];
  2. int (*p)[] = ia;
  3. p = &ia[];
  4. auto p = ia; //等价于int (*p)[4]=ia;

练习3.1:使用恰当的using声明重做1.4.1节(第13页)和2.6.2节(第76页)的练习。

1.

  1. #include <iostream>
  2.  
  3. using std::cout;
  4. using std::endl;
  5.  
  6. int main()
  7. {
  8. int sum = ;
  9. for (int i = ; i <= ; i++)
  10. sum += i;
  11. cout << "Sum of 50 to 100 inclusive is " << sum << endl;
  12. return ;
  13. }

2.

  1. #include <iostream>
  2.  
  3. using std::cout;
  4. using std::endl;
  5.  
  6. int main()
  7. {
  8. for (int i = ; i >= ; i--)
  9. cout << i << endl;
  10. return ;
  11. }

3.

  1. #include <iostream>
  2.  
  3. using std::cin;
  4. using std::cout;
  5. using std::endl;
  6.  
  7. int main()
  8. {
  9. int a, b;
  10. cout << "Enter two numbers:" << endl;
  11. for ( cin >> a >> b; a <= b; a++)
  12. cout << a << " ";
  13. return ;
  14. }

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

  1. #include <iostream>
  2.  
  3. using namespace std;
  4.  
  5. int main()
  6. {
  7. string line;
  8. getline(cin, line);
  9.  
  10. string word;
  11. cin >> word;
  12. return ;
  13. }

练习3.3:请说明string类的输入运算符和getline函数分别是如何处理空白字符的。

string类一但遇到空白字符就停止读入,而getline一旦遇到回车符则停止读入

练习3.4:编写一段程序读入两个字符串,比较其是否相等并输出结果。如果不相等,输出较大的那个字符串。改写上述程序,比较输入的两个字符串是否等长,如果不等长,输出长度较大的那个字符串。

  1. #include <iostream>
  2. #include <string>
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. string st1, st2;
  9. cout << "input two strings:";
  10. cin >> st1 >> st2;
  11. if (st1 == st2)
  12. cout << st1 << " = " << st2 << endl;
  13. else
  14. {
  15. if (st1 > st2)
  16. cout << st1 << endl;
  17. else
  18. cout << st2 << endl;
  19. }
  20.  
  21. if (st1.size() > st2.size())
  22. cout << st1 << endl;
  23. else
  24. cout << st2 << endl;
  25. return ;
  26. }

练习3.5:编写一段程序从标准输入中读入多个字符串并将它们连接在一起,输出连接成的大字符串。然后修改上述程序,用空格把输入的多个字符串分隔开来。

  1. #include <iostream>
  2. #include <string>
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. string st1, st2;
  9. while (cin >> st1)
  10. st2 += st1;
  11. cout << st2 << endl;
  12.  
  13. while (cin >> st1)
  14. st2 = st2 + st1 + " ";
  15. cout << st2 << endl;
  16.  
  17. return ;
  18. }

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

  1. #include <iostream>
  2. #include <string>
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. string str;
  9. cin >> str;
  10. if (!str.empty())
  11. for (auto &i : str)
  12. i = 'X';
  13. return ;
  14. }

练习3.7:就上一题完成的程序而言,如果将循环控制变量的类型设为char将发生什么?先估计一下结果,然后实际编程进行验证。

不会发生什么,结果和auto是相同的

练习3.8:分别用while循环和传统的for循环重写第一题的程序,你觉得哪种形式更好呢?为什么?

  1. #include <iostream>
  2. #include <string>
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. string str;
  9. cin >> str;
  10. string::size_type i = ;
  11. if (!str.empty())
  12. while (i < str.size())
  13. {
  14. str[i] = 'X';
  15. ++i;
  16. }
  17.  
  18. //for loop
  19. if (!str.empty())
  20. for (string::size_type i = ; i < str.size(); ++i)
  21. {
  22. str[i] = 'X';
  23. }
  24. return ;
  25. }

三种循环相比,最好的形式是范围for循环,简便易用.

练习3.9:下面的程序有何作用?它合法吗?如果不合法,为什么?

  1. string s;
  2. cout << s[] << endl;

不合法,因为此时s并没有元素,s[0]是非法访问,其行为是未定义的.

练习3.10:编写一段程序,读入一个包含标点符号的字符串,将标点符号去除后输出字符串剩余的部分。

  1. #include <iostream>
  2. #include <string>
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. string str, result;
  9. cin >> str;
  10. if (!str.empty())
  11. for (auto &i : str)
  12. {
  13. if (!ispunct(i))
  14. result += i;
  15. }
  16. cout << result << endl;
  17. return ;
  18. }

练习3.11:下面的范围for语句合法吗?如果合法,c的类型是什么?

  1. const string s = "Keep out!";
  2. for(auto &c : s){ /* ... */ }

合法,此时的auto能推断出顶层const,因此c将是一个常量,c的类型是一个const char.

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

(a) vector<vector<int>> ivec;    //正确,定义了一个空的int vector

(b) vector<string> svec = ivec;   //错误,ivec和svec的类型不匹配

(c) vector<string> svec(10, "null");  //正确,定义了一个含有10个字符串为"null"的string元素的vector

练习3.13:下列的vector对象各包含多少个元素?这些元素的值分别是多少?

(a) vector<int> v1;        //没有包含元素

(b) vector<int> v2(10);      //包含10个元素,所有的元素值为0

(c) vector<int> v3(10, 42);     //包含10个元素,所有的元素值为42

(d) vector<int> v4{10};      //包含1个元素,值为10

(e) vector<int> v5{10, 42};    //包含2个元素,值为10和42

(f) vector<string> v6{10};     //包含10个元素,所有的元素值为空字符串

(g) vector<string> v7{10, "hi"};  //包含10个元素,所有的元素值为字符串“hi”

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

  1. #include <iostream>
  2. #include <vector>
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. int i;
  9. vector<int> v;
  10. while (cin >> i)
  11. v.push_back(i);
  12.  
  13. return ;
  14. }

练习3.15:改写上题的程序,不过这次读入的是字符串。

  1. #include <iostream>
  2. #include <vector>
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. string s;
  9. vector<string> v;
  10. while (cin >> s)
  11. v.push_back(s);
  12.  
  13. return ;
  14. }

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

  1. #include <iostream>
  2. #include <vector>
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. vector<int> v1; //没有包含元素
  9. for (auto i : v1)
  10. cout << i << '\t';
  11. cout << "\n============\n";
  12. vector<int> v2(); //包含10个元素,所有的元素值为0
  13. for (auto i : v2)
  14. cout << i << '\t';
  15. cout << "\n============\n";
  16. vector<int> v3(, ); //包含10个元素,所有的元素值为42
  17. for (auto i : v3)
  18. cout << i << '\t';
  19. cout << "\n============\n";
  20. vector<int> v4{}; //包含1个元素,值为10
  21. for (auto i : v4)
  22. cout << i << '\t';
  23. cout << "\n============\n";
  24. vector<int> v5{, }; //包含2个元素,值为10和42
  25. for (auto i : v5)
  26. cout << i << '\t';
  27. cout << "\n============\n";
  28. vector<string> v6{}; //包含10个元素,所有的元素值为空字符串
  29. for (auto i : v6)
  30. cout << i << '\t';
  31. cout << "\n============\n";
  32. vector<string> v7{, "hi"};  //包含10个元素,所有的元素值为字符串“hi”
  33. for (auto i : v7)
  34. cout << i << '\t';
  35. cout << "\n============\n";
  36. return ;
  37. }

练习3.17:从cin读入一组词并把它们存入一个vector对象,然后设法把所有词都改写为大写形式。输出改变后的结果,每个词占一行。

  1. #include <iostream>
  2. #include <vector>
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. string s;
  9. vector<string> v;
  10. while (cin >> s)
  11. v.push_back(s);
  12. for (auto &i : v)
  13. {
  14. for (auto &ii : i)
  15. ii = toupper(ii);
  16. cout << i << '\n';
  17. }
  18.  
  19. return ;
  20. }

练习3.18:下面的程序合法吗?如果不合法,你准备如何修改?

  1. vector<int> ivec;
  2. ivec[] = ;

不合法 ,应修改为ivec.push_back(42);

练习3.19:如果想定义一个含有10个元素的vector对象,所有元素的值都是42,请列举出三种不同的实现方法。哪种方法更好呢?为什么?

  1. #include <iostream>
  2. #include <vector>
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. vector<int> v1(, );
  9. vector<int> v2{, , , , , , , , , ,};
  10. vector<int> v3;
  11. vector<int> ivec3;
  12. for (int i = ; i < ; ++i)
  13. v3.push_back();
  14.  
  15. return ;
  16. }

显然,圆括号实现的方法更好,简洁易用。

练习3.20:读入一组整数并把它们存入一个vector对象,将每对相邻整数的和输出出来。改写你的程序,这次要求先输出第1个和最后1个元素的和,接着输出第2个和倒数第2个元素的和,以此类推。

  1. #include <iostream>
  2. #include <vector>
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. vector<int> v;
  9. int n;
  10. while (cin >> n)
  11. v.push_back(n);
  12. for (int i = ; i < v.size() - ; ++i)    //考虑到v[i+1],防止溢出,应该循环 v.size() - 1 次
  13. {
  14. cout << v[i] + v[i + ] << '\t';
  15. ++i;
  16. }
  17. cout << '\n';
  18. return ;
  19. }
  1. #include <iostream>
  2. #include <vector>
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. vector<int> v;
  9. int n;
  10. while (cin >> n)
  11. v.push_back(n);
  12. vector<int>::size_type loop = v.size();
  13. for (int i = ; i < v.size() / ; ++i)
  14. {
  15. cout << v[i] + v[loop - ] << '\t';
  16. --loop;
  17. }
  18. cout << '\n';
  19. return ;
  20. }

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

  1. #include <iostream>
  2. #include <vector>
  3.  
  4. using namespace std;

  5. //以下两个同名函数为重载
  6. void traverse(const vector<int> fv)
  7. {
  8. for (auto b = fv.begin(); b != fv.end(); ++b)
  9. {
  10. cout << *b << '\t';
  11. }
  12. cout << '\n';
  13. }
  14.  
  15. void traverse(const vector<string> fv)
  16. {
  17. for (auto b = fv.begin(); b != fv.end(); ++b)
  18. {
  19. cout << *b << '\t';
  20. }
  21. cout << '\n';
  22. }
  23.  
  24. int main()
  25. {
  26. vector<int> v1;
  27. vector<int> v2();
  28. vector<int> v3(, );
  29. vector<int> v4{};
  30. vector<int> v5{, };
  31. vector<string> v6{};
  32. vector<string> v7{, "hi"};
  33.  
  34. traverse(v1);
  35. traverse(v2);
  36. traverse(v3);
  37. traverse(v4);
  38. traverse(v5);
  39. traverse(v6);
  40. traverse(v7);
  41.  
  42. return ;
  43. }

练习3.22:修改之前那个输出text第一段的程序,首先把text的第一段全都改成大写形式,然后再输出它。

  1. #include <iostream>
  2. #include <vector>
  3. #include <string>
  4.  
  5. using namespace std;
  6.  
  7. int main()
  8. {
  9. string s;
  10. vector<string> v;
  11. while (cin >> s)
  12. v.push_back(s);
  13. for (auto b = v.begin(); b != v.end() && !b->empty(); ++b)
  14. {
  15. for (auto &i : *b)
  16. i = toupper(i);
  17. cout << *b << '\t';
  18. }
  19. cout << '\n';
  20. return ;
  21. }

练习3.23:编写一段程序,创建一个含有10个整数的vector对象,然后使用迭代器将所有元素的值都变成原来的两倍。输出vector对象的内容,检验程序是否正确。

  1. #include <iostream>
  2. #include <vector>
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. vector<int> v;
  9. for (int i = ; i < ; ++i)
  10. {
  11. int x;
  12. cin >> x;
  13. v.push_back(x);
  14. }
  15. for (auto &i : v)
  16. {
  17. i += i;
  18. cout << i << '\t';
  19. }
  20. cout << '\n';
  21. return ;
  22. }

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

  1. #include <iostream>
  2. #include <vector>
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. vector<int> v;
  9. int n;
  10. while (cin >> n)
  11. v.push_back(n);
  12. for (auto b = v.begin(); b < v.end() - ; ++b)
  13. {
  14. cout << *b + *(b + ) << '\t';
  15. ++b;
  16. }
  17.  
  18. cout << '\n';
  19.  
  20. auto b = v.begin() , e = v.end() - ;
  21. while (b < e)
  22. {
  23. cout << *b + *e << '\t';
  24. ++b;
  25. --e;
  26. }
  27.  
  28. return ;
  29. }

练习3.25:3.3.3节(第94页)划分分数段的程序是使用下标运算符实现的,请利用迭代器改写该程序并实现完全相同的功能。

  1. #include <iostream>
  2. #include <vector>
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. vector<unsigned> scores(, );
  9. unsigned grade;
  10. while (cin >> grade)
  11. ++*(scores.begin() + grade / );
  12.  
  13. return ;
  14. }

练习3.26:在100页的二分搜索程序中,为什么用的是mid = beg + (end - beg) / 2,而非mid = (beg + end) /2;?

因为迭代器之间没有定义“ + ”的运算。

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

unsigned buf_size = 1024;
(a) int ia[buf_size];        //非法,非常量表达式(g++/clang++扩展的C99特性可以编译通过)
(b) int ia[4 * 7 - 14];        //合法
(c) int ia[txt_size()];        //非法,非常量表达式
(d) char st[11] = "fundamental";   //非法,字符串末尾隐含'\0'字符,数组溢出

练习3.28:下列数组中元素的值是什么?

  1. #include <iostream>
  2.  
  3. using namespace std;
  4.  
  5. string sa[]; //空字符元素
  6. int ia[]; //元素0
  7. int main()
  8. {
  9. string sa2[]; //空字符元素
  10. int ia2[]; //未定义
  11. return ;
  12. }

练习3.29:相比于vector 来说,数组有哪些缺点,请例举一些。

缺点有三:

1.vector大小可变,数组固定,无法增长,限制大

2.vector可以进行赋值,而数组无法复制

3.vector带有一些方法用于操作vector,比如push,size,empty,<,>等,而数组没有这些方便使用的方法

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

  1. constexpr size_t array_size = ;
  2. int ia[array_size];
  3. for (size_t ix = ; ix <= array_size; ++ix) //数组大小是10,但数组下标索引从0开始,最大为9,循环溢出
  4. ia[ix] = ix;

练习3.31:编写一段程序,定义一个含有10个int的数组,令每个元素的值就是其下标值。

  1. #include <iostream>
  2.  
  3. using namespace std;
  4.  
  5. int main()
  6. {
  7. int arr[];
  8. for (int i = ; i != ; ++i)
  9. {
  10. arr[i] = i;
  11. }
  12. return ;
  13. }

练习3.32:将上一题刚刚创建的数组拷贝给另外一个数组。利用vector重写程序,实现类似的功能。

  1. #include <iostream>
  2. #include <vector>
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. int arr[];
  9. for (int i = ; i != ; ++i)
  10. {
  11. arr[i] = i;
  12. }
  13.  
  14. int arr2[];
  15. for (auto i : arr)
  16. {
  17. arr2[i] = i;
  18. }
  19.  
  20. //vector
  21. vector<int> v();
  22. for (int i = ; i != ; ++i)
  23. {
  24. v[i] = i;
  25. }
  26. vector<int> v2 = v;
  27. return ;
  28. }

练习3.33:对于104页的程序来说,如果不初始化scores将会发生什么?
数组中所有元素的值将会默然初始化,其中的元素值是未定义的。

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

p1 += p2 - p1;

程序的功能是将p1指向p2所指的元素,当p1和p2不指向同一个数组中的元素时,该程序是非法的

练习3.35:编写一段程序,利用指针将数组中的元素置为0。

  1. #include <iostream>
  2.  
  3. using namespace std;
  4.  
  5. int main()
  6. {
  7. int array[] = {, , , , , , , , };
  8. for (int *p = array; p != &array[]; p++)
  9. {
  10. cout << *p << '\t';
  11. *p = ;
  12. cout << *p << '\t';
  13. cout << '\n';
  14. }
  15. return ;
  16. }

练习3.36:编写一段程序,比较两个数组是否相等。再写一段程序,比较两个vector对象是否相等。

  1. #include <iostream>
  2. #include <vector>
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. int array1[] = {, , , , , , , };
  9. int array2[] = {, , , , , , , };
  10. if (sizeof(array2) == sizeof(array2)) //数组长度一致时才具体比较
  11. {
  12. for (int i = ; i != sizeof(array1) / sizeof(int); ++i)
  13. {
  14. if (array1[i] != array2[i]) //逐元素比较
  15. {
  16. cout << "not equal!" << endl;
  17. break;
  18. }
  19. }
  20. }
  21. else
  22. {
  23. cout << "not equal!" << endl;
  24. }
  25.  
  26. //vector
  27. vector<int> v1{, , , , };
  28. vector<int> v2{, , , , , };
  29. if (v1 != v2)
  30. cout << "not equal!" << endl;
  31. return ;
  32. }

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

  1. const char ca[] = { 'h', 'e', 'l', 'l', 'o' };
  2. const char *cp = ca;
  3. while (*cp)
  4. {
  5. cout << *cp << endl;
  6. ++cp;
  7. }

只要cp所指向的元素不是'\0'(空字符),就一直循环遍历,该程序将会一直循环,直到在内存中遇到空字符

练习3.38:在本节中我们提到,将两个指针相加不但是非法的,而且也没有什么意义。请问为什么两个指针相加没有意义?

两个指针相加,即两个内存地址相加,是没有任何逻辑意义的。

练习3.39:编写一段程序,比较两个string对象。再编写一段程序,比较两个C风格字符串的内容。

  1. #include <iostream>
  2. #include <cstring>
  3. #include <string>
  4.  
  5. using namespace std;
  6.  
  7. int main()
  8. {
  9. string s1{"hello"};
  10. string s2{"world"};
  11. if (s1 == s2)
  12. cout << "s1 = s2" << endl;
  13. else
  14. cout << "s1 != s2" << endl;
  15.  
  16. //C-style character string
  17. const char *c1 = "string1";
  18. const char *c2 = "string1";
  19. if (strcmp(c1, c2))
  20. {
  21. cout << "c1 != c2" << endl;
  22. }
  23. else
  24. {
  25. cout << "c1 = c2" << endl;
  26. }
  27. return ;
  28. }

练习3.40:编写一段程序,定义两个字符数组并用字符串字面值初始化它们;接着再定义一个字符数组存放前面两个数组连接后的结果。使用strcpy和strcat把前两个数组的内容拷贝到第三个数组当中。

  1. #include <iostream>
  2. #include <cstring>
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. char c1[] = "hello";
  9. char c2[] = "world";
  10. char c3[];
  11. strcpy(c3, c1);
  12. strcat(c3, c2);
  13. cout << c3 << endl;
  14. return ;
  15. }

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

  1. #include <iostream>
  2. #include <vector>
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. int arr[] = {, , , , };
  9. vector<int> v(begin(arr), end(arr));
  10.  
  11. return ;
  12. }

练习3.42:编写一段程序,将含有整数元素的vector对象拷贝给一个整型数组。

  1. #include <iostream>
  2. #include <vector>
  3.  
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. vector<int> v{, , , , };
  9. int array[];
  10. for (int i = ; i < v.size(); ++i)
  11. {
  12. array[i] = v[i];
  13. }
  14. return ;
  15. }

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

  1. #include <iostream>
  2.  
  3. using namespace std;
  4.  
  5. int main()
  6. {
  7. int ia[][] = {, , , , , , , , , , , };
  8.  
  9. //version 1
  10. for (int (&i)[] : ia)
  11. {
  12. for (int j : i)
  13. cout << j << '\t';
  14. }
  15. cout << endl;
  16.  
  17. //version 2
  18. for (int i = ; i < ; ++i)
  19. {
  20. for (int j = ; j < ; ++j)
  21. cout << ia[i][j] << '\t';
  22. }
  23. cout << endl;
  24.  
  25. //version 3
  26. for (int *p = &ia[][]; p != &ia[][]; ++p) //ia[2][3]是最后一个元素,ia[2][4]是尾后元素
  27. {
  28. cout << *p << '\t';
  29. }
  30. cout << endl;
  31.  
  32. return ;
  33. }

练习3.44:改写上一个练习中的程序,使用类型别名来代替循环控制变量的类型。

  1. #include <iostream>
  2.  
  3. using namespace std;
  4.  
  5. int main()
  6. {
  7. int ia[][] = {, , , , , , , , , , , };
  8.  
  9. //version 1
  10. using ref4 = int (&)[];
  11. for (ref4 i : ia)
  12. {
  13. for (int j : i)
  14. cout << j << '\t';
  15. }
  16. cout << endl;
  17.  
  18. //version 2
  19. for (int i = ; i < ; ++i)
  20. {
  21. for (int j = ; j < ; ++j)
  22. cout << ia[i][j] << '\t';
  23. }
  24. cout << endl;
  25.  
  26. //version 3
  27. for (int *p = &ia[][]; p != &ia[][]; ++p) //ia[2][3]是最后一个元素,ia[2][4]是尾后元素
  28. {
  29. cout << *p << '\t';
  30. }
  31. cout << endl;
  32.  
  33. return ;
  34. }

练习3.45:再一次改写程序,这次使用auto关键字。

  1. #include <iostream>
  2.  
  3. using namespace std;
  4.  
  5. int main()
  6. {
  7. int ia[][] = {, , , , , , , , , , , };
  8.  
  9. //version 1
  10. for (auto &i : ia)
  11. {
  12. for (auto j : i)
  13. cout << j << '\t';
  14. }
  15. cout << endl;
  16.  
  17. //version 2
  18. for (int i = ; i < ; ++i)
  19. {
  20. for (int j = ; j < ; ++j)
  21. cout << ia[i][j] << '\t';
  22. }
  23. cout << endl;
  24.  
  25. //version 3
  26. for (auto p = ia; p != ia + ; ++p)
  27. {
  28. for (auto q = *p; q != *p + ; ++q)
  29. cout << *q << '\t';
  30. }
  31. cout << endl;
  32.  
  33. return ;
  34. }

C++ Primer 5th 第3章 字符串、向量和数组的更多相关文章

  1. <<C++ Primer>> 第三章 字符串, 向量和数组 术语表

    术语表 第 3 章 字符串, 向量和数组 begin: 是 string 和 vector 的成员,返回指向第一个元素的迭代器.也是一个标准库函数,输入一个数字,返回指向该数字首元素的指针.    缓 ...

  2. [C++ Primer] 第3章: 字符串, 向量和数组

    标准库类型string string初始化 string s2(s1); string s2 = s1; string s3("value"); string s3 = " ...

  3. C++ Primer 5th 第6章 函数

    正如第一章所说:C++的函数是一个能够完成一个功能的模块或者说是一段命名了的代码块. 如下图所示,函数可以重载,是一段实现某些功能命名了的代码. 一个完整的函数的构成有四部分: 1.返回类型 2.函数 ...

  4. PHP:第五章——字符串与数组及其他函数

    <?php header("Content-Type:text/html;charset=utf-8"); //1.str_split——将字符串转换为数组. /*$str= ...

  5. C++ Primer 5th 第12章 动态内存

    练习12.1:在此代码的结尾,b1 和 b2 各包含多少个元素? StrBlob b1; { StrBlob b2 = {"a", "an", "th ...

  6. C++ Primer 5th 第9章 顺序容器

    练习9.1:对于下面的程序任务,vector.deque和list哪种容器最为适合?解释你的选择的理由.如果没有哪一种容器优于其他容器,也请解释理由.(a) 读取固定数量的单词,将它们按字典序插入到容 ...

  7. C++ Primer 5th 第2章 变量和基本类型

    *****代码在Debian g++ 5.3.1 / clang++ 3.8(C++11)下编写调试***** 由于部分编译器对标准遵循的不同以及自身额外的扩展,本章书中的少数知识点与实际实现存在偏差 ...

  8. C++ Primer 5th 第1章 开始

    *****代码在Ubuntu g++ 5.31 / clang++ 3.8(C++11)下编写调试***** 每个C++程序必须有一个main( )函数,main( )函数的返回值也必须是int类型, ...

  9. C++ Primer 5th 第14章 重载运算与类型转换

    当运算符作用域类类型的对象时,可以通过运算符重载来重新定义该运算符的含义.重载运算符的意义在于我们和用户能够更简洁的书写和更方便的使用代码. 基本概念 重载的运算符是具有特殊名字的函数:函数名由关键词 ...

随机推荐

  1. php __clone实现

    <?php class Account { public $balance; public function __construct($balance) { $this->balance ...

  2. 文成小盆友python-num12 Redis发布与订阅补充,python操作rabbitMQ

    本篇主要内容: redis发布与订阅补充 python操作rabbitMQ 一,redis 发布与订阅补充 如下一个简单的监控模型,通过这个模式所有的收听者都能收听到一份数据. 用代码来实现一个red ...

  3. Mongodb增加权限管理

     前言: 随着列式存储理念的成熟,越来越多的开发者开始接纳mongodb,hbase这类大储存的分布式列式数据库.特别是mongodb的这种快速搭建,快速使用特点,使其得到更多人的青睐.本人主要通过官 ...

  4. 大小写转换,split分割

    一.大小写转换 1.定义和用法 toUpperCase() 方法用于把字符串转换为大写. toLowerCase() 方法用于把字符串转换为小写.    用法: stringObject.toUppe ...

  5. Head First --- Python 第一章

    List 1. python -V 查看python当前版本 2. list.pop() 删除列表里的最后一个元素,并返回删除元素的值 3. list.extend(['a','b','c']) 在原 ...

  6. hdu 3435 A new Graph Game

    http://acm.hdu.edu.cn/showproblem.php?pid=3435 #include <cstdio> #include <iostream> #in ...

  7. DOCKER内部安装指南

    本文介绍如何在RedHat/CentOS环境下安装Docker.官方文档要求Linux kernel至少3.8以上,且docker只能运行在64位的系统中.由于RHEL6和CentOS6的内核版本为2 ...

  8. JAVA接口示例

    总感觉有点虚,但慢慢找到感觉了.将对象放进数组里,这就比较深入了. interface drawTest{ public void draw(); public void doAnyThing(); ...

  9. delphi编写dll心得, 谢谢原作者的分享。转

    delphi编写dll心得 1.每个函数体(包括exports和非exports函数)后面加 'stdcall;', 以编写出通用的dll2.exports函数后面必须加'export;'(放在'st ...

  10. 如何使用深度学习破解验证码 keras 连续验证码

    在实现网络爬虫的过程中,验证码的出现总是会阻碍爬虫的工作.本期介绍一种利用深度神经网络来实现的端到端的验证码识别方法.通过本方法,可以在不切割图片.不做模板匹配的情况下实现精度超过90%的识别结果. ...