1.一维数组的基本概念

发布时间 2023-08-03 20:06:06作者: CodeMagicianT

1.一维数组的基本概念

  数组是一组数据类型相同的变量,可以存放一组数据。

特点:

①数组中的每个数据元素具有相同的数据类型。

②数组占用一段连续的内存空间。

1)创建数组

  声明数组的语法:数据类型 数组名[数组长度];

  注意:数组长度必须是整数,可以是常量,也可以是变量和表达式

  C90规定必须用常量表达式指明数组的大小,C99允许使用整型非常量表达式。经测试,在VS中可以用整型非常量表达式,不能用变量;但是,Linux中还可以用变量。

2)数组的使用

  可以通过下标访问数组中元素,数组下标从0开始。

  数组中每个元素的特征和使用方法与单个变量完全相同。

  语法:数组名[数组下标]

注意:

●数组下标也必须是整数,可以是常量,也可以是变量

●合法的数组下标取值是:0~(数组长度-1)

3)数组占用内存的情况

  数组在内存中占用的空间是连续的。

  用sizeof(数组名)可以得到整个数组占用内存空间的大小(只适用于C++基本数据类型)。

4)数组的初始化

  声明的时候初始化:

  数据类型 数组名[数组长度] = { 值1,值2,值3, ...... , 值n};

int arr[length] = {1, 2, 3...};

  数据类型 数组名[ ] = { 值1,值2,值3, ...... , 值n};

int arr[] = {1, 2, ..., n};

  数据类型 数组名[数组长度] = { 0 }; // 把全部的元素初始化为0。

int arr[length] = {0};

  数据类型 数组名[数组长度] = { }; // 把全部的元素初始化为0。

int arr[length] = { };

注意:如果{}内不是数组长度个数据,剩余数据用0补全,但是,不建议这么用,你可能在数组中漏了某个值。如果想把数组中全部的元素初始化为0,可以在{}内只填一个0或什么也不填。

  C++11标准可以不写等于号。

5)清空数组

  用memset()函数可以把数组中全部的元素清零。(只适用于C++基本数据类型)

函数原型:

void *memset(void *s, int c, size_t n);

memset是C和C++标准库中的一个函数,其功能是把s指向的内存区域的前n个字节设置成字符c(转换为unsigned char类型)。

注意,在Linux下,使用memcpy()函数需要包含头文件#include <string.h>

6)复制数组

  用memcpy()函数可以把数组中全部的元素复制到另一个相同大小的数组。(只适用于C++基本数据类型)

函数原型:

void *memcpy(void *dest, const void *src, size_t n);

注意,在Linux下,使用memcpy()函数需要包含头文件#include <string.h>

示例:

#include <iostream>         // 包含头文件。
using namespace std;        // 指定缺省的命名空间。

int main()
{
	int bh[] = {3, 6, 1,6,7,4,3,5,6,7,8,322,2,3,9};               // 超女编号。
	string name[3];    // 超女姓名。
	
	for (int ii = 0; ii < sizeof(bh)/sizeof(int); ii++)
	{
		cout << "bh["<<ii<<"]=" << bh[ii] << endl;
	}

	int bh1[sizeof(bh) / sizeof(int)];   // 数组长度必须是整数,可以是常量,也可以是变量和表达式。

	memcpy(bh1, bh, sizeof(bh));      // 把数组bh中的内容复制到bh1。    

	for (int ii = 0; ii < sizeof(bh1) / sizeof(int); ii++)
	{
		cout << "bh1[" << ii << "]=" << bh1[ii] << endl;
	}
}

2.一维数组和指针

1)指针的算术

  将一个整型变量加1后,其值将增加1。

  但是,将指针变量(地址的值)加1后,增加的量等于它指向的数据类型的字节数。

2)数组的地址

a)数组在内存中占用的空间是连续的。

b)C++将数组名解释为数组第0个元素的地址。

c)数组第0个元素的地址和数组首地址的取值是相同的。

d)数组第n个元素的地址是:数组首地址+n

e)C++编译器把 数组名[下标] 解释为 *(数组首地址+下标)

3)数组的本质

  数组是占用连续空间的一块内存,数组名被解释为数组第0个元素的地址。C++操作这块内存有两种方法:数组解释法和指针表示法,它们是等价的。

4)数组名不一定会被解释为地址

  在多数情况下,C++将数组名解释为数组的第0个元素的地址,但是,将sizeof运算符用于数据名时,将返回整个数组占用内存空间的字节数。

可以修改指针的值,但数组名是常量,不可修改。

示例:

