侯捷 C++ 课程学习笔记:C++数组

C++数组

目录

C++数组

✨前言✨

📚本篇目标

🔥重难点说明

📖学习正文

一、数组基础概念

二、一维数组的使用

三、数组与函数

四、多维数组

五、常见操作

六、数组与指针

 七、标准库容器

八、应用案例

💻练习题

单选题-5

多选题-3

判断题-2

代码题-1

C++的优势对比于python的维度点说明


✨前言✨

本系列文章目的在于将C++的基础内容完全夯实,最终目的是为后期的深度学习在算法上有一定的铺垫,前期在学习数学的过程中也会有很大的帮助,相对于python来说C++有自身的优势,文末会有 C++的优势对比于python的维度点说明,所以我这里先写了C++的用法说明,后续会有Python的,各自有各自的优势,我们要根据具体的需求来分析使用哪种语言更为方便,其它的语言暂时不在考虑范畴之内,我们的目标是AI深度学习。

前置环境与代码结构文章:

1、C++环境理解与配置(MinGW)

2、C++的Visual Studio Code运行环境配置

3、头文件与类的声明

整体文章目录:入门 C++ 课程目录

📚本篇目标

  1. 理解数组的基本概念与内存结构
  2. 掌握一维和多维数组的声明、初始化和操作方法
  3. 熟悉数组与函数、指针的交互方式
  4. 了解标准库容器std::array和std::vector的特性
  5. 通过案例实践数组的实际应用场景

🔥重难点说明

  • 重点:数组的声明与遍历、数组与指针的关系、动态内存分配
  • 难点:多维数组的内存布局、函数返回数组的实现、std::vector的动态扩容机制

📖学习正文

一、数组基础概念

什么是数组

数组是相同类型元素的连续内存集合,通过索引访问元素。

声明与初始化

#include <iostream>
using namespace std;

int main() {
    int arr[5]; // 声明未初始化  
    int scores[] = {90, 85, 78}; // 声明并初始化  
    int nums[3][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }; // 多维数组初始化
    char vowels[] = {'a', 'e', 'i', 'o', 'u'}; // 字符数组初始化
    string names[] = {"John", "Mary", "Bob"}; // 字符串数组初始化
    int length = sizeof(arr) / sizeof(arr[0]); // 获取数组长度
    return 0;
}
二、一维数组的使用

访问元素

arr[0] = 10; // 首元素赋值  

遍历数组

for (int i = 0; i < 5; i++)  
    cout << arr[i] << " ";
三、数组与函数

数组作为参数

void printArray(int arr[], int size) { /*...*/ }  

函数返回数组
需通过指针或容器实现(直接返回数组地址存在风险)。

四、多维数组

二维数组声明

int matrix[3][4] = {
  {1,2,3,4}, {5,6,7,8}};  
五、常见操作

查找元素
线性查找或二分查找(需有序)。
排序
冒泡排序示例:

for (int i = 0; i < n-1; i++)  
    for (int j = 0; j < n-i-1; j++)  
        if (arr[j] > arr[j+1]) swap(arr[j], arr[j+1]);
六、数组与指针

关系
数组名是首元素地址:arr == &arr[0]。
动态数组

int* dynamicArr = new int[10];  
delete[] dynamicArr; // 必须手动释放  
 七、标准库容器

std::array
固定大小,安全替代原生数组:

#include <array>  
std::array<int, 5> myArray;  

std::vector
动态扩容:

#include <vector>  
std::vector<int> vec = {1, 2, 3};  
vec.push_back(4);  
八、应用案例

学生成绩系统

用数组存储成绩并计算平均分。

#include <iostream>

int main() {
    // 定义数组大小,假设这里存储 5 个学生的成绩
    const int SIZE = 5;
    // 定义一个数组来存储成绩
    double scores[SIZE];

    // 提示用户输入成绩
    std::cout << "请输入 " << SIZE << " 个学生的成绩:" << std::endl;
    for (int i = 0; i < SIZE; ++i) {
        std::cout << "请输入第 " << (i + 1) << " 个学生的成绩: ";
        std::cin >> scores[i];
    }

    // 计算成绩总和
    double sum = 0;
    for (int i = 0; i < SIZE; ++i) {
        sum += scores[i];
    }

    // 计算平均分
    double average = sum / SIZE;

    // 输出平均分
    std::cout << "这 " << SIZE << " 个学生的平均成绩是: " << average << std::endl;

    return 0;
}

图像处理

二维数组表示像素矩阵(如灰度图像)。

#include <iostream>
#include <fstream>
#include <vector>

// 定义图像类
class GrayscaleImage {
private:
    int width;
    int height;
    std::vector<std::vector<int>> pixels;

public:
    // 构造函数,初始化图像
    GrayscaleImage(int w, int h) : width(w), height(h) {
        pixels.resize(height, std::vector<int>(width, 0));
    }

