第3章 字符串、向量和数组

目录

3.1 命名空间的using声明

3.2 标准库类型string

3.2.1 定义和初始化string对象

3.2.2 string对象上的操作

3.2.3 处理string对象中的字符

3.3 标准库类型vector

3.3.1 定义和初始化vector

3.3.2 向vector对象中添加元素

3.3.3 其他vector操作

3.4 迭代器介绍

3.4.1 使用迭代器

3.4.2 迭代器运算

3.5 数组

3.5.1 定义和初始化数组

3.5.2 访问数组元素

3.5.3 指针和数组

3.5.4 C风格字符串

3.5.5 与旧代码地接口

3.6 多维数组

练习


3.1 命名空间的using声明

using namespace::name;

每个名字都需要独立的using声明

头文件不应包含using声明

3.2 标准库类型string

3.2.1 定义和初始化string对象

string s1;           // 默认初始化,s1是空串
string s2(s1);       // s2是s1的副本,直接初始化
string s2 = s1;      // 等价于s2(s1),拷贝初始化
string s3("value");  // s3是字面值"value"的副本,除了字面值最后的空字符外
string s3 = "value"; // 等价于s3("value"),s3是字面值"value"的副本
string s4(n, 'c');   // 把s4初始化为连续 n 个字符 'c' 组成的字符串,直接初始化
string s4 = string(n, 'c');    // 拷贝初始化,等价于s4(n, 'c')

3.2.2 string对象上的操作

string的操作
os << s将s写到输出流os当中,返回os
is >> s从输入流is中读取字符串赋给s,字符串以空白分隔,返回is
getline(is, s)从输入流is读取一行赋给s,返回is
s.empty() s为空返回true,否则返回false
s.size() 返回s中字符的个数,返回类型为string::size_type
s[n]返回s中第n个字符的引用,位置n从0机起
s1 + s2返回s1和s2连接后的结果
s1 = s2用s2的副本代替s1中原来的字符
s1 == s2如果s1和s2中所含字符完全一样,则它们相等;string对象的相
等性判断对字母的大小写敏感
s1 != s2
<, <=, >, >=利用字符在字典中的顺序进行比较,对字母的大小写敏感

3.2.3 处理string对象中的字符

cctype头文件中的函数
isalnum(c)c是字母或数字时为true
isalpha(c)c是字母时为true
iscntrl()c是控制字符时为true
isdigit(c)c是数字时为true
isgraph(c)c不是空格但可以打印时为true
islower(c)c是小写字母时为true
isprint(c)c是可打印的字符时为true
ispunct(c)c是标点符号时为true
isspace(c)c是空白字符时为true
isupper(c)c是大写字母时为true
isxdigit(c)c是十六进制数时为true
tolower(c)c是大写字母,则返回小写字母形式,否则返回c。
toupper(c)c是小写字母,则返回大些字母形式,否则返回c。

处理每个字符?使用范围for(range for)语句

#include<iostream>
#include<string>
#include<cctype>
using namespace std;

int main()
{
    string s("Hello Word!!!");
    decltype(s.size()) punct_cnt = 0;   // 标点符号计数器
    // 统计s中标点符号的数量
    for (char c : s)
    {
        if (ispunct(c))
            ++punct_cnt;
    }
    cout << punct_cnt << " punctuation characters in " << s << endl;
    return 0;
}

使用范围for语句改变字符串中的字符

#include<iostream>
#include<string>
#include<cctype>
using namespace std;

int main()
{
    string s("Hello Word!!!");
    // 转换成大写形式
    for (char& c : s)       
        c = toupper(c);     // c是一个引用,因此赋值语句将改变s中字符的值
    cout << s << endl;
    return 0;
}

使用下标执行迭代

#include<iostream>
#include<string>
#include<cctype>
using namespace std;

int main()
{
    string s("some string");
    // 依次处理s中的字符直至处理完全部字符或者遇到空白
    for (string::size_type index = 0; 
        index != s.size() && !isspace(s[index]); ++index)
        s[index] = toupper(s[index]);   // 将当前字符改成大写形式
    cout << s << endl;
    return 0;
}