#include <iostream>         // 包含头文件。
using namespace std;        // 指定缺省的命名空间。

int main()
{
	char a;
	cout << "sizeof(char) =" << sizeof(char) << endl;            // 1字节
	short b;    
	cout << "sizeof(short) =" << sizeof(short) << endl;         // 2字节
	int c;         
	cout << "sizeof(int) =" << sizeof(int) << endl;                  // 4字节
	double d; 
	cout << "sizeof(double) =" << sizeof(double) << endl;   // 8字节

	cout << "a的地址是:" << (void*)&a << endl;
	cout << "a的地址+1是:" << (void*)(&a + 1) << endl;

	cout << "b的地址是:" << (void*)&b << endl;
	cout << "b的地址+1是:" << (void*)(&b + 1) << endl;

	cout << "c的地址是:" << (void*)&c << endl;
	cout << "c的地址+1是:" << (void*)(&c + 1) << endl;

	cout << "d的地址是:" << (void*)&d << endl;
	cout << "d的地址+1是:" << (void*)(&d + 1) << endl;

	return 0;
}
#include <iostream>         // 包含头文件。
using namespace std;        // 指定缺省的命名空间。

int main()
{
	double a[5];

	cout << "a的值是:" << (long long) a << endl;
	cout << "&a的值是:" << (long long)&a << endl;

	cout << "a[0]的地址是:" << (long long) &a[0] << endl;
	cout << "a[1]的地址是:" << (long long) &a[1] << endl;
	cout << "a[2]的地址是:" << (long long) &a[2] << endl;
	cout << "a[3]的地址是:" << (long long) &a[3] << endl;
	cout << "a[4]的地址是:" << (long long) &a[4] << endl;

	double* p = a;
	cout << "p的值是:" << (long long)p << endl;
	cout << "p+0的值是:" << (long long)(p+  0) << endl;
	cout << "p+1的值是:" << (long long)(p + 1) << endl;
	cout << "p+2的值是:" << (long long)(p + 2) << endl;
	cout << "p+3的值是:" << (long long)(p + 3) << endl;
	cout << "p+4的值是:" << (long long)(p + 4) << endl;
    
        return 0;
}
#include <iostream>         // 包含头文件。
using namespace std;        // 指定缺省的命名空间。

int main()
{
	int a[5] = { 3 , 6 , 5 , 8 , 9 };

	// 用数组表示法操作数组。
	cout << "a[0]的值是:" << a[0] << endl;
	cout << "a[1]的值是:" << a[1] << endl;
	cout << "a[2]的值是:" << a[2] << endl;
	cout << "a[3]的值是:" << a[3] << endl;
	cout << "a[4]的值是:" << a[4] << endl;

	// 用指针表示法操作数组。
	int* p = a;
	cout << "*(p+0)的值是:" << *(p+  0) << endl;
	cout << "*(p+1)的值是:" << *(p + 1) << endl;
	cout << "*(p+2)的值是:" << *(p + 2) << endl;
	cout << "*(p+3)的值是:" << *(p + 3) << endl;
	cout << "*(p+4)的值是:" << *(p + 4) << endl;
}

5)指针也是迭代器

  vector 和string的迭代器支待的运算,数组的指针全都支待。例如,允许使用递增运算符将指向数组元素的指针向前移动到下一个位置上:

int arr[]= {0,1,2,3,4,5,6,7,8,9}; 
int *p= arr;//p指向arr的第一个元素
++p; 
//p指向arr[1]

我们可以设法获取数组尾元素之后的那个并不存在的元素的地址:

int *e = &arr[10];//指向arr尾元素的下一位置的指针

  这里显然使用下标运算符索引了一个不存在的元素,arr有10个元素,尾元素所在位置的索引是9,接下来那个不存在的元素唯一的用处就是提供其地址用于初始化e。就像尾后迭代器一样,尾后指针也不指向具体的元素。

6)指针运算

  和迭代器一样,两个指针相减的结果是它们之间的距离。参与运算的两个指针必须指向同一个数组当中的元素:

auto n = end(arr) - begin(arr);//n的值是5,也就是arr中元素的数量

  两个指针相减的结果的类型是种名为ptrdiff_t的标准库类型,和size_t一样,ptrdiff_t也是一种定义在cstddef头文件中的机器相关的类型。因为差值可能为负值,所以ptrdiff_t是一种带符号类型。

  只要两个指针指向同一个数组的元素,或者指向该数组的尾元素的下一位置,就能利用关系运算符对其进行比较。例如,可以按照如下的方式遍历数组中的元素:

int *b = arr, *e = arr + sz;
while(b < e)
{
    //使用*b
    ++b;
}

如果两个指针分别指向不相关的对象,则不能比较它们:

inti= 0, sz = 42; 
int *p = &i, *e = &sz; 
//未定义的:p和e元关,因此比较毫无意义!
while (p < e) 

  尽管作用可能不是特别明显,但必须说明的是,上述指针运算同样适用于空指针和所指对象并非数组的指针。在后一种情况下,两个指针必须指向同一个对象或该对象的下一位置。如果p是空指针,允许给p加上或减去一个值为0的整型常量表达式。两个空指针也允许彼此相减,结果当然是0。

3.一维数组用于函数的参数

1)指针的数组表示

在C++内部,用指针来处理数组。

C++编译器把数组名[下标] 解释为 *(数组首地址+下标)

C++编译器把地址[下标] 解释为 *(地址+下标)

2)一维数组用于函数的参数

  一维数组用于函数的参数时,只能传数组的地址,并且必须把数组长度也传进去,除非数组中有最后一个元素的标志。

书写方法有两种:

void func(int* arr, int len);
void func(int arr[], int len);

注意:

在函数中,可以用数组表示法,也可以用指针表示法。

在函数中,不要对指针名用sizeof运算符,它不是数组名。

示例:

#include <iostream>         // 包含头文件。
using namespace std;        // 指定缺省的命名空间。

int main()
{
	char a[20];             // 这是一个长度为20的字符型数组。

	int* p = (int *)a;        // 让整型指针p指向数组a的内存。

	for (int ii = 0; ii < 6; ii++)
	{
		p[ii] = ii + 300;     // 用数组表示法操作指针。
	}

	for (int ii = 0; ii < 6; ii++)
	{
		cout << "*(p+" << ii << ")的值是:" << *(p + ii) << endl;    // 地址[下标]  解释为  *(地址+下标)。
	}
}
#include <iostream>         // 包含头文件。
using namespace std;        // 指定缺省的命名空间。

// void func(int *arr,int len)
void func(int arr[],int len)
{
	for (int ii = 0; ii < len; ii++)
	{
		cout << "arr[" << ii << "]的值是:" << arr[ii] << endl;              // 用数组表示法操作指针。
		cout << "*(arr+" << ii << ")的值是:" << *(arr + ii) << endl;   // 地址[下标]  解释为  *(地址+下标)。
	}
}

int main()
{
	int a[] = {2,8,4,6,7,1,9};
	
	func(a, sizeof(a) / sizeof(int));
}

4.用new动态创建一维数组

  普通数组在栈上分配内存,栈很小;如果需要存放更多的元素,必须在堆上分配内存。

  动态创建一维数组的语法:数据类型 *指针=new 数据类型[数组长度];

  释放一维数组的语法:delete[] 指针;

注意:

●动态创建的数组没有数组名,不能用sizeof运算符。

● 可以用数组表示法和指针表示法两种方式使用动态创建的数组。

●必须使用delete[]来释放动态数组的内存(不能只用delete)。

● 不要用delete[]来释放不是new[]分配的内存。

●不要用delete[]释放同一个内存块两次(否则等同于操作野指针)。

●对空指针用delete[]是安全的(释放内存后,应该把指针置空nullptr)。

●声明普通数组的时候,数组长度可以用变量,相当于在栈上动态创建数组,并且不需要释放。

● 如果内存不足,调用new会产生异常,导致程序中止;如果在new关键字后面加(std::nothrow)选项,则返回nullptr,不会产生异常。

●为什么用delete[]释放数组的时候,不需要指定数组的大小?因为系统会自动跟踪已分配数组的内存。

示例:

#include <iostream>         // 包含头文件。
using namespace std;        // 指定缺省的命名空间。

int main()
{
	int *arr=new int[8];          // 创建8个元素的整型数组。

	for (int ii = 0; ii < 8; ii++)
	{
		arr[ii] = 100 + ii;                                                                  // 数组表示法。
		cout << "arr[" << ii << "]=" << *(arr + ii) << endl;        // 指针表示法。
	}

	delete[]arr;
}

5.一维数组的排序qsort

qsort()函数用于对各种数据类型的数组进行排序。

函数的原型:

void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));

第一个参数:数组的起始地址。

第二个参数:数组元素的个数(数组长度)。

第三个参数:数组元素所占的大小(sizeof(数组的数据类型))。

第四个参数:回调函数的地址。

回调函数决定了排序的顺序,声明如下:

int compar(const void *p1, const void *p2);

1)如果函数的返回值<0,那么p1所指向元素会被排在p2所指向元素的前面。