    // 模拟读取图像
    void readImage() {
        std::cout << "请输入 " << height << "x" << width << " 的灰度图像像素值(范围 0 - 255):" << std::endl;
        for (int y = 0; y < height; ++y) {
            for (int x = 0; x < width; ++x) {
                std::cin >> pixels[y][x];
                // 确保像素值在 0 - 255 范围内
                pixels[y][x] = std::max(0, std::min(255, pixels[y][x]));
            }
        }
    }

    // 显示图像
    void displayImage() {
        std::cout << "当前图像的像素矩阵:" << std::endl;
        for (int y = 0; y < height; ++y) {
            for (int x = 0; x < width; ++x) {
                std::cout << pixels[y][x] << " ";
            }
            std::cout << std::endl;
        }
    }

    // 灰度反转处理
    void invertImage() {
        for (int y = 0; y < height; ++y) {
            for (int x = 0; x < width; ++x) {
                pixels[y][x] = 255 - pixels[y][x];
            }
        }
    }

    // 模拟保存图像
    void saveImage(const std::string& filename) {
        std::ofstream outFile(filename);
        if (outFile.is_open()) {
            for (int y = 0; y < height; ++y) {
                for (int x = 0; x < width; ++x) {
                    outFile << pixels[y][x] << " ";
                }
                outFile << std::endl;
            }
            outFile.close();
            std::cout << "图像已保存到 " << filename << std::endl;
        } else {
            std::cerr << "无法打开文件 " << filename << " 进行保存。" << std::endl;
        }
    }
};

int main() {
    // 定义图像的宽度和高度
    int width = 3;
    int height = 3;

    // 创建灰度图像对象
    GrayscaleImage image(width, height);

    // 读取图像
    image.readImage();

    // 显示原始图像
    std::cout << "原始图像:" << std::endl;
    image.displayImage();

    // 进行灰度反转处理
    image.invertImage();

    // 显示处理后的图像
    std::cout << "灰度反转后的图像:" << std::endl;
    image.displayImage();

    // 保存处理后的图像
    image.saveImage("output_image.txt");

    return 0;
}

💻练习题

单选题-5

  1. 以下声明正确的是:
    A. int arr[];
    B. int arr[3] = {1,2,3,4};
    C. int arr[3] = {1};
    D. int arr[ ] = ;
    答案:C

  2. 数组索引从什么开始?
    A. 0
    B. 1
    C. 任意值
    答案:A

  3. int arr[5]占用的内存大小是:
    A. 5
    B. 5*sizeof(int)
    C. sizeof(arr[0])
    答案:B

  4. 动态数组释放应使用:
    A. delete
    B. delete[]
    C. free()
    答案:B

  5. std::vector的优点是:
    A. 固定大小
    B. 自动内存管理
    C. 不支持迭代器
    答案:B

多选题-3

  1. 数组初始化方式包括:
    A. int arr[3] = {0};
    B. int arr[];
    C. int arr[] = {1,2};
    答案:AC

  2. 数组作为函数参数时传递的是:
    A. 数组副本
    B. 首元素地址
    C. 数组引用
    答案:BC

  3. 属于标准库容器的是:
    A. std::array
    B. std::list
    C. std::vector
    答案:AC

判断题-2

  1. 数组越界访问会导致编译错误。
    答案:×(运行时错误)

  2. std::vector可以自动扩容。
    答案:√

代码题-1

编写函数,返回整型数组中的最大值。

int findMax(int arr[], int size) {  
    int max = arr[0];  
    for (int i = 1; i < size; i++)  
        if (arr[i] > max) max = arr[i];  
    return max;  
}

C++的优势对比于python的维度点说明

