1. 定义和初始化内置数组
数组的声明格式一般为:
type arrayName[dimension];
其中,arrayName
是数组名,dimension
表示数组中元素的个数,必须为一个常量表达式。数组的元素类型可以是任意对象类型(但不包括引用)。
1.1 常规定义
例如:
int arr[10]; // 定义一个含有 10 个 int 类型元素的数组
unsigned sz = 42;
constexpr unsigned csz = 42;
int *parr[csz]; // 定义一个含有 42 个整型指针的数组
注意:
- 数组的维度必须是常量表达式。如果使用普通变量(如
sz
),则会导致编译错误。- 默认情况下,数组的元素会被默认初始化。对于内置类型,如 int,默认初始化通常不会自动赋值(局部数组未初始化);但如果是全局数组,则所有元素初始化为 0。
- 不能使用
auto
关键字让编译器推断数组类型,必须明确指定数组类型和维度。
2. 显式初始化数组元素
可以在数组声明时使用列表初始化来显式地指定每个元素的初始值。如果在声明时省略数组的维度,编译器将根据初始值的个数自动推导出数组大小;反之,如果指定了维度,则初始值的个数不能超过该维度,多余部分会忽略,缺少部分将使用默认值进行填充。
2.1 使用列表初始化
下面的示例展示了不同方式初始化数组的效果:
示例代码 | 说明 |
---|---|
int a1[3] = {0, 1, 2}; |
数组 a1 有 3 个元素,分别初始化为 0、1、2。 |
int a2[] = {0, 1, 2}; |
数组 a2 的维度自动推导为 3。 |
int a3[5] = {0, 1, 2}; |
数组 a3 有 5 个元素,前三个元素分别为 0、1、2,后两个元素值默认初始化为 0。 |
string a4[3] = {"hi", "bye"}; |
数组 a4 有 3 个元素,前两个初始化为 “hi” 和 “bye”,第三个元素初始化为空字符串。 |
例如:
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;
int main() {
int a1[3] = {
0, 1, 2};
int a2[] = {
0, 1, 2};
int a3[5] = {
0, 1, 2}; // 剩余两个元素初始化为 0
string a4[3] = {
"hi", "bye"};
cout << "a1: ";
for (auto x : a1)
cout << x << " ";
cout << endl;
cout << "a3: ";
for (int i = 0; i < 5; ++i)
cout << a3[i] << " ";
cout << endl;
cout << "a4: ";
for (auto &s : a4)
cout << "\"" << s << "\" ";
cout << endl;
return 0;
}
3. 字符数组的特殊初始化
字符数组具有额外的初始化方式,可以使用字符串字面值来初始化。例如:
char al[] = {
'C', '+', '+'}; // 列表初始化,没有空字符,维度为 3
char a2[] = {
'C', '+', '+', '\0'}; // 显式包含空字符,维度为 4
char a3[] = "C++"; // 自动在末尾添加空字符,维度为 4
注意:
当用字符串字面值初始化字符数组时,必须预留空间存放结尾的空字符。例如:const char a4[6] = "Daniel"; // 错误:字符串 "Daniel" 实际上有 7 个字符(包括结尾的空字符),数组大小必须至少为 7。
4. 数组的拷贝和赋值
内置数组与其他对象不同,不能直接将一个数组赋值给另一个数组,也不能用数组作为初始值拷贝另一个数组:
int a[] = {
0, 1, 2};
// int b[] = a; // 错误:不能使用数组初始化另一个数组
// a = b; // 错误:不能直接赋值给数组
如果需要将一个数组的内容复制到另一个数组中,可以使用循环或标准库算法(如 std::copy
)。
5. 理解复杂的数组声明
数组声明中涉及多种修饰符时,必须特别注意绑定顺序。以下是一些例子:
5.1 存放指针的数组
int *ptrs[10]; // 定义一个数组 ptrs,其中包含 10 个指向 int 的指针
这是最简单的情况:从右向左阅读,ptrs 是一个数组,数组的每个元素都是指向 int 的指针。
5.2 数组的指针和数组的引用
数组的指针和数组的引用声明相对复杂。举例:
-
数组指针:
int arr[10]; int (*Parray)[10] = &arr;
解释:
Parray
是一个指针,它指向一个包含 10 个 int 元素的数组。 -
数组引用:
int arr[10]; int (&arrRef)[10] = arr;
解释:
arrRef
是一个引用,引用了一个包含 10 个 int 元素的数组。 -
引用数组(错误示例):
数组不能直接存放引用,因此下面的写法是错误的:// int &refs[10] = /*?*/; // 错误:不存在引用的数组
-
引用数组中的指针:
可以定义一个引用,该引用引用的是一个数组,其中元素为指针:int *ptrs[10]; int*(&arry)[10] = ptrs; // arry 是一个引用,引用了一个含有 10 个 int* 的数组
从内向外阅读:首先,
arry
是一个引用,其引用的对象是一个数组;数组中每个元素是int*
。
6. 总结
-
定义和初始化:
- 数组声明时必须指定元素个数,且该个数必须为常量表达式。
- 可以使用列表初始化对数组元素进行显式初始化,若初始值数量少于数组维度,其余元素采用默认初始化。
-
字符数组特殊性:
- 使用字符串字面值初始化字符数组时,编译器会自动添加一个空字符作为结束标志,因此数组大小必须足够大以容纳该空字符。
-
数组赋值与拷贝:
- 内置数组不支持直接赋值或拷贝,只能通过逐元素复制实现。
-
复杂声明:
- 数组的指针和数组的引用声明相对复杂,理解时建议从内向外或从右向左逐步解析修饰符的绑定顺序。
通过掌握数组的这些基本知识和细节,你可以在 C++ 编程中正确使用内置数组,并为后续学习更高级的容器(如 vector)奠定基础。
参考资料
- cppreference.com 关于数组、初始化和数组指针的详细说明
- 各大 C++ 编码规范中对数组声明与初始化的建议
希望这篇文章能帮助你全面理解 C++ 内置数组的定义、初始化方法以及复杂声明的技巧,从而更好地运用数组处理数据。