2)如果函数的返回值==0,那么p1所指向元素与p2所指向元素的顺序不确定。

3)如果函数的返回值>0,那么p1所指向元素会被排在p2所指向元素的后面。

void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));

qsort()函数的其它细节:

●形参中的地址用void是为了支持任意数据类型,在回调函数中必须具体化。

●为什么需要第三个形参size_t size?

●size_t是C标准库中定义的,在64位系统中是8字节无符号整型(unsigned long long)。

typedef unsigned long long size_t

●排序的需求除了升序和降序,还有很多不可预知的情况,只能用回调函数。

示例:

#include <iostream>         // 包含头文件。
using namespace std;        // 指定缺省的命名空间。

int compasc(const void* p1, const void* p2)         // 升序的回调函数。
{
	return *((int*)p1) - *((int*)p2);
}

int compdesc(const void* p1, const void* p2)       // 降序的回调函数。
{
	return *((int*)p2) - *((int*)p1);
}

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

	qsort(a,sizeof(a)/sizeof(int),sizeof(int),compasc);                   // 对数组a进行升序排序。

	for (int ii = 0; ii < 8; ii++)
	{
		cout << "a[" << ii << "]=" << a[ii] << endl;
	}

	qsort(a, sizeof(a) / sizeof(int), sizeof(int), compdesc);            // 对数组a进行降序排序。

	for (int ii = 0; ii < 8; ii++)
	{
		cout << "a[" << ii << "]=" << a[ii] << endl;
	}
}

6.一维数组的查找-折半查找

折半查找也叫二分查找,只适用于已排序的数组(升序降序都可以)。

示例:

#include <iostream>         // 包含头文件。
using namespace std;        // 指定缺省的命名空间。

// 在arr中查找key,成功返回key在arr中的数组下标,失败返回-1。
int search(int arr[], int len, int key)
{
	int low = 0, high = len - 1, mid;          // 初始化:low=0,high=数组长度-1。

	while (low <= high)
	{
		mid = (low + high) / 2;     // 计算mid指针的位置。

		if (arr[mid] == key)
			return mid;         // 查找成功。     
		else if (arr[mid] > key)
			high = mid - 1;  // 继续在前半区查找。
		else
			low = mid + 1;                    // 继续在后半区查找。
	}

	return -1; // 查找失败。
}

int main()
{
	int a[10] = { 7,9,12,16,21,25,30,35,41,48 };    // 必须是已排好序的数组。

	if (search(a, 10, 30) >= 0) cout << "在数组a中查找30成功。\n";
	else cout << "在数组a中查找30失败。\n";
}

7.二维数组

一维数组的数学概念是线性表,二维数组的数学概念是矩阵。

1)创建二维数组

声明二维数组的语法:数据类型 数组名[行数][列数];

注意:数组长度必须是整数,可以是常量,也可以是变量和表达式

C90规定必须用常量表达式指明数组的大小,C99允许使用整型非常量表达式。经测试,在VS中可以用用整型非常量表达式,不能用变量;但是,Linux中还可以用变量。

2)二维数组的使用

可以通过行下标和列下标访问二维数组中元素,下标从0开始。

二维数组中每个元素的特征和使用方法与单个变量完全相同。

语法:数组名[行下标][列下标]

注意:

●二维数组下标也必须是整数,可以是常量,也可以是变量

●合法的行下标取值是:0~(行数-1)

●合法的列下标取值是:0~(列数-1)

3)二维数组占用内存的情况

用sizeof(数组名)可以得到整个二维数组占用内存空间的大小(只适用于C++基本数据类型)。

二维数组在内存中占用的空间是连续的。

4)二维数组的初始化

声明的时候初始化:

数据类型 数组名[行数][列数] = { {数据1,数据2 } ,{数据3,数据4 },...... };

数据类型 数组名[行数][列数] = { 数据1,数据2,数据3,数据4, ......};

数据类型 数组名[ ][列数] = { 数据1,数据2,数据3,数据4,......};

数据类型 数组名[行数][列数] = { 0 }; // 把全部的元素初始化为0。

数据类型 数组名[行数][列数] = { }; // 把全部的元素初始化为0。

注意:如果{}内不足数组长度个数据,剩余数据用0补全,但是,不建议这么用,你可能在数组中漏了某个值。如果想把数组中全部的元素初始化为0,可以在{}内只填一个0或什么也不填。

​ C++11标准可以不写等于号。

5)清空二维数组

用memset()函数可以把二维数组中全部的元素清零。(只适用于C++基本数据类型)

