问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

C++二维数组:概念、创建、初始化与操作详解

创作时间:
作者:
@小白创作中心

C++二维数组:概念、创建、初始化与操作详解

引用
CSDN
1.
https://blog.csdn.net/2201_75539691/article/details/144937755

本文详细介绍了C++中二维数组的概念、创建、初始化与操作方法,通过多个示例代码帮助读者理解二维数组的使用场景和具体实现。文章还介绍了使用memset函数初始化二维数组的方法,进一步丰富了内容。

在C++学习中,数组是最基础且常用的数据结构之一,而二维数组更是实现日常计算中一维数组无法解决的复杂问题的重要工具。本文将深入解释C++中二维数组的概念、创建、初始化与操作,并尤其深入学习怎样使用它在实际中解决问题。

二维数组的概念

在学习了一维数组之后,我们对数组有了基本认识:一维数组是一个连续的内存区域,其元素是同一类型的数据。

二维数组,则可以理解为“数组的数组”:它不仅仅是连续内存中的元素,而是连续内存中包含了多个一维数组。这些一维数组又是于同一类型。

如下图,展示了一维数组和二维数组的连续性:

图示:

  • 一维数组:int arr[5]:连续内存。
  • 二维数组:int arr[3][5]:用行和列分布数据。

而如果二维数组重叠(二维数组内元素为数组),就是三维数组。依此类推,可实现多维数组,这为高维数据处理提供了基础。

二维数组的创建和初始化

1.创建二维数组

在C++中,二维数组的创建较一维数组最基本。

语法:

数据类型 数组名[ 行数 ][ 列数 ];

示例:

int arr[3][5];        // 定义一个 3 行 5 列的整数数组
double data[2][8];    // 定义一个 2 行 8 列的浮点数组

认识解释:

  1. 行数与列数:
  • 3表示数组有 3 行;
  • 5表示每行有 5 个元素。
  1. 数据类型:
  • 如:int表示二维数组的每个元素是整数类型。
  1. 数组名:
  • 如:arr,用于标识数组,是你在代码中呼叫它的方式。
  1. 值字应与合:
  • 举例:行数和列数必须是常量,无法用变量指定。

2.初始化二维数组

在创建或者说明数组时,为其添加初始值,称为“初始化”。

举例:

如果想初始化数组,二维数组使用大括号。

初始化方式一:不完全初始化

int arr1[3][5] = {1, 2};   // 初始化仅前两个值,其余表充为 0
int arr2[3][5] = {0};      // 数组中所有元素初始化为 0

初始化方式二:完全初始化

int arr3[3][5] = {1, 2, 3, 4, 5,
                  2, 3, 4, 5, 6,
                  3, 4, 5, 6, 7};

初始化方式三:按行初始化

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

初始化方式四:省略行数

如果想省略行数,则初始化中数据能自动推定数组行数:

int arr5[][5] = {1, 2, 3};
int arr6[][5] = {1, 2, 3, 4, 5, 6, 7};
int arr7[][5] = {{1, 2}, {3, 4}, {5, 6}};

二维数组的访问和输出

1.二维数组的访问

二维数组使用二重下标访问:二重下标分别表示行和列。

C++设定:行和列从 0 开始,也即第一行和第一列的下标均为 0。

如:查看第2行第4列,则为:

cout << arr[2][4] << endl;

如果有二维数组:

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

则:

  • arr[2][4] = 第3行第5列的值,即 7。

二维显示:

二维数组 arr:
0 1 2 3 4
0 1 2 3 4 5
1 2 3 4 5 6
2 3 4 5 6 7

2.二维数组的遍历和打印

对二维数组,可以通过层层循环(外循环遍历行,内循环遍历列)实现打印。

示例代码:

#include <iostream>
using namespace std;
int main()
{
    int arr[3][5] = {
        1, 2, 3, 4, 5, 
        2, 3, 4, 5, 6, 
        3, 4, 5, 6, 7
    };
    for (int i = 0; i < 3; i++)  // 遍历行
    {
        for (int j = 0; j < 5; j++)  // 遍历列
        {
            cout << arr[i][j] << " ";  // 打印每个元素
        }
        cout << endl;  // 换行
    }
    return 0;
}

