数组基础篇(对应C++ Primer plus 4.10)
概要:数组是由一组同类型的元素组成的集合,在内存上是一片连续的存储空间。C++提供了三种数组的表示方法:普通数组,模板类vector(C++98 新增的标准模板库STL提供该模板类)和模板类array(C++11新增)
下面分别讨论这三种类型的数组是如何使用的以及他们的区别:
一,这三种数组如何被定义?初始化?元素如何被访问?
(1)普通数组
普通数组定义和初始化:
typename vt[n_elem]; //数组vt长度n_elem必须是定常量或常量表达式
int a1[10]={1,3,4}; //定义由10个int型变量的数组a1,并用列表中数值依次对数组元素进行初始化(不足项默认为0)
int a1[]={1,3,4}; //定义由3个int型变量的数组a1,(这里定义数组时长度可以省略)
char a1[]="i i" //定义由4个char型变量组成的数组a1(注意别忘了字符串中间的空格和字符串结尾省略的空字符,另外中文字符占2字节,英文、空格、结尾空字符都占一个字节)
普通数组元素的访问:
int a1[3]={1,3,4};
cout<<a1[2]<<*(a1+2)<<endl; //可以通过数组名和元素下标来访问数组的元素;a1[2]相当于*(a1+2),数组名a1被解析为第一个元素的地址,这里即引用第一个元素往后移动两个元素位置的地址上数值。
int p1=a1; //使用指针指向数组首地址后即可像数组名那样访问数组元素(而且是引用不是copy),
cout<<p1[2]<<endl; //p1[2]相当于a1[2], 即p1[2]---a1[2]---*(p1+2)---*(a1+2)
int p2=&a1[1];
cout<<p2[1]<<*(p2+1)<<*(a1+1+1)<<a1[2]<<end; //这三者等
(2)模板类vector(动态数组)
模板类vectot的定义和初始化:
vector<typename>vt(n_elem); //注意,vector对象vt的长度n_elem可以是常量也可以是变量;在运行阶段根据实际需要设置它的长度;
#include<vector>
...
using name space std;
//
vector<int> a2; 创建由0个int型变量组成的vector对象,内存为0;
a2.push_back(0)
a2.push_back(1) //.push_back为vector模板类的一个方法,用于给数组扩容,即往数组尾部插入变量;注意,这里不能用a2[i]=数值;否则因vector数值越界而程序中断;
a2.push_back(2) //vector对象的存储空间每次扩大时都会重新申请一片空间,然后将旧空间存储的值copy过来,最后将就空间释放;所以vector功能虽强,但代价是效率低。
//
vector<int>a2(10); //创建由10个int变量组成的vector对象;
a[0]=0;
...
a[9]=9; //注意,C++98不支持用列表初始化vector对象,仅普通数组和array对象支持列表初始化;但在C++11新增了vector的列表初始化;
模板类vectot对象的元素访问:
同上面普通数组,可用vector对象名和元素下标访问a2[i],也可以用指向vector对象的指针访问 ;注意区别在于vector对象名不能被解析为第一个元素的地址。
int n;
cin >>n;
vector<int>a2(n);
a2[2] = 2;
int* p = &a2[0];
cout << a2[2] << p[2] << *(p + 2); //这三者等效
*遗留问题1:vector对象内存空间如何缩小?具体的原理是什么?
(3)模板类array
模板类array的定义和初始化
array<typename,n_elem>vt; //同普通数组一样,创建array对象必须指定长度,n_elem须是常量或常量表达式。
#include<array>
...
using name space std;
array<int ,3>a3={1,2,3} //定义由三个int型变量组成的array对象,并用列表里数值对array对象的元素进行初始化。
模板类array对象的元素访问:
对array对象的元素进行访问时,同普通数组和vector对象一样,都能通过对象名和元素下标进行访问,或者用指向对象的指针进行访问,
但同样,array对象名和vector对象名一样,不能像普通数组名称那样被解析为首元素的地址,即&a2[0]不等同于a2,&a3[0]不等同于a[3]
示例如下:
array<int, 3>a3 = { 1,2,3 };
int* p = &a3[0];
cout << a3[2] <<p[2]<<*(p+2) << endl;
二,三种数组的比较
1-就灵活性和功能上来说,模板类vecotor具有更高的灵活性,体现在其定义时不用指定具体的长度,而是可以在程序运行时根据需要确定其长度大小,另外,当vector对象的存储空间不够用时,会自动扩容,而无需手动分配内存。
2-就效率而言,由于vector对象每次扩容时都会重新申请一片更大的内存空间,然后将数据从原来的存储空间copy过来,再释放原来的存储空间;这样的过程将大大降低效率;而普通数组与array对象都是长度固定的,也使用栈(静态内存分配),而不是自由存储区,因此二者效率一样,都比vector高效。
3-安全性上:C++/C不会检查数组越界的情况,如a1[-2]=3,而array和vector提供了检查数组越界的方法.at(),使用at(i)访问元素时,会先判断小标i是否合法,从而提高了安全性。
4-方便性,vector和array对象可以进行赋值操作,而内置数组只能逐个元素copy,另外,vector和array有很多方便的api 如获取长度.size(),判断是都为空.empty(),而数组只能通过sizeof() / strlen()以及遍历计数来获取大小和是否为空。
5.vector和array提供了更好的遍历机制,即有正向迭代器和反向迭代器
6.vector和array提供了两个容器对象的内容交换,即swap()的机制,而数组对于交换只能通过遍历的方式逐个交换元素
7.array提供了初始化所有成员的方法fill()
8.由于vector的动态内存变化的机制,在插入和删除时,需要考虑迭代的是否有效问题
9.vector和array在声明变量后,在声明周期完成后,会自动地释放其所占用的内存。对于数组如果用new[] / malloc申请的空间,必须用对应的delete[]和free来释放内存
10.array对象和数组存储在相同的内存区域(栈)中,vector对象存储在自由存储区(堆)。
------------总结
通过以上比较,以后如需动态数组,使用vector,如需固定长度数组,选用array.
另外,学习栈和堆后回顾一下array和内置数组、vector的存储原理。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 提示词工程——AI应用必不可少的技术
· 地球OL攻略 —— 某应届生求职总结
· 字符编码:从基础到乱码解决
· SpringCloud带你走进微服务的世界