函数原型:void *memset(void *s, int c, size_t n);

注意,在Linux下,使用memcpy()函数需要包含头文件#include <string.h>

6)复制二维数组

用memcpy()函数可以把二维数组中全部的元素复制到另一个相同大小的数组(没说多少维)。(只适用于C++基本数据类型)

函数原型:void *memcpy(void *dest, const void *src, size_t n);

注意,在Linux下,使用memcpy()函数需要包含头文件#include <string.h>

示例:

#include <iostream>         // 包含头文件。
using namespace std;        // 指定缺省的命名空间。

int main()
{
	// int bh[2][3] = { {11,12,13},{21,22,23} };                              // 声明一个两行三列的二维数组,存放超女的编号。
	// int bh[2][3] = { 11,12,13,21,22,23 };
	int bh[][3] = { 11,12,13,21,22,23 };

	/*bh[0][0] = 11;		bh[0][1] = 12;    bh[0][2] = 13;
	bh[1][0] = 21; 	bh[1][1] = 22;    bh[1][2] = 23;*/
	
	/*cout << "bh[0][0] = " << bh[0][0] << "  bh[0][1] = " << bh[0][1] << "  bh[0][2] = " << bh[0][2] << endl;
	cout << "bh[1][0] = " << bh[1][0] << "  bh[1][1] = " << bh[1][1] << "  bh[1][2] = " << bh[1][2] << endl;*/

	for (int ii = 0; ii < 2; ii++)                 // 第一层循环表示行数,循环继续的条件是计数器小于行数。
	{
		for (int jj = 0; jj < 3; jj++)              // 第二层循环表示列数,循环继续的条件是计数器小于列数。
		{
			cout << "&bh["<<ii<<"]["<<jj<<"] = " << (long long) & bh[ii][jj] << "  ";          // 处理二维数组的每个元素。
		}
		cout << endl;          // 每处理一行数据后,输出一个换行。
	}

	int* p = (int *)bh;

	for (int ii = 0; ii < 6; ii++)
	{
		cout << "p[" << ii << "]=" << p[ii] << endl;     // 一维数组的数组表示法。
	}
}

8.二维数组用于函数的参数

int* p;   // 整型指针。
int* p[3];  // 一维整型指针数组,元素是3个整型指针(p[0]、p[1]、p[2])。
int* p();  // 函数p的返回值类型是整型的地址。
int (*p)(int ,int);  // p是函数指针,函数的返回值是整型。

1)行指针(数组指针)

声明行指针的语法:数据类型 (*行指针名)[行的大小]; // 行的大小即数组长度。

int (*p1)[3];  // p1是行指针,用于指向数组长度为3的int型数组。
int (*p2)[5]; // p2行指针,用于指向数组长度为5的int型数组。
double (*p3)[5]; // p3是行指针,用于指向数组长度为5的double型数组。

一维数组名被解释为数组第0个元素的地址。

对一维数组名取地址得到的是数组的地址,是行地址。

2)二维数组名是行地址

int bh[2][3] = { {11,12,13},{21,22,23} };

bh是二维数组名,该数组有两行元素,每一个元素本身又是一个数组长度为3的整型数组。

bh被解释为数组长度为3的整型数组类型的行地址。

如果存放bh的值,要用数组长度为3的整型数组类型的行指针。

int (*p)[3]=bh;
int bh[4][2][3];

bh是三维数组名,该数组有4个元素,每一个元素本身又是一个2行3列的二维数组。

bh被解释为2行3列的二维数组类型的二维地址。

如果存放bh的值,要用2行3列的二维数组类型的二维指针。

int (*p)[2][3]=bh;

3)把二维数组传递给函数

如果要把bh传给函数,函数的声明如下:

void func(int (*p)[3],int len);
void func(int p[][3],int len);

示例:

#include <iostream>         // 包含头文件。
using namespace std;        // 指定缺省的命名空间。

int main()
{
	int a[10];

	cout << "数组a第0个元素的地址:" << a<< endl;    
	cout << "数组a的地址:"      << &a << endl;           

	cout << "数组a第0个元素的地址+1:" << a + 1 << endl;   // 地址的增加量是4。
	cout << "数组a的地址+1:" << &a + 1 << endl;                // 地址的增加量是40。
}
#include <iostream>         // 包含头文件。
using namespace std;        // 指定缺省的命名空间。

// void func(int(*p)[3], int len)
void func(int p[][3], int len)
{
	for (int ii = 0; ii < len; ii++)
	{
		for (int jj = 0; jj < 3; jj++)
			cout << "p[" << ii << "][" << jj << "]=" << p[ii][jj] << "  " ;

		cout << endl;
	}
}