输出结果:

1 2 3 4 5
2 3 4 5 6
3 4 5 6 7

解析:

  • 嵌套循环:
  • 外层循环控制行数i(从0到2,总共 3 行)。
  • 内层循环控制列数j(从0到4,每行 5 列)。
  • cout << arr[i][j] << " ":
  • 每次输出一个元素,后面紧跟空格,确保同一行的元素在一行打印。
  • 换行:
  • 每次打印完一行后,cout << endl;用于换行。

二维数组内容设置:使用 memset 初始化

memset是C/C++中的标准库函数,用来将指定内存块的内容设置为某个固定值。它可以快速初始化数组或将数组重置为某个值。

示例1:将二维数组所有值初始化为0

#include <iostream>
using namespace std;
int main()
{
    int a[10][10];  // 定义一个10x10的二维数组
    memset(a, 0, sizeof(a));  // 将数组的所有字节初始化为0
    for (int i = 0; i < 10; i++)  // 遍历行
    {
        for (int j = 0; j < 10; j++)  // 遍历列
        {
            cout << a[i][j] << " ";  // 打印元素
        }
        cout << endl;  // 换行
    }
    return 0;
}

示例2:将二维数组的所有字节设置为字符a

#include <iostream>
using namespace std;
int main()
{
    char a[10][10];  // 定义一个10x10的字符型二维数组
    memset(a, 'a', sizeof(a));  // 将数组的所有字节设置为字符'a'
    for (int i = 0; i < 10; i++)  // 遍历行
    {
        for (int j = 0; j < 10; j++)  // 遍历列
        {
            cout << a[i][j] << " ";  // 打印元素
        }
        cout << endl;  // 换行
    }
    return 0;
}

输出结果:

示例1:

0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
...

示例2:

a a a a a a a a a a
a a a a a a a a a a
...

注意事项:

  1. memset是按字节设置值的。
  • 对于整型数组,memset(a, 0, sizeof(a));是可行的,因为所有字节都被设置为0,最终值也是0。
  • 但是如果用memset(a, 1, sizeof(a));,每个字节被置为1,会产生意想不到的值(非 1)。
  1. memset适合字符型数组或所有字节统一设置为0的场景。

总结

通过本文,我们深入学习了C++中的二维数组:从基本概念到创建、初始化和访问,再到如何高效遍历和打印二维数组,最后讨论了memset在数组初始化中的应用。掌握这些内容,不仅可以帮助我们解决实际开发中的问题,也为进一步学习更复杂的数据结构打下了基础。

学习C++的过程如同一场探索复杂编程世界的旅程。在这个旅途中,我深刻体会到了C++的强大,同时也感受到它的复杂性。以下是我的一些学习心得和感悟,希望能够对正在学习C++的朋友们有所帮助。

扎实基础是关键

C++的复杂性往往体现在它是一个“多范式”的编程语言,既支持面向过程,也支持面向对象,同时还提供了现代化的泛型编程和函数式编程能力。在学习初期,掌握基础概念非常重要,例如:

  • 变量和数据类型:理解变量的作用域、生命周期,熟悉基础类型(如int、double)以及它们的内存占用。
  • 控制流:熟练掌握if-else、for、while等基本语法结构,这是编程逻辑的核心。
  • 数组和指针:理解一维数组、二维数组,特别是指针与数组之间的关系,以及指针的实际用途。

基础知识扎实,才能在后续深入学习复杂特性时不被细节绊住脚步。

理解面向对象编程的核心

