当前位置: 首页 > news >正文

「C++系列」数组

文章目录

  • 一、数组
    • 1. 声明数组
    • 2. 初始化数组
    • 3. 访问数组元素
    • 4. 遍历数组
    • 注意事项
    • 示例代码
  • 二、多维数组
    • 1. 声明二维数组
    • 2. 初始化二维数组
    • 3. 访问二维数组元素
    • 4. 遍历二维数组
    • 注意事项
    • 示例代码
  • 三、指向数组的指针
    • 1. 声明指向数组的指针
    • 2. 通过指针访问数组元素
    • 3. 指针和数组的关系
    • 4. 使用指向数组的指针进行排序
  • 四、传递数组给函数
    • 方法1:传递数组名和大小
    • 方法2:使用引用传递数组(不推荐用于原始数组)
    • 方法3:使用模板和引用传递(对于固定大小的数组)
    • 注意事项
  • 五、从函数返回数组
    • 方法1:返回指向静态数组的指针
    • 方法2:使用 `std::array` 或 `std::vector`
      • 使用 `std::array`
      • 使用 `std::vector`
    • 方法3:返回指向动态分配数组的指针(需要手动管理内存)
  • 六、相关链接

一、数组

C++ 中的数组是一种基础的数据结构,用于存储相同类型的数据的集合。数组中的每个元素可以通过索引(或下标)来访问,索引通常是从 0 开始的。数组的大小在声明时必须指定,并且之后不能改变(尽管可以通过一些技巧如动态内存分配来模拟可变大小的数组,但这通常涉及到指针和动态内存管理)。
在这里插入图片描述

1. 声明数组

数组的声明需要指定数组的类型和大小。例如,声明一个整型数组 arr,包含 5 个元素:

int arr[5];

2. 初始化数组

在声明数组的同时,可以初始化数组中的元素。例如:

int arr[5] = {1, 2, 3, 4, 5};

如果初始化时提供的元素少于数组的大小,则未明确初始化的元素将被自动初始化为零(对于内置类型如 intfloat 等)。

3. 访问数组元素

使用索引来访问数组中的元素。索引是从 0 开始的。例如,访问上面声明的数组 arr 的第一个元素:

int firstElement = arr[0]; // firstElement 的值为 1

4. 遍历数组

遍历数组通常使用循环结构,如 for 循环。例如,打印数组 arr 的所有元素:

for (int i = 0; i < 5; i++) {std::cout << arr[i] << " ";
}
std::cout << std::endl;

注意事项

  • 数组的大小在编译时确定,之后不能改变。
  • 数组越界是未定义行为,可能导致程序崩溃或数据损坏。因此,访问数组元素时要确保索引在有效范围内。
  • C++ 标准库提供了 std::vector 容器,它是一个更灵活、更安全的数组替代品,能够动态地增长和缩小。

示例代码

下面是一个完整的示例,展示了如何声明、初始化、访问和遍历数组:

#include <iostream>int main() {int arr[5] = {1, 2, 3, 4, 5};// 访问数组的第一个元素std::cout << "第一个元素是: " << arr[0] << std::endl;// 遍历数组并打印所有元素for (int i = 0; i < 5; i++) {std::cout << arr[i] << " ";}std::cout << std::endl;return 0;
}

输出将是:

第一个元素是: 1
1 2 3 4 5 

二、多维数组

C++ 中的多维数组是数组的数组,用于存储具有两个或更多维度的数据。最常见的是二维数组,但它可以扩展到更高维度。多维数组中的每个元素都可以通过一组索引来访问,这些索引对应于数组的每个维度。

1. 声明二维数组

二维数组可以看作是数组的数组,其中每个内部数组(或行)具有相同数量的元素。以下是声明二维数组的语法:

type arrayName[rows][columns];

其中 type 是数组元素的类型,arrayName 是数组的名称,rows 是数组的行数,columns 是数组的列数。

2. 初始化二维数组

在声明二维数组时,可以立即初始化它。如果初始化时省略了某些元素,则这些元素将被自动初始化为零(对于内置类型)。

int matrix[3][4] = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12}
};

如果初始化时只提供了部分行,则剩余的行将被自动初始化为零。

3. 访问二维数组元素

二维数组中的元素通过两个索引来访问:第一个索引指定行,第二个索引指定列。