int main()
{
	int bh[2][3] = { {11,12,13},{21,22,23} };

	func(bh,2);
}
#include <iostream>         // 包含头文件。
using namespace std;        // 指定缺省的命名空间。

void func(int (*p)[2][3])
{
	int ii = 1;
	// 遍历三维数组p,给它的每个元素赋值。
	for (int a = 0; a < 4; a++)
		for (int b = 0; b < 2; b++)
			for (int c = 0; c < 3; c++)
				p[a][b][c] = ii++;
}

int main()
{
	int bh[4][2][3];        // 假设有4个超女方阵,每个方阵有2行,每行有3个超女。

	memset(bh, 0, sizeof(bh));

	func(bh);

	for (int a = 0; a < 4; a++)
	{
		for (int b = 0; b < 2; b++)
		{
			for (int c = 0; c < 3; c++)
				cout << bh[a][b][c] <<"\t";

			cout << endl;    // 每显示一行后,输出一个换行符。
		}

		cout << endl<<endl;    // 每显示一个方阵后,输出两个换行符。
	}
}

参考资料:

来源于慕课网和C++ Primer

9.STL中的array

1.array介绍

在C++标准库中,array是固定大小的序列容器,array中包含特定个数并且严格按照线性序列排序的元素。因此array允许对元素进行随机访问,指向某一元素的指针可以通过偏移访问其他元素。在array内部,它只保存自己包含的元素,其他任何信息都不保存,包括自身的大小。

array是C++ 标准模板库的一部分,因此,想要使用array,需要在程序中包含头文件array

 #include <array> 

2.array定义和初始化

格式:
包含头文件array之后,我们可以使用下边的格式定义array

std::array<object_type,size> variable_name;

object_type规定了array中可以存放哪种类型的元素。
size为array的大小。
variable_name为array名。

方式: 定义array的常用方式如下所示:

std::array<int, 5> n;
std::array<int, 5> n = {1, 2, 3, 4, 5};
std::array<int, 5> n { {1, 2, 3, 4, 5} };

示例代码

3.array的迭代器

array中的迭代器包括以下几个,分别为:

array.begin():指向array首元素的迭代器
array.end():指向array尾元素下一个位置的迭代器
array.rbegin():指向array尾元素的反向迭代器,即rbegin()指向尾元素,rbegin-1指向倒数第二个元素
array.rend():指向array头元素前一个位置的反向迭代器,即rend()指向头元素前一个位置元素,rbegin-1指向第一个元素
array.cbegin():指向array首元素的迭代器
array.cend():指向array尾元素下一个位置的迭代器
array.crbegin():指向array尾元素的反向迭代器
array.crend():指向array头元素前一个位置的反向迭代器

代码示例如下:

#include <array>
#include <iostream>

using std::array;
using std::cout;
using std::endl;

int main() 
{
    array<int, 5> myarray = { 1, 2, 3, 4, 5 };
    cout << "初始化后array为: ";
    for (auto num : myarray) 
    {
        cout << num << " ";
    }
    cout << endl;

    // array.begin()为指向array头元素的迭代器
    auto begin_iterator = myarray.begin();
    cout << "begin() 指向的元素:" << *begin_iterator << endl;

    // array.end()为指向array尾元素后一个位置的迭代器,则myarray.end()-1指向尾元素
    auto end_iterator = myarray.end();
    cout << "end()-1 指向的元素:" << *(end_iterator - 1) << endl;

    // array.rbegin()为指向尾元素的迭代器,即反向(r)的头(begin)迭代器
    auto rbegin_iterator = myarray.rbegin();
    cout << "rbegin() 指向的元素:" << *rbegin_iterator << endl;

    // array.rend()为指向头元素的前一个位置的迭代器,即反向(r)尾(end)迭代器,则myarray.rend()-1指向头元素
    auto rend_iterator = myarray.rend();
    cout << "rend()-1 指向的元素:" << *(rend_iterator - 1) << endl;

    // array.cbegin()为指向array头元素的const迭代器,与begin()不同的是返回迭代器类型为array<int>::const_iterator
    auto cbegin_iterator = myarray.cbegin();
    cout << "cbegin() 指向的元素:" << *cbegin_iterator << endl;

    // array.cend()为指向array尾元素下一个位置的const迭代器,与end()不同的是返回迭代器类型为array<int>::const_iterator
    auto cend_iterator = myarray.cend();
    cout << "cend()-1 指向的元素:" << *(cend_iterator - 1) << endl;

    // array.crbegin()为指向尾元素的const迭代器,即反向(r)的const(c)头(begin)迭代器
    auto crbegin_iterator = myarray.crbegin();
    cout << "crbegin() 指向的元素: " << *crbegin_iterator << endl;

    // array.crend()为指向头元素下一个位置的const迭代器,即反向(r)的const(c)尾(end)迭代器
    auto crend_iterator = myarray.crend();
    cout << "crend()-1 指向的元素: " << *(crend_iterator - 1) << endl;
    
    return 0;
}

