初始化(1)
入门。
C++中数组分静态和动态两种。静态数组的大小固定,编译时(compile-time)就分配好了,存在 stack 中:
int a[5] = {1, 2, 3, 4, 5};
for (int i = 0; i < 5; i++) {
cout << a[i] << " ";
}
// 1 2 3 4 5
动态数组则是运行时(run-time)才分配,存在heap中,大小可变,但切记要用后删除否则可能会内存泄漏:
int *a = new int[5];
for (int i = 0; i < 5; i++) {
cout << a[i] << " ";
}
delete[] a;
// 0 0 0 0 0
C++11 之后可以方便的初始化一个动态数组:
int *a = new int[5]{1, 2, 3, 4, 5};
for (int i = 0; i < 5; i++) {
cout << a[i] << " ";
}
delete[] a;
// 编译时可能需要加上 --std c++11
// $ clang++ --std c++11 tmp.cpp; ./a.out
// 1 2 3 4 5
Java 的语法跟 C++很像,但完全不需要担心内存的管理:
jshell> int[] a = new int[5]
a ==> int[5] { 0, 0, 0, 0, 0 }
放弃?
1 号坑
貌似 Java 自动将数组元素全部初始化为 0,那如果不初始化 C++的数组会怎样?
int a[5];
for (int i = 0; i < 5; i++) {
cout << a[i] << " ";
}
结果却是...
// 4196528 0 4196160 0 -258455680
2 号坑
数组越界的时候,Java 非常省心的抛了一个错:
jshell> int a[] = new int[5]
a ==> int[5] { 0, 0, 0, 0, 0 }
jshell> a[10]
| Exception java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 5
...
C++却有返回值:
int a[5] = {0};
cout << a[10];
// -1195833
3 号坑
听说这样就可以将静态数组全部初始化为 0:
int a[5] = {0};
那是不是这样就可以全部初始化为 1?
int a[5] = {1}
结果是...
1 0 0 0 0
4 号坑
类比 Java 的数组,生成一个大小为 5 的ArrayList
是不是应该这样:
jshell> List<Integer> a = new ArrayList<>(5);
但读取的时候缺产生了这样的错误:
jshell> a.get(0)
| Exception java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0
| at Preconditions.outOfBounds (Preconditions.java:64)
...
进阶!
C/C++是比 Java 更底层一点的语言,它可以直接操纵内存,而 Java 是跑在 JVM 虚拟机里。C/C++如果不对数组进行初始化,声明了数组只是知道了数组起始的内存地址。由于这段内存可能之前被用在别的程序,所以直接打印出来不全是 0 也不足为奇;而由于地址是连续的,a[10]
只是说从起始地址位移十个单位,所以它是可以读取到内存里的值的。是不是感觉 C++在内存管理这方面略微可怕?
int a[5] = {1}
的意思是把第一个初始化为 1,其他没注明的就为 0。
new ArrayList<>(5);
里的5
设的是初始容量(capacity),这是区别于数组大小(size)的: capacity 是当前数组可能的最大长度,而 size 是实际上ArrayList
里有多少个元素,当 size 要超过 capacity 的时候,ArrayList
会悄悄扩大它下面的数组。
分步来看,首先a
的 size 为0
,加入一个数后变为1
:
jshell> List<Integer> a = new ArrayList<>(5);
a ==> []
jshell> a.size()
$1 ==> 0
jshell> a.add(1)
$2 ==> true
jshell> a.size()
$3 ==> 1
Capacity 是不直接可见的,但通过 reflection 可以看到,当前为 5,是初始化时设置的:
jshell> import java.lang.reflect.Field;
jshell> Field field = ArrayList.class.getDeclaredField("elementData");
field ==> transient java.lang.Object[] java.util.ArrayList.elementData
jshell> field.setAccessible(true);
jshell> field.get(a)
$7 ==> Object[5] { 1, null, null, null, null }
jshell> int capacity = ((Object[])field.get(a)).length
capacity ==> 5
继续添加元素,发现 capacity 自动从 5 升到了 7,而数组的 size 为 6:
jshell> a.addAll(Arrays.asList(2, 3, 4, 5, 6))
jshell> field.get(a)
$10 ==> Object[7] { 1, 2, 3, 4, 5, 6, null }
jshell> int capacity = ((Object[])field.get(a)).length
capacity ==> 7
jshell> int size = a.size()
size ==> 6