int secondRowThirdColumn = matrix[1][2]; // 值为 7

4. 遍历二维数组

遍历二维数组通常涉及嵌套循环,外层循环遍历行,内层循环遍历列。

for (int i = 0; i < 3; i++) {for (int j = 0; j < 4; j++) {std::cout << matrix[i][j] << " ";}std::cout << std::endl;
}

注意事项

  • 多维数组的大小在编译时确定,之后不能改变。
  • 访问数组元素时要确保索引在有效范围内,以避免数组越界。
  • C++ 标准库中的 std::vector<std::vector<T>> 可以作为二维数组的替代品,提供更大的灵活性和动态内存管理。

示例代码

以下是一个完整的示例,展示了如何声明、初始化、访问和遍历二维数组:

#include <iostream>int main() {int matrix[3][4] = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12}};// 访问并打印第二行第三列的元素std::cout << "第二行第三列的元素是: " << matrix[1][2] << std::endl;// 遍历并打印整个二维数组for (int i = 0; i < 3; i++) {for (int j = 0; j < 4; j++) {std::cout << matrix[i][j] << " ";}std::cout << std::endl;}return 0;
}

输出将是:

第二行第三列的元素是: 7
1 2 3 4 
5 6 7 8 
9 10 11 12 

三、指向数组的指针

在C++中,指向数组的指针是一个特殊的指针,它指向数组的第一个元素的地址。通过这个指针,我们可以访问和修改数组中的元素。然而,需要注意的是,指针本身并不知道它所指向的数组的大小,这意味着在通过指针遍历数组时,我们需要知道何时停止,以避免越界访问。

1. 声明指向数组的指针

当我们声明一个指向数组的指针时,我们通常会让它指向数组的第一个元素。指针的类型应该与数组元素的类型相匹配。

int arr[5] = {1, 2, 3, 4, 5};
int* ptr = arr; // ptr 指向 arr 的第一个元素

在这里,ptr 是一个指向 int 的指针,它被初始化为指向 arr 数组的第一个元素。由于数组名(在这里是 arr)在大多数表达式中会被解释为指向其第一个元素的指针,所以上面的 ptr = arr; 是合法的。

2. 通过指针访问数组元素

一旦我们有了指向数组第一个元素的指针,我们就可以使用指针算术来访问数组中的其他元素。

#include <iostream>int main() {int arr[5] = {1, 2, 3, 4, 5};int* ptr = arr;// 使用指针访问数组元素for (int i = 0; i < 5; i++) {std::cout << *(ptr + i) << " "; // 等同于 arr[i]}std::cout << std::endl;return 0;
}

3. 指针和数组的关系

在C++中,数组名在很多情况下会被隐式地转换为指向其第一个元素的指针。但是,有几个重要的区别:

  1. 大小信息丢失:当数组名被用作指针时,它不再携带关于数组大小的信息。因此,通过指针遍历数组时,我们需要知道数组的大小。
  2. 类型不同:尽管在大多数上下文中数组名可以像指针一样使用,但它们的类型是不同的。数组名是一个常量指针,指向数组的第一个元素,并且它的类型是“N个T类型的数组”,其中N是数组的大小,T是元素的类型。而指针是一个指向T类型对象的变量。
  3. 使用 sizeof 操作符:对数组使用 sizeof 会返回整个数组的大小(以字节为单位),而对指针使用 sizeof 会返回指针本身的大小(这取决于平台和编译器)。

4. 使用指向数组的指针进行排序

下面是一个简单的示例,展示了如何使用指向数组的指针来对数组进行排序(这里使用冒泡排序作为示例)。

#include <iostream>void bubbleSort(int* arr, int n) {for (int i = 0; i < n-1; i++) {for (int j = 0; j < n-i-1; j++) {if (*(arr + j) > *(arr + j + 1)) {// 交换 arr[j] 和 arr[j+1]int temp = *(arr + j);*(arr + j) = *(arr + j + 1);*(arr + j + 1) = temp;}}}
}int main() {int arr[5] = {5, 3, 8, 4, 2};int n = sizeof(arr)/sizeof(arr[0]);bubbleSort(arr, n);for (int i = 0; i < n; i++) {std::cout << arr[i] << " ";}std::cout << std::endl;return 0;
}

四、传递数组给函数