输出:

初始化后array为: 1 2 3 4 5
begin() 指向的元素:1
end()-1 指向的元素:5
rbegin() 指向的元素:5
rend()-1 指向的元素:1
cbegin() 指向的元素:1
cend()-1 指向的元素:5
crbegin() 指向的元素: 5
crend()-1 指向的元素: 1

4.array大小(容量)相关方法

4.1 size()——元素个数

要想知道array中有多少元素,使用array.size()方法,作用是返回array中元素的个数。示例如下:

示例代码

#include <iostream>
#include <array>
using std::cout;
using std::endl;
using std::array;

int main()
{
    array<int, 5> myarray = { 1,2,3,4,5 };

    cout << "初始化后的myarray为:";
    for (auto num : myarray) 
    {
        cout << num << " ";
    }

    int sizeOfArray = myarray.size();

    cout << "\nmyarray中的元素个数为:" << sizeOfArray;
    return 0;
}

输出:

初始化后的myarray为:1 2 3 4 5
myarray中的元素个数为:5

4.2 max_size()——最多能容纳元素个数:

要想知道array最多可以有多少元素,使用array.max_size()方法,作用是返回array中最多能容纳元素个数。在array容器中,array中最多能容纳的元素个数即为array中实际元素个数,因为array容器固定,不可以扩展或者收缩。

示例如下:

#include <iostream>
#include <array>
using std::cout;
using std::endl;
using std::array;

int main()
{
    array<int, 5> myarray = { 1,2,3,4,5 };
    cout << "初始化后的myarray为:";
    for (auto num : myarray)
    {
        cout << num << " ";
    }

    int sizeOfArray = sizeof(myarray)/sizeof(int);
    cout << "\nmyarray.size为:" << sizeOfArray;
    int max_sizeOfArray = myarray.max_size();
    cout << "\nmyarray.max_size为:" << max_sizeOfArray;

    return 0;
}

4.3empty()——检查array是否为空

想要检查array是否为空,使用array.empty()方法,如果为空返回true,否则返回false。

示例如下:

#include <array>
#include <iostream>
using std::array;
using std::cout;
using std::endl;

int main()
{
    array<int, 5> myarray = { 1, 2, 3, 4, 5 };
    array<int, 0> myarray2;
    cout << "初始化后的myarray为:";
    for (auto num : myarray) 
    {
        cout << num << " ";
    }

    cout << "\n初始化后的myarray2为:";
    for (auto num : myarray2)
    {
        cout << num << " ";
    }

    bool isEmpty = myarray.empty();
    bool isEmpty2 = myarray2.empty();
    cout << "\nmyarray.empty() = " << isEmpty << endl;
    cout << "myarray2.empty() = " << isEmpty2 << endl;

    return 0;
}

输出:

初始化后的myarray为:1 2 3 4 5
初始化后的myarray2为:
myarray.empty() = 0
myarray2.empty() = 1

5.array常用操作

5.1at()——访问array元素

使用元素的索引来访问array中的元素,在array中,可以使用array.at(index)或者array[index]访问索引为index的元素,示例代码如下:

#include<iostream>
#include<array>

using std::endl;
using std::cout;
using std::array;

int main() 
{
    array<int, 5> myarray = { 1,2,3,4,5 };

    cout << "myarray索引为0的元素: " << myarray.at(0) << endl;
    cout << "myarray索引为1元素: " << myarray.at(1) << endl;
    cout << "myarray索引为2元素: " << myarray[2] << endl;

    return 0;
}

输出:

myarray索引为0的元素: 1
myarray索引为1元素: 2
myarray索引为2元素: 3

两种访问方法的优劣
虽然使用 array.at(index)和array[index]的方式都能访问索引为index的元素,但是,如果此元素不存在,即索引越界时, array.at(index)会抛出一个异常,但是array[index]会返回一个垃圾值,因此,最好使用array.at(index),在其他容器中也是这样。

5.2front()——访问array头元素

front()返回array第一个元素

示例如下:

#include <array>
#include <iostream>
using std::array;
using std::cout;
using std::endl;

