写 C 的时候定义一个 int a[100],测试数据一多就越界,改成 int a[100000] 又白白多占几百 KB 内存。个数事先定不下来的场景,就该换动态数组了。
普通数组的大小在编译时就写死,内存一般从栈上分,声明多少就是多少。动态数组不一样,长度可以在运行时改,内存放在堆上,装不下就申请一块更大的,把旧数据搬过去接着用。
各语言都有现成的
- C++:vector,push_back 往尾部追加,size 看当前个数
- Java:ArrayList,add 追加,get 按下标取值
- Python:list 本身就是动态的,append 一直往里塞就行
- Go:切片配合 append,容量不够时自动换一块更大的底层数组
C 语言没有现成的,得自己 malloc 一块内存,不够用了再 realloc 扩:
int *p = (int *)malloc(10 * sizeof(int)); p = (int *)realloc(p, 20 * sizeof(int)); free(p);
扩容不是免费的
容量不够时,多数实现按 1.5 倍或 2 倍申请新内存,把旧数据整个拷过去,再释放旧空间。数据量大的时候这一下很伤性能,所以 C++ 可以先调 reserve 把容量一次留够,避免反复搬家。
它的强项是随机访问,按下标取值和普通数组一样快;弱项是中间插入和删除,后面的元素全得往后挪。频繁在中间插删的场景,链表或者别的结构更合适。
顺带一提 size 和 capacity 是两码事:size 是已经装了的个数,capacity 是已经申请下来的容量,vector 的 capacity 一般大于等于 size,多出来的就是留给以后追加用的。
几个常见的坑
- malloc 的返回值没检查,内存不够时 p 是空指针,直接用程序就崩
- realloc 失败会返回 NULL,原来的内存还在,别拿返回值直接覆盖旧指针,不然老内存丢了还找不到人释放
- vector 扩容搬家之后,之前存下来的迭代器会失效,接着用就是未定义行为
怎么选很简单:个数确定就不折腾,普通数组最省心;个数会变、要增删,直接用动态数组,别手写一堆 realloc 硬扛。