简要回答
什么是数组?
数组本质上是一组相同类型元的的集合,在内存中是连续排列的。
为什么需要数组?
当你需要表示大量数据集合的情况下,手动的创建和管理单独的命名的变量是不可取并且难以维护的。
定义和访问数组?
int a[]{1,2,3,4,5};
int b[3]{'a','b','c'}
数组零索引,起始索引 0
int a[]{1,2,3,4,5};
a[2] = 9;
知识拓展
数组和指针的关系?
数组本质上只是一个指针,这非常重要。
int example[4] = {2,2,6};
数组的名称,例如 example,实际上是一个内存地址,是一个指向该数组存储位置的内存块的起始位置的指针。尽管是个指针,但是 C++允许我们使用下面这样的语法访问元素。
std::cout << example[2] << std:endl;
std::cout << *(example+2) << std::endl;
// 上述两种方式等价
当你通过索引访问一个元素(例如 example[i])时,实际上编译器在幕后做的事情是计算一个偏移量。它从数组的起始内存地址开始,然后加上 索引 元素大小 的偏移量来找到目标元素的内存地址。例如,访问 example(第三个元素)时,它会从起始地址加上 2 size_of_int(通常是 8 字节)来找到第三个整数的位置。
这意味着数组索引是指针的语法糖。
边界检查和潜在问题?
C++的原始数组不提供自动的边界检查。当你在调试模式下,尝试访问一个超出数组有效索引的元素,就会发生内存访问违规,这会导致程序崩溃然。而在 release 模式下,程序不会崩溃,但你会操作到不属于你的内存区域,就可能无意中修改程序中的其他变量或者其他程序的变量,这非常危险。
数组分配在内存中的哪里?
栈 (Stack) 和 堆 (Heap)。当你在函数或者代码块中声明一个固定大小的数组时,这个数组是在栈上的,它的生命周期仅存在于当前作用域,当程序执行离开这个代码块,或者这个函数结束时,这个数组就会被自动销毁,这也是为什么在函数不能直接返回一个数组的原因。第二步就是堆分配啊,你可以用 new 关键字来动态的分配数组。这样的话一个数组就会分配在堆上,当然这需要你手动的去管理这个数组的内存的释放。如果你没有释放的话会导致内存泄漏。栈和堆中的数组还有很多的不同,我们不在这里进行说明。
数组的大小如何确定?
对于栈上的数组,可以使用 sizeof(array_name) / sizeof(element_type) 。至于堆上,目前没有标准可靠的办法。如果你对堆分配的数组指针使用 sizeof(array_name),你只会得到指针本身的大小。建议你使用 C++11 新引入的 std::array ,自带了.size()方法。