int main()
{
    array<int, 5> myarray = { 1, 2, 3, 4, 5 };

    cout << "初始化后的myarray为:";
    for (auto num : myarray) 
    {
        cout << num << " ";
    }
    int front = myarray.front();
    cout << "\nmyarray中的第一个元素为:" << front;

    return 0;
}

输出:

初始化后的myarray为:1 2 3 4 5
myarray中的第一个元素为:1

5.3back()——访问array尾元素

front()返回array最后一个元素

示例如下:

#include <array>
#include <iostream>
using std::array;
using std::cout;
using std::endl;

int main()
{
    array<int, 5> myarray = { 1, 2, 3, 4, 5 };

    cout << "初始化后的myarray为:";
    for (auto num : myarray)
    {
        cout << num << " ";
    }

    int back = myarray.back();
    cout << "\nmyarray中的最后一个元素为:" << back;

    return 0;
}

输出:

初始化后的myarray为:1 2 3 4 5
myarray中的最后一个元素为:5

5.4data()——返回指向array中第一个元素的指针

使用array.data()会返回指向array中第一个元素的指针,因为array中元素线性排列,则可以使用data()的偏移来访问array中其他的元素。

示例如下:

#include <iostream>
#include <array>
using std::cout;
using std::endl;
using std::array;

int main() 
{
    array<int, 5> myarray = { 4,3,1,2,6 };

    cout << "初始化后的myarray为:";
    for (auto num : myarray) 
    {
        cout << num << " ";
    }
    int* sizeOfArray = myarray.data();
    cout << "\nmyarray中第一个元素为:" << *sizeOfArray;
    cout << "\nmyarray中第二个元素为:" << *(sizeOfArray + 1);
    cout << "\nmyarray中第五个元素为:" << *(sizeOfArray + 4);

    return 0;
}

输出:

初始化后的myarray为:4 3 1 2 6
myarray中第一个元素为:4
myarray中第二个元素为:3
myarray中第五个元素为:6

5.5fill(n)——使用n填充array

可以使用array.fill(n)来填充array,执行的结果是数组中的元素都变为n。

示例如下:

#include <iostream>
#include <array>
using std::cout;
using std::endl;
using std::array;

int main() 
{
    // 定义一个array,未初始化
    array<int, 5> myarray;
    myarray.fill(2);
    cout << "fill(2)后的myarray为:";
    for (auto num : myarray) 
    {
        cout << num << " ";
    }

    myarray.fill(5);
    cout << "\nfill(5)后的myarray为:";
    for (auto num : myarray) 
    {
        cout << num << " ";
    }

    return 0;
}

输出:

fill(2)后的myarray为:2 2 2 2 2
fill(5)后的myarray为:5 5 5 5 5

5.6swap()——交换两个array容器中的元素

可以使用swap()来交换两个array中的元素,前提是两个array容器中存储的元素类型以及元素个数都相同 。

示例如下:

#include <array>
#include <iostream>
using std::array;
using std::cout;
using std::endl;

int main()
{
    array<int, 5> myarray1 = { 1, 2, 3, 4, 5 };
    array<int, 5> myarray2 = { 11, 22, 33, 44, 55 };

    cout << "初始化后的myarray1为:";
    for (auto num : myarray1) 
    {
        cout << num << " ";
    }

    cout << "\n初始化后的myarray2为:";
    for (auto num : myarray2) 
    {
        cout << num << " ";
    }

    myarray1.swap(myarray2);
    cout << "\n交换元素后myarray1为:";
    for (auto num : myarray1)
    {
        cout << num << " ";
    }
    cout << "\n交换元素后myarray2为:";
    for (auto num : myarray2)
    {
        cout << num << " ";
    }

    return 0;
} 

输出:

初始化后的myarray1为:1 2 3 4 5
初始化后的myarray2为:11 22 33 44 55
交换元素后myarray1为:11 22 33 44 55
交换元素后myarray2为:1 2 3 4 5

6.array容器和数组的区别与联系

联系:

●都使用连续空间存储元素,可以进行随机访问
●元素个数都固定
区别:

●数组是不安全的,array是比较安全的,array避免访问越界
●array提供了更好的数据访问机制,即可以使用front()和back()以及at()访问方式,使得访问更加安全。而数组只能通过下标访问,在写程序中很容易出现越界的错误
●array容器支持迭代器,访问遍历更加方便
●array提供了size()和Empty(),而数组只能通过sizeof()/strlen()以及遍历计数来获取大小和是否为空

参考:C++array容器用法解析,它与普通数组究竟有何不同?