对比维度C++ 优势Python 情况
性能效率1. 执行速度快:编译型语言,将代码编译为机器码,处理大量数据、复杂算法时效率高,能充分利用硬件资源。
2. 内存管理精细:允许程序员直接管理内存,精确控制分配和释放,优化内存使用,适合对内存要求苛刻的系统。
1. 执行速度慢:解释型语言,代码逐行解释执行,在处理大规模数据和复杂计算时性能较差。
2. 内存管理自动化:Python 有自动的垃圾回收机制,虽然方便但可能导致内存使用不够精细,在内存紧张的场景下可能出现问题。
底层控制能力1. 硬件访问能力强:可直接访问计算机硬件资源,如寄存器、内存地址等,便于与底层硬件高效交互,适用于驱动程序、嵌入式系统开发。
2. 系统级编程优势:在操作系统、编译器、数据库管理系统等系统软件开发中,能与底层系统更好交互,实现资源精细管理和控制,提升系统性能和稳定性。
底层控制能力弱:Python 主要用于高层级的应用开发,对底层硬件的直接访问能力有限,在系统级编程方面不够灵活和高效。
语言特性1. 强类型语言:编译时进行严格类型检查,有助于发现早期错误,提高代码稳定性和可靠性,减少运行时类型不匹配错误。
2. 支持多种编程范式:支持面向对象、泛型、函数式等多种编程范式,开发者可根据问题和需求灵活选择。
1. 动态类型语言:类型检查在运行时进行,虽然编写代码更灵活,但可能在运行时出现类型相关的错误,代码的稳定性和可维护性相对较弱。
2. 编程范式相对有限:主要以面向对象和函数式编程为主,泛型编程的支持不如 C++ 强大。
可移植性1. 跨平台能力:编写的代码可在不同操作系统上编译运行,只要有相应编译器和运行时环境,适合开发跨平台应用。
2. 库的可移植性:许多开源库和框架(如 Boost、Qt)具有良好可移植性,可在不同平台使用,减少跨平台开发工作量。
1. 跨平台性依赖解释器:Python 代码的跨平台性依赖于 Python 解释器,虽然大多数情况下能在不同操作系统上运行,但在一些涉及底层系统调用的场景下可能会有兼容性问题。
2. 部分库存在兼容性问题:虽然 Python 有丰富的库,但部分库在不同平台上的表现和兼容性可能存在差异。
生态系统和工具链1. 成熟的开发工具:有 Visual Studio、CLion、Eclipse 等成熟开发工具和集成开发环境,提供强大代码编辑、调试、性能分析等功能,提高开发效率。
2. 丰富的库资源:拥有大量专业级库,如用于数学计算的 LAPACK、图形处理的 OpenGL、网络编程的 ACE 等,为专业领域开发提供支持。
1. 开发工具相对简单:Python 的开发工具(如 PyCharm、VS Code 等)功能相对侧重于代码编辑和基本调试,在一些复杂的性能分析和底层调试方面不如 C++ 的开发工具强大。
2. 库更侧重于高层应用:Python 的库主要用于数据分析、机器学习、Web 开发等高层应用场景,在底层系统编程和高性能计算方面的专业库相对较少。
安全性和稳定性1. 资源管理安全:通过智能指针等机制,一定程度上保证资源安全管理,避免内存泄漏和悬空指针问题,提升程序稳定性和安全性。
2. 代码审查严格:语法相对复杂,在大型项目中严格的代码审查和规范可确保代码质量和安全性,减少潜在漏洞和风险。
1. 资源管理自动化存在隐患:自动垃圾回收机制可能导致一些资源管理问题,例如循环引用可能会造成内存泄漏,虽然 Python 有解决方案,但增加了复杂性。
2. 代码灵活性带来风险:动态类型和简洁的语法使得代码编写容易,但也可能导致代码的可读性和可维护性下降,增加了出现安全漏洞的风险。
### 关于侯捷 C++ 系列课程学习笔记 #### 内存管理和 `std::allocator` 在探讨内存管理时,`std::allocator` 是一个重要的概念。它提供了一种通用的方式来进行动态内存分配和释放操作[^1]。通过使用 `std::allocator`,可以更灵活地控制容器内部对象的创建与销毁。 ```cpp #include <memory> using namespace std; int main() { allocator<int> alloc; const int n = 1000; int* p = alloc.allocate(n); // 构造元素 uninitialized_fill_n(p, n, 0); // 销毁并回收内存 destroy_n(p, n); alloc.deallocate(p, n); } ``` 这段代码展示了如何利用 `std::allocator` 来手动管理一块整型数据的空间,并对其进行初始化以及最终清理工作。 #### 使用迭代器简化输入处理 对于从标准输入流读取数值并将这些值存储至目标容器的操作,可以通过组合 `istream_iterator` 和算法库中的 `copy()` 函数来实现简洁高效的解决方案[^2]: ```cpp #include <iostream> #include <iterator> #include <vector> #include <algorithm> using namespace std; int main(){ vector<double> c; istream_iterator<double> eos; // 表示结束标记 istream_iterator<double> iit(cin); // 创建基于cin的输入迭代器 copy(iit, eos, back_inserter(c)); // 将所有输入复制到向量c中 } ``` 此程序片段能够方便地接收来自用户的多个浮点数作为输入,并自动将其追加到指定的目标集合内而无需显式循环结构。 #### 移动语义的重要性及其应用 当涉及到像 `std::vector` 这样的序列式容器时,确保自定义类型的移动构造函数和赋值运算符被声明为 `noexcept` 至关重要。这不仅有助于提高性能,还可能影响某些 STL 容器的行为逻辑,比如扩容机制会优先考虑调用 noexcept 的版本以减少异常风险[^3]。 ```cpp class MyClass { public: MyClass(MyClass&& other) noexcept : member(std::move(other.member)) {} MyClass& operator=(MyClass&& other) noexcept { if (this != &other){ member = std::move(other.member); } return *this; } private: string member; }; ``` 上述类实现了无抛出保证的右值引用重载方法,从而使得该类型更适合用于频繁变动大小的数据结构之中。 #### 初始化列表 (`initializer_list`) 及其特性 最后,在介绍现代 C++ 特性的过程中不得不提到 `initializer_list`。这是一种特殊的模板参数形式,允许我们采用花括号语法传递一组同质化的初始值给构造函数或其他接受此类参数的地方[^4]。值得注意的是,尽管看起来像是拥有自己的副本,实际上 initializer_list 并不持有任何实际的对象实例——它仅仅是指向原始数组的一个视图而已。 ```cpp template<typename T> void print(const initializer_list<T>& ilist){ for(auto elem : ilist) cout << elem << " "; } // 调用方式如下所示: print({1, 2, 3}); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值