使用下标执行随机访问

#include<iostream>
#include<string>
#include<cctype>
using namespace std;

int main()
{
    // 把0到15之间的十进制数转换成对应的十六进制数
    const string hexdigits("0123456789ABCDEF");     // 可能的十六进制数
    cout << "输入一系列0到15之间的十进制数: " << endl;
    string hex_str;     // 用于保存十六进制的字符串
    string::size_type n;    // 用于保存从输入流读取的数
    while (cin >> n)
    {
        if (n < hexdigits.size())    // 忽略无效输入
        {
            hex_str += hexdigits[n]; // 得到对应的十六进制数
        }
    }
    cout << "对应的十六进制数:" << hex_str << endl;
    return 0;
}

3.3 标准库类型vector

3.3.1 定义和初始化vector

vector<T> v1;               // 默认初始化,v1是一个空的vector,它潜在的元素是T类型的
vector<T> v2(v1);           // 直接初始化,v2中包含v1所有元素的副本
vector<T> v2 = v1;          // 拷贝初始化,v2中包含v1所有元素的副本
vector<T> v3(n, val);       // v3包含了n个重复的元素,每个元素的值都是val
vector<T> v4(n);            // v4包含了n个重复地执行了值初始化的对象
vector<T> v5{a,b,c...};     // 列表初始化,v5包含了初始值个数的元素,每个元素被赋予相应的初始值
vector<T> v5 = {a,b,c...};  // 等价于v5{a,b,c...}

3.3.2 向vector对象中添加元素

// 使用vector的成员函数push_back向其中添加元素
vector<int> v;
for (int i = 0; i != 10; ++i)
    v.push_back(i);

3.3.3 其他vector操作

vector支持的操作
v.empty() v为空返回true,否则返回false
v.size() 返回v中元素的个数
v.push_back(t)向v的尾端添加一个值为t的元素
v[n]返回v中第n个字符的引用
v1 = v2用v2中元素的拷贝替换v1中的元素
v1 = {a, b, c...}用列表中元素的拷贝替换v1中的元素
v1 == v2v1和v2相等当且仅当它们的元素数量相同且对应位置的元素
值都相同
v1 != v2
<, <=, >, >=利用字典顺序进行比较

3.4 迭代器介绍

3.4.1 使用迭代器

迭代器运算符

string s("some thing");
if (s.begin() != s.end())
{
    // 首字母改为大写形式
    auto it = s.begin();
    *it = toupper(*it);
}

将迭代器从一个元素移动到另一个元素

// 将所有字符改写为大写形式
for (auto it = s.begim(); it != s.end(); ++it)
    *it = toupper(*it)

迭代器类型

vector<T>::iterator        读写        vector<T>::const_iterator        只读

3.4.2 迭代器运算

        两个迭代器相减,所得结果是俩个迭代器的距离,类型为difference_type的带符号整数。

使用迭代器计算

// 二分搜索
auto beg = text.begin(), end = text.end();
auto mid = beg + (end - beg) / 2;   // 初始状态下的中间点
while (mid != end && *mid != sought)
{
    if (sought < *mid)
        end = mid;
    else
        beg = mid + 1;
    mid = beg + (end - beg) / 2;    // 新的中点
}

3.5 数组

3.5.1 定义和初始化数组

  • 数组的维度必须是一个常量表达式
  • 数组不允许拷贝和赋值

理解复杂的数组声明

int* ptrs[10];              // ptrs是数组,该数组含有10个int型指针
int& refs[10] = /* ? */;    // 不存在引用的数组
int(*Parray)[10] = &arr;    // Parray是一个指针,指向一个int数组,数组含有10个元素
int(&arrRef)[10] = arr;     // arrRef是数组的引用,该数组有10个int元素
int*(&arry)[10] = ptrs;     // arry是数组的引用,该数组有10个int型指针