在C++中,传递数组给函数通常是通过传递数组的指针来完成的。由于数组名在大多数情况下会被解释为指向其第一个元素的指针,因此你可以直接将数组名作为参数传递给函数。然而,这种方式并不会传递数组的大小,所以通常还需要将数组的大小作为另一个参数传递给函数。

方法1:传递数组名和大小

这是最常见的方法,你将数组名(实际上是指向数组第一个元素的指针)和数组的大小作为两个独立的参数传递给函数。

#include <iostream>// 函数声明,接收指向int的指针和数组的大小
void printArray(int* arr, int size) {for (int i = 0; i < size; i++) {std::cout << arr[i] << " ";}std::cout << std::endl;
}int main() {int myArray[5] = {1, 2, 3, 4, 5};// 传递数组名和大小给函数printArray(myArray, 5);return 0;
}

方法2:使用引用传递数组(不推荐用于原始数组)

需要注意的是,在C++中你不能直接传递数组的引用(如 void func(int (&arr)[5]); 这样的声明在函数模板之外是不允许的,因为它要求数组的大小在编译时已知)。然而,你可以通过传递指向数组的指针来模拟这种行为,或者如果你在处理的是固定大小的数组,并且希望避免传递大小参数,你可以考虑使用模板(但这不是传递数组的“引用”,而是类型推导)。

方法3:使用模板和引用传递(对于固定大小的数组)

虽然这不是直接传递数组给函数的标准方式,但如果你在处理固定大小的数组,并且想要避免传递大小参数,你可以使用模板。

#include <iostream>// 模板函数,用于处理固定大小的数组
template<size_t N>
void printFixedArray(int (&arr)[N]) {for (size_t i = 0; i < N; i++) {std::cout << arr[i] << " ";}std::cout << std::endl;
}int main() {int myArray[5] = {1, 2, 3, 4, 5};// 使用模板函数,不需要传递大小printFixedArray(myArray);return 0;
}

注意事项

  • 当通过指针传递数组时,请确保在函数内部不会越界访问数组。
  • 传递数组时,并不会复制数组本身,而是传递了指向数组第一个元素的指针。因此,函数内部对数组元素的修改将影响原始数组。
  • 如果你正在处理动态分配的数组(例如,使用 new 关键字分配的数组),请确保在不再需要时释放内存(使用 delete[])。然而,在传递动态分配的数组给函数时,你通常只需要传递指向数组的指针,而不需要传递数组的大小(如果函数内部有逻辑来确定大小的话),但最佳实践是始终传递大小以避免潜在的错误。

五、从函数返回数组

在C++中,直接从函数返回一个数组本身是不被直接支持的,因为数组名在表达式中通常会被视为指向其第一个元素的指针,而不是一个可以返回的对象。但是,有几种方法可以模拟从函数返回数组的行为。

方法1:返回指向静态数组的指针

这种方法涉及在函数内部定义一个静态数组,并返回指向该数组的指针。然而,这种方法有局限性,因为它返回的总是同一个数组的引用,可能在多线程环境下导致问题,并且数组的大小在编译时就已确定。

#include <iostream>int* returnArray() {static int arr[] = {1, 2, 3, 4, 5};return arr;
}int main() {int* myArray = returnArray();for (int i = 0; i < 5; i++) {std::cout << myArray[i] << " ";}std::cout << std::endl;return 0;
}

方法2:使用 std::arraystd::vector

std::array 是一个固定大小的数组容器,而 std::vector 是一个可变大小的数组容器。它们都提供了复制和移动语义,因此可以直接从函数返回。

使用 std::array

#include <iostream>
#include <array>std::array<int, 5> returnArray() {return {1, 2, 3, 4, 5};
}int main() {std::array<int, 5> myArray = returnArray();for (int i : myArray) {std::cout << i << " ";}std::cout << std::endl;return 0;
}

使用 std::vector

#include <iostream>
#include <vector>std::vector<int> returnArray() {return {1, 2, 3, 4, 5};
}int main() {std::vector<int> myArray = returnArray();for (int i : myArray) {std::cout << i << " ";}std::cout << std::endl;return 0;
}

方法3:返回指向动态分配数组的指针(需要手动管理内存)

这种方法涉及在堆上动态分配数组,并返回指向该数组的指针。调用者需要负责在不再需要时释放内存。