C++是一门面向对象的语言,OOP(Object-Oriented Programming)是其核心思想之一。在学习面向对象编程时,我总结出以下几点心得:

  • 类与对象:理解类是对象的模板,而对象是类的具体实例。要清楚类的定义和对象的创建过程。
  • 封装性:通过public、private和protected等访问权限控制,保护对象的内部状态。
  • 继承性:学会通过继承复用已有代码,并理解多态的作用。尤其需要搞清楚虚函数的机制和作用。
  • 多态性:理解动态绑定和静态绑定的差异,虚函数表(vtable)的概念对理解多态背后的实现至关重要。

抓住C++的现代特性

C++从最初的C++98到现在的C++20,语言特性发生了巨大的变化。学习现代C++特性不仅可以让代码更简洁高效,也可以大幅提升程序的可读性。以下几个现代特性是我学习中的重点:

  • 智能指针(Smart Pointer):传统指针的内存管理很复杂,而现代C++提供了std::shared_ptr、std::unique_ptr等智能指针,大大简化了内存管理。
  • STL(标准模板库):STL是C++的一大优势,学习如何使用容器(如vector、map、set)和算法(如sort、find)可以大幅提高开发效率。
  • lambda表达式:学习lambda表达式可以帮助写出简洁的代码,尤其是在配合STL算法时。
  • constexpr和auto:这些特性使代码更加灵活高效,例如constexpr在编译期计算常量,auto自动推导变量类型。

学会调试和优化

编程不可能一次成功,尤其是面对复杂的C++程序,调试能力显得尤为重要。我在学习过程中发现,掌握以下调试技巧非常关键:

  • 使用调试器:熟练使用gdb或IDE(如VSCode、CLion)的调试工具,能快速定位问题。
  • 日志输出:在关键位置添加调试信息(std::cout),可以快速了解程序的运行状态。
  • 内存调试:借助工具(如Valgrind)检查内存泄漏和未定义行为,这对C++尤为重要。

同时,在程序优化方面,也需要不断思考如何让代码更高效:

  • 算法优化:优化算法的时间复杂度和空间复杂度。
  • 避免冗余拷贝:使用引用传递(const &)或移动语义(std::move)。
  • 线程并发:学习C++的多线程支持(如std::thread、std::async),在多核时代尤为重要。

理解内存管理的复杂性

C++的强大之处在于对内存的直接控制,但这同时也是学习的难点之一。在内存管理方面,我总结了以下几点:

  • 动态内存分配:学会使用new和delete,但同时也要理解其局限性。
  • 避免内存泄漏:每次分配的内存必须确保释放,养成良好的编程习惯。
  • RAII(资源获取即初始化):通过构造函数获取资源,析构函数释放资源,避免手动管理资源的复杂性。
  • 智能指针的使用:用智能指针替代裸指针,是现代C++的推荐实践。

实践是最好的老师

学习编程语言的最终目标是使用它解决实际问题。因此,多写代码、多实践是掌握C++的关键。在学习过程中,我尝试做了以下事情:

  • 实现数据结构和算法:用C++实现常见的数据结构(如链表、栈、队列)和算法(如排序、搜索)。
  • 编写小项目:比如一个简单的学生管理系统,包含类的定义、文件操作、输入输出等。
  • 参与开源项目:在GitHub上寻找C++的开源项目,阅读代码并尝试贡献。

通过实践,我不仅巩固了语法知识,也锻炼了编程思维和解决问题的能力。

坚持学习,永不止步

C++是一门深奥的语言,其应用范围极广,从操作系统到图形渲染、从嵌入式开发到游戏引擎,都能看到它的身影。因此,学习C++是一个持续深入的过程。要保持好奇心和耐心,深入理解语言的细节,同时不断学习最新的语言标准和实践方法。

总结

学习C++是一场挑战,也是一场收获满满的旅程。从基础语法到面向对象,从STL到现代特性,再到内存管理和性能优化,每一步都让我对编程有了更深的理解。C++不仅教会了我编程技能,更让我养成了严谨和高效的思维习惯。

对于初学者,我的建议是:扎实基础,循序渐进;多写代码,多做总结;不怕犯错,坚持不懈。最终,你会发现,C++的世界不仅复杂,更充满无限可能!

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号