3.5.2 访问数组元素

使用数组下标时,通常将其定义为size_t类型。

3.5.3 指针和数组

在很多用到数组名字的地方,编译器会自动地将其替换为一个指向数组首元素地指针:

string* p = arr;    // 等价于 string* p = &arr[0]; 

        当使用数组作为一个auto变量地初始值时,推断得到的类型是指针而非数组;当使用decltype关键字时上述转换不会发生。

int ia[] = {0,1,2,3,4,5,6};
auto ia2(ia);    // ia2是一个整型指针,指向ia地第一个元素
decltype(ia) ia3 = {0,1,2,3,4,5,6,7,8,9};    // ia3是一个整型数组

标准库函数begin和end

int ia[] = {0,1,2,3,4,5,6};
int* beg = begin(ia);    // 指向ia首元素地指针
int* end = end(ia);      // 之现象ia尾元素地下一位置地指针

指针运算

两个指针相减地结果地类型是一种名为ptrdiff_t地标准库类型

3.5.4 C风格字符串

C风格字符串地函数
strlen(p)返回p地长度,空字符不计算在内
strcmp(p1, p2)比较p1和p2地相等性。返回值为0、正值和负值
strcat(p1, p2)将p2附加到p1之后,返回p1
strcpy(p1, p2)将p2拷贝给p1,返回p1

3.5.5 与旧代码地接口

混用string对象和C风格字符串

  • 允许使用以空字符结束的字符数组来初始化string对象或为string对象赋值
  • 不能用string对象直接初始化指向字符的指针,调用string的成员函数c_str返回一个C风格的字符串。

3.6 多维数组

多维数组其实是数组的数组

使用范围for语句处理多维数组

        要使用范围for语句处理多维数组,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型。

int a[3][4] = {
    {0,1,2,3},
    {1,2,3,4},
    {2,3,4,5}
};
for (auto& row : a)
{
    for (auto& col : row)
        cout << col << " ";
    cout << endl;
}

练习

练习3.2:

#include<iostream>
#include<string>
using namespace std;

int main()
{
    string line, word;
    while (getline(cin, line))
        cout << line << endl;

    while (cin >> word)
        cout << word << endl;
    return 0;
}

练习3.3:

string类的输入操作符空白字符的处理:读取并忽略有效字符(非空白字符)之前所有的空白字符,然后读取字符直至再次遇到空白字符,读取终止(该空白字符仍留在输入流中)。

getline函数对空白字符的处理:不忽略行开头的空白字符,读取字符直至遇到换行符,读取终止并丢弃换行符。

练习3.4:

#include<iostream>
#include<string>
using namespace std;

int main()
{
    // 输出较大的字符串
    cout << "输入两个字符串:" << endl;
    string str1, str2;
    while (cin >> str1 >> str2)
    {
        if (str1 == str2)
            cout << "两个字符串相等" << endl;
        else
            cout << ((str1 > str2) ? str1 : str2) << endl;
    }
    // 输出最长的字符串
    while (cin >> str1 >> str2)
    {
        if (str1 == str2)
            cout << "两个字符串等长" << endl;
        else
            cout << ((str1.size() >= str2.size()) ? str1 : str2) << endl;
    }  
    return 0;
}

练习3.5:

#include<iostream>
#include<string>
using namespace std;

int main()
{
    // 无空格
    string str, largeStr;
    while (cin >> str)
    {
        largeStr += str;
    }
    cout << largeStr << endl;
    // 有空格
    while (cin >> str)
    {
        largeStr += str + ' ';
    }
    cout << largeStr << endl;

    return 0;
}

练习3.6:

#include<iostream>
#include<string>
using namespace std;

int main()
{
    string str("abcdefg");
    for (auto &c : str)
    	c = 'X';
    cout << str << endl;
    return 0;
}

练习3.7:字符串内的字符将不会改变,必须是char&。

练习3.8:

#include<iostream>
#include<string>
using namespace std;