#include <iostream>int* returnArray(int size) {int* arr = new int[size];for (int i = 0; i < size; i++) {arr[i] = i + 1;}return arr;
}int main() {int* myArray = returnArray(5);for (int i = 0; i < 5; i++) {std::cout << myArray[i] << " ";}std::cout << std::endl;delete[] myArray; // 不要忘记释放内存return 0;
}

在这里插入图片描述

六、相关链接

  1. Visual Studio Code下载地址
  2. Sublime Text下载地址
  3. 「C++系列」C++简介、应用领域
  4. 「C++系列」C++ 基本语法
  5. 「C++系列」C++ 数据类型
  6. 「C++系列」C++ 变量类型
  7. 「C++系列」C++ 变量作用域
  8. 「C++系列」C++ 常量知识点-细致讲解
  9. 「C++系列」C++ 修饰符类型
  10. 「C++系列」一篇文章说透【存储类】
  11. 「C++系列」一篇文章讲透【运算符】
  12. 「C++系列」循环
  13. 「C++系列」判断
  14. 「C++系列」函数/内置函数
  15. 「C++系列」数字/随机数

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 使用Chainlit接入通义千问快速实现一个多模态的对话应用
  • Android12 显示框架之SurfaceComposerClient创建
  • element的el-autocomplete带输入建议搜索+搜索匹配文字高亮显示
  • 前端开发:Vue2.0桌面组件库-Element
  • 【音视频之SDL2】Windows配置SDL2项目模板
  • 【数据集处理】Polars库、Parquet 文件
  • GO-学习-02-常量
  • 【EI会议征稿通知】第五届大数据、人工智能与软件工程国际研讨会(ICBASE 2024)
  • js_拳皇(下)
  • 「树形结构」基于 Antd 实现一个动态增加子节点+可拖拽的树
  • ArduPilot开源代码之lida2003套机+伴机电脑外场
  • 前端 socket.io 跨域
  • 【Go - context 速览,场景与用法】
  • 解析西门子PLC的String和WString
  • 套接字选项、单播、广播和多播
  • 【译】JS基础算法脚本:字符串结尾
  • “寒冬”下的金三银四跳槽季来了,帮你客观分析一下局面
  • 0x05 Python数据分析,Anaconda八斩刀
  • 5、React组件事件详解
  • angular2 简述
  • avalon2.2的VM生成过程
  • ComponentOne 2017 V2版本正式发布
  • ES6 ...操作符
  • gitlab-ci配置详解(一)
  • Git的一些常用操作
  • JS学习笔记——闭包
  • js正则,这点儿就够用了
  • LeetCode29.两数相除 JavaScript
  • LeetCode541. Reverse String II -- 按步长反转字符串
  • mysql常用命令汇总
  • orm2 中文文档 3.1 模型属性
  • Swift 中的尾递归和蹦床
  • uva 10370 Above Average
  • 基于遗传算法的优化问题求解
  • 将回调地狱按在地上摩擦的Promise
  • 跨域
  • 数组大概知多少
  • AI又要和人类“对打”,Deepmind宣布《星战Ⅱ》即将开始 ...
  • ​linux启动进程的方式
  • #{}和${}的区别?
  • #我与Java虚拟机的故事#连载15:完整阅读的第一本技术书籍
  • (27)4.8 习题课
  • (安全基本功)磁盘MBR,分区表,活动分区,引导扇区。。。详解与区别
  • (二刷)代码随想录第16天|104.二叉树的最大深度 559.n叉树的最大深度● 111.二叉树的最小深度● 222.完全二叉树的节点个数
  • (附源码)springboot“微印象”在线打印预约系统 毕业设计 061642
  • (附源码)springboot炼糖厂地磅全自动控制系统 毕业设计 341357
  • (附源码)ssm教材管理系统 毕业设计 011229
  • (九)c52学习之旅-定时器
  • (免费领源码)python+django+mysql线上兼职平台系统83320-计算机毕业设计项目选题推荐
  • (三分钟)速览传统边缘检测算子
  • (完整代码)R语言中利用SVM-RFE机器学习算法筛选关键因子
  • (详细文档!)javaswing图书管理系统+mysql数据库
  • (一)C语言之入门:使用Visual Studio Community 2022运行hello world
  • (已解决)Bootstrap精美弹出框模态框modal,实现js向modal传递数据
  • (已解决)报错:Could not load the Qt platform plugin “xcb“