C++ 数组:一维数组的定义、遍历与常见操作
在 C++ 编程中,数组是一种基础且常用的数据结构,用于存储一组相同类型的数据,并通过连续的内存空间组织这些数据,方便按索引快速访问。一维数组作为数组的基础形式,广泛应用于数据存储、遍历统计、排序查找等场景,是入门数据结构与算法的必备知识点。本文将从一维数组的定义与初始化、遍历方式、常见操作、内存特性四个维度,带你吃透一维数组的核心用法,夯实编程基础。
一、一维数组的定义与初始化
一维数组的核心是"相同类型+连续内存",定义时需指定数据类型、数组名称与数组长度(可选,初始化时可推导),初始化则是为数组元素赋予初始值,避免使用未初始化的随机值。
1. 基本定义语法
cpp
// 语法格式:数据类型 数组名[数组长度];
// 数组长度必须是常量(C++11 前不支持变量,C++11 后可通过初始化列表推导)
int arr[5]; // 定义一个能存储5个int类型数据的一维数组
float score[10]; // 定义一个能存储10个float类型数据的一维数组
char name[20]; // 定义一个能存储20个char类型数据的一维数组
注意:数组长度一旦确定,无法动态修改(C++ 原生数组不支持动态扩容),若需灵活调整长度,需使用 vector 容器(后续进阶内容)。
2. 初始化方式(4种常用形式)
初始化的核心是为数组元素赋值,避免未初始化导致的内存垃圾值,不同场景可选择不同初始化方式:
-
完全初始化 :指定所有元素的初始值,数组长度可省略(编译器自动推导)。
int arr1[5] = {1, 2, 3, 4, 5}; // 长度5,元素依次为1-5 int arr2[] = {6, 7, 8, 9}; // 省略长度,编译器推导长度为4 -
部分初始化 :只指定部分元素值,未指定的元素自动初始化为0(数值类型)、'\0'(字符类型)。
int arr3[5] = {1, 2}; // 前2个元素为1、2,后3个元素为0 char arr4[5] = {'a', 'b'}; // 前2个为'a'、'b',后3个为'\0' -
全部初始化为0 :仅写一个0,所有元素均初始化为0,适用于数值类型数组。
int arr5[10] = {0}; // 10个元素全部为0 float arr6[8] = {0}; // 8个float类型元素全部为0.0 -
C++11 列表初始化(推荐) :省略等号,直接用大括号赋值,语法更简洁,支持长度推导。
int arr7[] = {10, 20, 30}; // 推导长度为3 char arr8[] = {'x', 'y', 'z'}; // 字符数组列表初始化
3. 错误初始化场景
cpp
// 错误1:数组长度为变量(C++11前不支持,部分编译器报错)
int n = 5;
int arr9[n]; // 非法(C++11后可通过初始化列表规避,但不推荐)
// 错误2:初始化元素个数超过数组长度
int arr10[3] = {1, 2, 3, 4}; // 元素个数4>长度3,编译报错
// 错误3:字符数组初始化字符串未留结束符位置
char arr11[3] = "abc"; // "abc"包含'a','b','c','\0'4个字符,长度不足
二、一维数组的遍历方式
遍历是数组的核心操作,指依次访问数组的每个元素,常用于数据读取、统计、修改等场景。C++ 中一维数组有三种常用遍历方式,各有优劣,适配不同场景。
1. 下标法遍历(最常用)
通过数组下标(从0开始)访问元素,下标范围为 0 ~ 数组长度-1,语法直观,可灵活访问任意位置元素。
cpp
#include <iostream>
using namespace std;
int main() {
int arr[] = {1, 2, 3, 4, 5};
int len = sizeof(arr) / sizeof(arr[0]); // 计算数组长度(关键技巧)
// 下标法遍历:从0到len-1
for (int i = 0; i < len; i++) {
cout << "arr[" << i << "] = " << arr[i] << " ";
}
return 0;
}
关键技巧:sizeof(arr) 计算数组总字节数,sizeof(arr[0]) 计算单个元素字节数,二者相除得到数组长度,适用于所有一维数组。
2. 指针法遍历(底层实现)
数组名本质是数组首元素的地址(常量指针),可通过指针自增访问后续元素,更贴近数组的内存存储逻辑,执行效率高。
cpp
#include <iostream>
using namespace std;
int main() {
int arr[] = {1, 2, 3, 4, 5};
int len = sizeof(arr) / sizeof(arr[0]);
int* p = arr; // 指针p指向数组首元素地址(等价于p = &arr[0])
// 指针法遍历:指针自增,直至遍历完所有元素
for (int i = 0; i < len; i++) {
cout << "*(p+" << i << ") = " << *(p + i) << " ";
// 等价写法:cout << *p++ << " ";(注意优先级,先取值后自增)
}
return 0;
}
注意:数组名是常量指针,不可直接自增(如 arr++ 非法),需用变量指针接收后操作。
3. 范围 for 遍历(C++11 及以上,推荐简洁场景)
专门用于遍历容器或数组的语法,无需手动控制下标或指针,自动遍历所有元素,语法简洁,不易出错。
cpp
#include <iostream>
using namespace std;
int main() {
int arr[] = {1, 2, 3, 4, 5};
// 范围for遍历:auto自动推导元素类型,elem为每个元素的副本
for (auto elem : arr) {
cout << elem << " ";
}
cout << endl;
// 若需修改元素,需用引用(&)
for (auto& elem : arr) {
elem *= 2; // 所有元素翻倍
}
// 再次遍历查看修改结果
for (auto elem : arr) {
cout << elem << " "; // 输出2 4 6 8 10
}
return 0;
}
适用场景:无需指定遍历范围、仅需依次访问所有元素的场景,修改元素时需加引用(&),否则仅修改副本。
三、一维数组的常见操作
基于遍历基础,一维数组的常见操作包括元素修改、统计分析、排序、查找等,以下结合案例讲解核心操作的实现逻辑。
1. 元素修改与赋值
通过下标或指针直接访问元素并赋值,是最基础的操作,需注意下标不越界。
cpp
#include <iostream>
using namespace std;
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int len = sizeof(arr) / sizeof(arr[0]);
// 下标法修改元素
arr[2] = 30; // 将下标2的元素(原3)改为30
// 指针法修改元素
int* p = arr;
*(p + 4) = 50; // 将下标4的元素(原5)改为50
// 遍历查看结果
for (auto elem : arr) {
cout << elem << " "; // 输出1 2 30 4 50
}
return 0;
}
2. 统计分析(求和、求最值)
遍历数组,通过累加、比较实现求和、求最大值、最小值等统计功能,是数组操作的高频场景。
cpp
#include <iostream>
using namespace std;
int main() {
int arr[] = {12, 45, 7, 39, 22, 56};
int len = sizeof(arr) / sizeof(arr[0]);
int sum = 0;
int maxVal = arr[0];
int minVal = arr[0];
// 遍历数组统计求和、最值
for (int i = 0; i < len; i++) {
sum += arr[i]; // 累加求和
if (arr[i] > maxVal) {
maxVal = arr[i]; // 更新最大值
}
if (arr[i] < minVal) {
minVal = arr[i]; // 更新最小值
}
}
cout << "数组总和:" << sum << endl; // 输出181
cout << "最大值:" << maxVal << endl; // 输出56
cout << "最小值:" << minVal << endl; // 输出7
return 0;
}
3. 查找操作(顺序查找)
顺序查找是最基础的查找算法,遍历数组逐一对比目标值,找到则返回下标,未找到则返回-1(表示不存在)。
cpp
#include <iostream>
using namespace std;
// 顺序查找函数:返回目标值的下标,未找到返回-1
int sequentialSearch(int arr[], int len, int target) {
for (int i = 0; i < len; i++) {
if (arr[i] == target) {
return i; // 找到目标,返回下标
}
}
return -1; // 未找到
}
int main() {
int arr[] = {12, 45, 7, 39, 22, 56};
int len = sizeof(arr) / sizeof(arr[0]);
int target = 39;
int index = sequentialSearch(arr, len, target);
if (index != -1) {
cout << "目标值" << target << "在数组下标" << index << "处" << endl;
} else {
cout << "未找到目标值" << target << endl;
}
return 0;
}
4. 排序操作(冒泡排序)
冒泡排序是入门级排序算法,核心逻辑是通过相邻元素两两比较、交换,将最大值(或最小值)逐步"冒泡"到数组末尾,重复操作直至数组有序。
cpp
#include <iostream>
using namespace std;
// 冒泡排序函数:升序排序(从小到大)
void bubbleSort(int arr[], int len) {
// 外层循环控制排序轮次(共len-1轮)
for (int i = 0; i < len - 1; i++) {
// 内层循环控制每轮比较次数(每轮减少1次,已排序元素无需比较)
for (int j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
// 交换相邻元素
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
int main() {
int arr[] = {12, 45, 7, 39, 22, 56};
int len = sizeof(arr) / sizeof(arr[0]);
bubbleSort(arr, len);
// 遍历输出排序后结果
cout << "升序排序后:";
for (auto elem : arr) {
cout << elem << " "; // 输出7 12 22 39 45 56
}
return 0;
}
四、一维数组的内存特性与注意事项
1. 核心内存特性
-
连续内存存储:数组元素在内存中连续排列,每个元素占用相同字节数,下标访问本质是通过首地址偏移计算元素地址(如 arr[i] = *(arr + i))。
-
数组名是首元素地址:数组名本质是常量指针,指向数组首元素的内存地址,不可修改(如 arr++ 非法),但可赋值给变量指针操作。
-
局部数组在栈区分配:main 函数或自定义函数中定义的数组,存储在栈区,函数执行完毕后栈区内存自动释放,不可返回局部数组地址供外部使用。
2. 常见避坑要点
(1)下标越界问题(最常见错误)
数组下标范围是 0 ~ 长度-1,下标超出范围会访问非法内存,导致程序崩溃或数据异常,编译器不报错,需手动校验。
cpp
int arr[5] = {1,2,3,4,5};
arr[5] = 10; // 下标5越界(最大下标4),非法访问内存,程序可能崩溃
规避方案:遍历或访问元素时,确保下标在合法范围,可通过数组长度控制循环边界。
(2)不可返回局部数组地址
cpp
// 错误示例:返回局部数组地址
int* getArr() {
int arr[5] = {1,2,3,4,5};
return arr; // 局部数组在栈区,函数结束后内存释放,地址无效
}
int main() {
int* p = getArr();
cout << *p; // 访问无效内存,结果随机
return 0;
}
规避方案:若需返回数组,可使用全局数组、动态内存分配(new)或 vector 容器。
(3)数组长度不可动态修改
C++ 原生一维数组长度一旦确定,无法动态扩容或缩容,若需灵活调整数据量,推荐使用 C++ 标准库中的 vector 容器(封装了动态数组功能)。
(4)字符数组与字符串的区别
字符数组存储字符串时,需预留字符串结束符 '\0' 的位置,否则无法正确识别字符串长度。
cpp
// 正确:长度4,存储'a','b','c','\0'
char str1[4] = "abc";
// 错误:长度3,无'\0'位置,字符串识别异常
char str2[3] = "abc";
// 推荐:省略长度,编译器自动预留'\0'位置
char str3[] = "abc";
五、一维数组与函数的结合使用
数组作为函数参数时,本质是传递数组首元素的地址(而非整个数组拷贝),函数内修改数组元素会同步影响原数组,且无法通过 sizeof 计算数组长度(需手动传递长度参数)。
cpp
#include <iostream>
using namespace std;
// 数组作为函数参数:本质传递首地址,需手动传长度
void modifyArr(int arr[], int len) {
for (int i = 0; i < len; i++) {
arr[i] *= 2; // 函数内修改,影响原数组
}
}
int main() {
int arr[] = {1,2,3,4,5};
int len = sizeof(arr) / sizeof(arr[0]);
modifyArr(arr, len);
for (auto elem : arr) {
cout << elem << " "; // 输出2 4 6 8 10,原数组被修改
}
return 0;
}
六、总结
一维数组是 C++ 基础数据结构的核心,核心要点可概括为"定长连续、下标访问、地址传递"。掌握其定义与初始化方式,能正确创建数组并赋予初始值;熟练运用三种遍历方式,可适配不同场景的元素访问需求;理解常见操作与内存特性,能规避下标越界、无效地址等坑点,写出健壮代码。
一维数组是后续学习二维数组、指针、vector 容器、排序算法的基础,建议多动手练习遍历统计、排序查找等操作,加深对内存逻辑与下标计算的理解。在实际开发中,若需动态调整数据量,可优先使用 vector 容器,但原生数组的底层逻辑与操作方式,仍是理解更高阶数据结构的关键。