int main()
{
    string str("some string");
    // 使用while
    string::size_type i = 0;
    while (i != str.size()) {
        str[i] = 'X';
        ++i;
    }
    cout << str << endl;
    // 使用for
    for (i = 0; i != str.size(); ++i) 
        str[i] = 'Y';
    cout << str << endl;

    return 0;
}

练习3.9:使用下标时必须确保其在合理范围之内,s为空串,s[0]输出空字符('\0')。

练习3.10:

#include<iostream>
#include<string>
using namespace std;

int main()
{
    string s;
    while (getline(cin, s)) 
    {
        for (auto c : s)
            if (!ispunct(c)) 
                cout << c;
        cout << endl;
    }
    return 0;
}

练习3.11:合法  const char &

练习3.12:(a)正确;(b)错误,元素类型不匹配;(c)正确。

练习3.13:

 (a)元素数量0;(b)元素数量10,每个都为0;(c)元素数量10,每个都为42;(d)元素数量1,值为10; (e)元素数量2,值为10和42;(f)元素数量10,都为空串;(g)元素数量10,每个字符串都为"hi"。

练习3.14:

#include<iostream>
#include<vector>
using namespace std;

int main()
{
    vector<int> iv;
    int num;
    while (cin >> num)
    {
    	iv.push_back(num);
    }
    return 0;
}

练习3.15:

#include<iostream>
#include<vector>
using namespace std;

int main()
{
    vector<string> str_v;
    string str;
    while (cin >> str)
    {
    	str_v.push_back(str);
    }
    return 0;
}

练习3.17:

#include<iostream>
#include<vector>
using namespace std;

int main()
{
    vector<string> str_v;
	string str;
	while (cin >> str)
	{
		for (auto& c : str)
		{
			c = toupper(c);
		}
		str_v.push_back(str);
	}
	for (auto elem : str_v)
	{
		cout << elem << endl;
	}
    return 0;
}

练习3.18:不合法;ivec为空,不能用下标,应改为ivec.push_back(42)。

练习3.19:

vector<int> vec1(10, 42);
vector<int> vec2{ 42,42,42,42,42,42,42,42,42,42 };
vector<int> vec3;
for (int i = 0; i < 10; ++i)
	vec3.push_back(42);

练习3.20:

#include<iostream>
#include<vector>
using namespace std;

int main()
{
	vector<int> ivec;
	int num;
	while (cin >> num)
	{
		ivec.push_back(num);
	}
	//相邻两数相加
	if (ivec.empty())
	{
		cout << "请输入至少一个数据" << endl;
		return -1;
	}
	else if (ivec.size() == 1)
	{
		cout << ivec[0] << " 没有相邻数据。" << endl;
	}
	else
	{
		for (decltype(ivec.size()) i = 0; i != ivec.size() - 1; ++i)
		{
			cout << ivec[i] + ivec[i + 1] << " ";
		}
		cout << endl;
	}
	//首尾两数相加
	for (int i = 0, j = ivec.size()- 1;i <= j; ++i, --j )
	{
		cout << ivec[i] + ivec[j] << " ";
	}
	cout << endl;
    return 0;
}

练习3.22:

#include<iostream>
#include<string>
#include<vector>
using namespace std;

int main()
{
    vector<string> text;
    for (string line; getline(cin, line);) 
        text.push_back(line);
    for (auto it = text.begin(); it != text.end() && !it->empty(); ++it) 
    {
        for (auto& c : *it) 
            c = toupper(c);
        cout << *it << endl;
    }
    return 0;
}

练习3.23:

#include<iostream>
#include<vector>
using namespace std;

int main()
{
    vector<int> ivec{ 1,2,3,4,5,6,7,8,9,10 };
    for (vector<int>::iterator it = ivec.begin(); it != ivec.end(); it++)
    	*it *= 2;
    for (auto value : ivec) 
        cout << value << " ";
    cout << endl;
    return 0;
}

练习3.24:

#include<iostream>
#include<vector>
using namespace std;

int main()
{
	vector<int> ivec;
	int num;
	while (cin >> num)
		ivec.push_back(num);
	//相邻两数相加
	if (ivec.empty())
	{
		cout << "请输入至少一个数据" << endl;
		return -1;
	}
	else if (ivec.size() == 1)
		cout << ivec[0] << " 没有相邻数据。" << endl;
	else
	{
		for (auto it = ivec.begin(); it != ivec.end() - 1; )
			cout << *it + *(++it) << " ";
		cout << endl;
	}
	//首尾两数相加
	for (auto beg = ivec.begin(), j = ivec.end()- 1; beg <= end; ++beg, --end)
		cout << *beg + *end << " ";
    cout << endl;
    return 0;
}

练习3.25:

#include<iostream>
#include<vector>
using namespace std;

int main()
{
    vector<unsigned> scores(11, 0); // 11个分数段,初始化为0
	unsigned grade;
	grade = -1;
	cout << grade << endl;
	while (cin >> grade)
	{
		if (grade <= 100)
			++(*(scores.begin() + grade / 10));
	}
	for (auto it = scores.begin(); it != scores.end(); it++)
		cout << *it<< " ";
	cout << endl;
    return 0;
}

练习3.26:迭代器之间没有定义两个迭代器之间的加法

练习3.27:

(a)、(c)非法,数组维度必须是常量表达式

(d)非法,没有空间存放空字符。

练习3.28:sa 和 sa2 是空串          ia全为0  ia2 是随机值

练习3.29:

        与vector不同的地方是,数组的大小确定不变,不能随意向数组中增加元素。因为数组的大小固定,因此对某些特殊的应用来说,程序的运行时性能较好,但是相应地也损失了一些灵活性。

练习3.30:ix不能等于array_size;

练习3.31:

#include <iostream>
using namespace std;

int main()
{
    int ia[10];
    for (size_t i = 0; i < 10; ++i) 
        ia[i] = i;
    return 0;
}

练习3.32:

#include <iostream>
#include<vector>
using namespace std;

int main()
{
    // 数组
    int ia[10];
    for (size_t i = 0; i < 10; ++i) 
        ia[i] = i;
    int ia2[10];
    for (size_t i = 0; i < 10; ++i) 
        ia2[i] = ia[i];
    // vector
    vector<int> iv(10);
    for (auto iter = iv.begin(); iter != iv.end(); ++iter)
        *iter = iter - iv.begin();
    vector<int> iv2(iv);

    return 0;
}

练习3.33:如果scores不初始化,编译器不会报错,但输出结果是一个未知数

练习3.34:p1指针移动p1-p2的距离,当p1是常量指针时,程序非法。

练习3.35:

#include<iostream>
#include<vector>
using namespace std;
int main()
{
	int arr[] = { 0,1,2,3,4,5,6 };
	for (auto ptr = begin(arr); ptr != end(arr); ++ptr)
		*ptr = 0;
	for (auto c : arr)
		cout << c << " ";
	return 0;
}

练习3.36:

#include<iostream>
#include<vector>
using namespace std;

bool compare(int* const pbeg1, int* const pend1, int* const pbeg2, int* const pend2)
{
	if ((pend1 - pbeg1) != (pend2 - pbeg2))
		return false;
	else
	{
		for (auto it1 = pbeg1, it2 = pbeg2; it1 != pend1 && it2 != pend2; ++it1, ++it2)
			if (*it1 != *it2)
				return false;
	}
	return true;
}

int main()
{
	int arr1[3] = { 0, 1, 2 };
	int arr2[3] = { 1, 1, 2 };
	cout << compare(begin(arr1), end(arr1), begin(arr2), end(arr2)) << endl;
	vector<int> vec1 = { 0, 1, 2 };
	vector<int> vec2 = { 0, 1, 2 };
	cout << (vec1 == vec2) << endl;
	return 0;
}

练习3.37: 输出hello后仍输出一段乱码字符。

