关于C++数组初始化的一些坑之原理剖析
前情提要
在开始之前先介绍几种数组初始化的语法:
将数组初始化为0,有以下几种常用方案
1. int arr[10]{0};
2. int* arr = new int[10]{0};//分配到堆内存
3. int arr[10] = {0};//或者 int arr[10];//默认初始化为0
4. vector<int> vec(10,0);//或者vector<int> vec(10);//默认也是初始化为0
值得注意的是,C++11之后才支持1和2两种初始化语法。
正文
我们先看一行代码:
int arr[10] = {-1};
请问如果遍历该数组,得到的结果会是什么呢?
答案是:-1 0 0 0 0 0 0 0 0 0
如果你的答案是正确的,而且你明白是怎么一回事就不需要往下看了。
这是我在刷 PAT 1039 到底买不买 这道题时遇到的问题,花了N多天的时间思考、查资料,终于弄清楚原理了,在这里跟各位道友分享一下。
一开始,我也天真地以为数组元素是10个-1,直到PAT报错……
其实,全部初始化为0是完全没问题的,问题就出在想把数组全部初始化成一个非0的数,即非默认值,是行不通的。这倒不是因为编译器对初始化为0给了个特例,而是因为一条基本语法规则:
数组初始化列表中的元素个数小于指定的数组长度时,不足的元素补以默认值。
而对于基本类型int来说,默认值就是0。
再看一下非基本类型的数组:
string a[5] = { "foo" };
有了上面的规则,就很容易知道其实相当于:
string a[5] = { "foo", "", "", "", "" };
也就是说,后面4个元素调用了string的默认构造函数进行的初始化,而第一个则调用的string::string(const char)进行的初始化。
memset的误区
如果想在数组创建结束后再对其进行初始化,可以使用C函数memset(),但是memset的使用有个大问题,就是它只对char类型的数组管用:
char a[10];
memset(a, 1, 10); // 将每个元素设置为1
如果将上面的a数组换成int或其他类型的,就会出现问题,因为memset的内部实现是以字节为单位进行赋值的,int 类型大于一个字节(假设是4个),数组内存连续,如果有下面代码:
int a[10];
memset(a, 1, sizeof(a));
将只会对前sizeof(a)即40个字节进行赋值1的操作,即给“前10个int”进行了赋值0x01010101的操作,所以,如果实在想再初始化,只能循环赋值。