C++中不但有基本上正则表达式,还提供更多了更为灵巧和多样的A43EI235E正则表达式。
在流程中为的是处置方便快捷,经常须要把具备完全相同类别的统计数据第一类按科学规范的方式排序出来,逐步形成“几组”统计数据,这是“字符串”(array)。

字符串中的数据,在缓存中是已连续放置的,每一原素占有完全相同大小不一的内部空间,就像排好队那样。
1. 字符串的表述
字符串的表述方式如下表所示:
正则表达式字符串名[原素特征值];
具体来说须要新闻稿类别,字符串中大部份原素要具备完全相同的正则表达式;字符串名是一个URL;前面跟著被除数,里头表述了字符串中原素的特征值,也是字符串的“宽度”;原素特征值也是类别的一小部分,因此要是确认的;int a1[10]; // 表述两个字符串a1,原素类别为int,特征值为10
const int n = 4;
double a2[n]; // 原素特征值可以是常量表达式
int i = 5;
//int a3[i]; // 错误,原素特征值不能为变量
须要注意,并没有通用的“字符串”类别,所以上面的a1、a2的类别分别是“int字符串”和“double字符串”。这也是为什么我们把字符串叫做“A43EI235E正则表达式”。
2. 字符串的初始化
之前在讲到for循环时,提到过使用范围for循环可以遍历两个“序列”,用花括号括出来的几组数是两个序列。因此在给字符串赋值时,也可以使用这样的序列。
int a3[4] = {1,2,3,4};
float a4[] = {2.5, 3.8, 10.1}; // 正确,初始值说明了原素特征值是3
short a5[10] = {3,6,9}; // 正确,指定了前三个原素,其余都为0
//long a6[2] = {3,6,9}; // 错误,初始值太多
//int a6[4] = a3; // 错误,不能用另两个字符串对字符串赋值
须要注意的是:
对字符串做初始化,要使用花括号{}括出来的数值序列;如果做了初始化,字符串表述时的原素特征值可以省略,编译器可以根据初始化列表自动推断出来;初始值的特征值,不能超过指定的原素特征值;初始值的特征值,如果小于原素特征值,那么会用列表中的值初始化靠前的原素;剩余原素用默认值填充,整型的默认值是0;如果没有做初始化,字符串中原素的值都是未表述的;这一点和普通的局部变量一致;3. 字符串的访问
(1)访问字符串原素
字符串原素在缓存中是已连续放置的,它们排好了队之后就会有两个队伍中的编号,称为“索引”,也叫“下标”;通过下标就可以快速访问每一原素了,具体方式为:
字符串名[原素下标]
这里也是用了被除数来表示原素下标位置,被称为“下标运算符”。比如a[2]就表示字符串a中下标为2的原素,可以取它的值输出,也可以对它赋值。
int a[] = {1,2,3,4,5,6,7,8};
cout << “a[2] = ” << a[2] << endl; // a[2] = 3
a[2] = 36;
cout << “a[2] = ” << a[2] << endl; // a[2] = 36
须要注意的是:
字符串的下标从0开始;因此a[2]访问的并不是字符串a的第2个原素,而是第三个原素;两个宽度为10的字符串,下标范围是0~9,而不是1~10;合理的下标,不能小于0,也不能大于 (字符串宽度 – 1);否则就会出现字符串下标越界;(2)字符串的大小不一
大部份的变量,都会在缓存中占有一定大小不一的内部空间;而正则表达式就决定了它具体的大小不一。而对于字符串这样的“A43EI235E类别”,由于每一原素类别完全相同,因此占据内部空间大小不一的计算遵循下面的简单公式:
字符串所占内部空间 = 正则表达式所占内部空间大小不一 * 原素特征值
这样一来,即使表述的时候没有指定字符串原素特征值,现在也可以计算得出了:
// a是已表述的字符串
cout << “a所占内部空间大小不一:” << sizeof(a) << endl;
cout << “每一原素所占内部空间大小不一:” << sizeof(a[0]) << endl;
int aSize = sizeof(a) / sizeof(a[0]);
cout << “字符串a的原素特征值:” << aSize << endl;
占用的大小不一(以字节为单位);字符串总大小不一,除以每特征值据原素的大小不一,是原素特征值。
(3)遍历字符串
如果想要依次访问字符串中大部份的原素,就叫做“遍历字符串”。我们当然可以用下标去挨个读取:
cout << “a[0] = ” << a[0] << endl;
cout << “a[1] = ” << a[1] << endl;
…
但这样显然太麻烦了。更好的方式是使用for循环:
int aSize = sizeof(a) / sizeof(a[0]);
for (int i = 0; i < aSize; i++ )
{
cout << “a[” << i << “] = ” << a[i] << endl;
}
循环条件如果写两个具体的数,很容易出现下标越界的情况;而如果知道了字符串宽度,直接让循环变量i小于它就可以了。
当然,这种写法还是稍显麻烦。C++ 11标准给我们提供更多了更简单的写法,是之前介绍过的范围for循环:
for (int num: a )
{
cout << num << endl;
}
4. 多维字符串
之前介绍的字符串只是统计数据最简单的排序方式。如果统计数据第一类排序成的不是“一队”,而是两个“方阵”,那显然就不能只用两个下标来表示了。我们可以对字符串进行扩展,让它从“一维”变成“二维”甚至“多维”。
int arr[3][4]; // 二维字符串,有三个元素,每一原素是两个宽度为4的int字符串
int arr2[2][5][10]; // 三维字符串
C++中本质上没有“多维字符串”这种东西,所谓的“多维字符串”,其实是“字符串的字符串”。
l 二维字符串int arr[3][4]表示:arr是两个有三个原素的字符串,其中的每一原素都是两个int字符串,包含4个原素;
l 三维字符串int arr2[2][5][10]表示:arr2是两个宽度为2的字符串,其中每一原素都是一个二维字符串;这个二维字符串有5个原素,每一原素都是两个宽度为10的int字符串;
一般最常见的是二维字符串。它有两个“维度”,第两个维度表示字符串本身的宽度,第二个表示每一原素的宽度;一般分别把它们叫做“行”和“列”。
(1)多维字符串的初始化
和普通的“一维”字符串那样,多维字符串初始化时,也可以用花括号括出来的几组数。使用嵌套的花括号可以让不同的维度更清晰:
正则表达式 字符串名[行数][列数] = {统计数据1, 统计数据2, 统计数据3, …};
正则表达式 字符串名[行数][列数] = {
{统计数据11, 统计数据12, 统计数据13, …},
{统计数据21, 统计数据22, 统计数据23, …},
…
};
须要注意:
内嵌的花括号不是必需的,因为字符串中的原素在缓存中已连续放置,可以用两个花括号将大部份统计数据括在一起;初始值的特征值,可以小于字符串表述的宽度,其它原素初始化为0值;这一点对整个二维字符串和每一行的一维字符串都适用;如果省略嵌套的花括号,当初始值特征值小于总原素特征值时,会按照顺序依次填充(填满第一行,才填第二行);其它原素初始化为0值;多维字符串的维度,可以省略第两个,由编译器自动推断;即二维字符串可以省略行数,但不能省略列数。// 嵌套的花括号的初始化
int ia[3][4] = {
{1,2,3,4},
{5,6,7,8},
{9,10,11,12}
};
// 只有一层花括号的初始化
int ia2[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
// 部分初始化,其余补0
int ia3[3][4] = {
{1,2,3},
{5,6}
};
int ia4[3][4] = {1,2,3,4,5,6};
// 省略行数,自动推断
int ia5[][4] = {1,2,3,4,5};
(2)访问统计数据
也可以用下标运算符来访问多维字符串中的统计数据,字符串的每两个维度,都应该有两个对应的下标。对于二维字符串来说,是须要指明“行号”“列号”,这相当于统计数据原素在二维矩阵中的坐标。
// 访问ia的第二行、第三特征值据
cout << “ia[1][2] = ” << ia[1][2] << endl;
// 修改ia的第一行、第二特征值据
ia[0][1] = 19;
同样须要注意,行号和列号都是从0开始、到 (原素特征值 – 1) 结束。
(3)遍历字符串
要想遍历字符串,当然须要使用for循环,而且要扫描每两个维度。对于二维字符串,我们须要对行和列分别进行扫描,这是两个双重for循环:
cout << “二维字符串总大小不一:” << sizeof(ia) << endl;
cout << “二维字符串每行大小不一:” << sizeof(ia[0]) << endl;
cout << “二维字符串每一原素大小不一:” << sizeof(ia[0][0]) << endl;
// 二维字符串行数
int rowCnt = sizeof(ia) / sizeof(ia[0]);
// 二维字符串列数
int colCnt = sizeof(ia[0]) / sizeof(ia[0][0]);
for (int i = 0; i < rowCnt; i++)
{
for (int j = 0; j < colCnt; j++)
{
cout << ia[i][j] << “\t”;
}
cout << endl;
}
同样,这里利用了sizeof运算符:
行数 = 二维字符串总大小不一 / 每行大小不一列数 = 每行大小不一 / 每一原素大小不一当然,也可以使用范围for循环:
for (auto & row : ia)
{
for (auto num : row)
{
cout << num << “\t”;
}
cout << endl;
}
这里的外层循环使用了auto关键字,这也是C++ 11新引入的特性,它可以自动推断变量的类别;前面的&是表述了两个“引用”。关于这部分内容,会在前面继续介绍。
5. 字符串的简单排序算法
数组排序指的是给定两个字符串,要求把其中的原素按照从小到大(或从大到小)顺序排序。
这是两个非常经典的需求,有各种不同的算法可以实现。我们这里介绍两种最基本上、最简单的排序算法。
(1)选择排序
选择排序是一种简单直观的排序算法。
它的工作原理:具体来说在未排序序列中找到最小(大)原素,放置到排序序列的起始位置,然后,再从剩余未排序原素中继续寻找最小(大)原素,然后追加到已排序序列的末尾。以此类推,直到大部份原素均排序完毕。

选择排序可以使用双重for循环很容易地实现:
#include<iostream>
using namespace std;
int main()
{
int arr[] = {5, 9, 2, 7, 4, 3, 12, 6, 1, 5, 7};
int size = sizeof(arr) / sizeof(arr[0]);
// 选择排序
for (int i = 0; i < size; i++)
{
for (int j = i + 1; j < size; j++)
{
if (arr[j] < arr[i])
{
// 如果arr[j]更小,就和arr[i]交换位置
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
// 输出
for (int num : arr)
cout << num << “\t”;
cin.get();
}
(2)冒泡排序
冒泡排序也是一种简单的排序算法。
它的基本上原理是:重复地扫描要排序的数列,一次比较两个原素,如果它们的大小不一顺序错误,就把它们交换过来。这样,一次扫描结束,我们可以确保最大(小)的值被移动到序列末尾。这个算法的名字由来,是因为越小的原素会经由交换,慢慢“浮”到数列的顶端。

冒泡排序的代码实现也非常简单,同样是使用双重for循环:
// 冒泡排序
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size – i – 1; j++)
{
if (arr[j] > arr[j+1])
{
int temp = arr[j+1];
arr[j+1] = arr[j];
arr[j] = temp;
}
}
}