练习3.38:指针的值只是所指对象的内存地址,将两个地址值相加没有意义。

练习3.39:

#include<iostream>
#include<string>
#include<cstring>
using namespace std;

int main()
{
	string s1("hello");
	string s2("hellx");
	if (s1 > s2)
		cout << "s1 > s2" << endl;
	else if (s1 < s2)
		cout << "s1 < s2" << endl;
	else
		cout << "si = s2" << endl;

	const char cs1[] = "hello";
	const char cs2[] = "hellx";
	if (strcmp(cs1, cs2) > 0)
		cout << "s1 > s2" << endl;
	else if (strcmp(cs1, cs2) < 0)
		cout << "s1 < s2" << endl;
	else
		cout << "si = s2" << endl;

	return 0;
}
#include <iostream>
#include <cstring>
using namespace std;

int main()
{
    const char cstr1[] = "Hello";
    const char cstr2[] = "world!";

    char cstr3[20];

    strcpy_s(cstr3, cstr1);
    strcat_s(cstr3, " ");
    strcat_s(cstr3, cstr2);

    cout << cstr3 << endl;

    return 0;
}

练习3.41:

#include <iostream>
#include <vector>
using namespace std;

int main()
{
    int ia[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    vector<int> ivec(begin(ia), end(ia));

    for (auto i : ivec) 
        cout << i << " ";
    cout << endl;

    return 0;
}

练习3.42:

#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int> ivec{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    int ia[10];

    for (int* i = begin(ia); i != end(ia); ++i)
        *i = ivec[i - begin(ia)];

    for (auto i : ia) 
        cout << i << " ";
    cout << endl;

    return 0;
}

练习3.43:

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	int ia[3][4] = { {0,1,2,3},{2,3,4,5},{4,5,6,7} };

	for (const int(&p)[4] : ia)
	{
		for (int q : p)
			cout << q << " ";
		cout << endl;
	}
	cout << endl;

	for (size_t i = 0; i != 3; i++)
	{
		for (size_t j = 0; j != 4; j++)
			cout << ia[i][j] << " ";
		cout << endl;
	}
	cout << endl;

	for (int(*p)[4] = ia; p != ia + 3; p++) //  p 是外层数组的首地址
	{
		for (int* q = *p; q != *p + 4; q++) // *p 是内层数组的首地址
		{
			cout << *q << " ";
		}
		cout << endl;
	}
	cout << endl;

    return 0;
}

练习3.44:

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	int ia[3][4] = { {0,1,2,3},{2,3,4,5},{4,5,6,7} };
	// 使用类型别名
	using int_array = int[4];
	for (int_array(&p) : ia)
	{
		for (int q : p)
		{
			cout << q << " ";
		}
		cout << endl;
	}
	cout << endl;

	for (size_t i = 0; i != 3; i++)
	{
		for (size_t j = 0; j != 4; j++)
			cout << ia[i][j] << " ";
		cout << endl;
	}
	cout << endl;

	for (int_array* p = ia; p != ia + 3; p++) //  p 是外层数组的首地址
	{
		for (int* q = *p; q != *p + 4; q++) // *p 是内层数组的首地址
		{
			cout << *q << " ";
		}
		cout << endl;
	}
	cout << endl;

    return 0;
}

练习3.45:

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	int ia[3][4] = { {0,1,2,3},{2,3,4,5},{4,5,6,7} };

	// 使用auto
	for (auto& p : ia)
	{
		for (auto q : p)
			cout << q << " ";
		cout << endl;
	}
	cout << endl;

	for (size_t i = 0; i != 3; i++)
	{
		for (size_t j = 0; j != 4; j++)
			cout << ia[i][j] << " ";
		cout << endl;
	}
	cout << endl;

	for (auto p = ia; p != ia + 3; ++p)
	{
		for (int* q = *p; q != *p + 4; ++q)
			cout << *q << " ";
		cout << endl;
	}
    cout << endl;

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值