C++ 数组:一维数组的定义、遍历与常见操作

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 容器,但原生数组的底层逻辑与操作方式,仍是理解更高阶数据结构的关键。

相关推荐
码道功成1 小时前
Pycham及IntelliJ Idea常用插件
java·ide·intellij-idea
月挽清风2 小时前
代码随想录第七天:
数据结构·c++·算法
TTGGGFF2 小时前
控制系统建模仿真(一):掌握控制系统设计的 MAD 流程与 MATLAB 基础运算
开发语言·matlab
星幻元宇VR2 小时前
走进公共安全教育展厅|了解安全防范知识
学习·安全·虚拟现实
消失的旧时光-19432 小时前
第四篇(实战): 订单表索引设计实战:从慢 SQL 到毫秒级
java·数据库·sql
2501_944424122 小时前
Flutter for OpenHarmony游戏集合App实战之贪吃蛇食物生成
android·开发语言·flutter·游戏·harmonyos
知识分享小能手2 小时前
Oracle 19c入门学习教程,从入门到精通, Oracle 表空间与数据文件管理详解(9)
数据库·学习·oracle
それども2 小时前
@ModelAttribute vs @RequestBody
java
充值修改昵称3 小时前
数据结构基础:从二叉树到多叉树数据结构进阶
数据结构·python·算法