C++动态数组两种实现方式详解(std::vector vs malloc)
作者:司徒轩宇
在C++中动态数组是处理可变大小数据集合的常见需求,本文将详细介绍两种主要的实现方式,使用C++标准库的std::vector和使用C风格的malloc,下面就来详细的介绍一下,感兴趣的可以了解一下
在C++中,动态数组是处理可变大小数据集合的常见需求。本文将详细介绍两种主要的实现方式:使用C++标准库的std::vector和使用C风格的malloc,并对比它们的差异、使用方法以及最佳实践。
1. std::vector:现代C++的推荐方式
1.1 基本语法与参数
// 基本语法 std::vector<T> name(size); // 实际示例 std::vector<uint8_t> occlusions(points_cnt);
参数详解:
- 模板参数
T:指定向量中元素的类型,这里是uint8_t(无符号8位整数,通常就是unsigned char) - 构造函数参数
size:指定向量初始的大小,这里是points_cnt(必须是整数类型) - 向量名称
occlusions:变量名,遵循C++命名规则
1.2 如何访问和操作元素
访问单个元素:
// 使用下标运算符(不进行边界检查) occlusions[0] = 255; // 设置第一个元素 uint8_t value = occlusions[0]; // 获取第一个元素 // 使用at()方法(进行边界检查,安全但稍慢) occlusions.at(0) = 255; // 如果索引越界会抛出std::out_of_range异常 uint8_t value = occlusions.at(0); // 使用迭代器 auto it = occlusions.begin(); // 获取起始迭代器 *it = 255; // 通过迭代器访问元素
遍历所有元素:
// 方法1:使用下标循环
for (size_t i = 0; i < occlusions.size(); ++i) {
occlusions[i] = i % 256; // 赋值
std::cout << (int)occlusions[i] << " "; // 访问
}
// 方法2:使用范围for循环(C++11起)
for (uint8_t& val : occlusions) {
val = 128; // 可以直接修改
}
// 方法3:使用迭代器
for (auto it = occlusions.begin(); it != occlusions.end(); ++it) {
*it = 64;
}
1.3 完整示例
#include <iostream>
#include <vector>
#include <cstdint>
void vectorExample() {
size_t points_cnt = 10;
// 创建并初始化向量
std::vector<uint8_t> occlusions(points_cnt, 0); // 所有元素初始化为0
// 修改元素
for (size_t i = 0; i < points_cnt; ++i) {
occlusions[i] = static_cast<uint8_t>(i * 25);
}
// 添加新元素
occlusions.push_back(255); // 自动调整大小
occlusions.emplace_back(128); // C++11:原地构造
// 读取并显示所有元素
std::cout << "Vector contents: ";
for (const auto& val : occlusions) {
std::cout << (int)val << " ";
}
std::cout << std::endl;
// 获取向量信息
std::cout << "Size: " << occlusions.size() << std::endl;
std::cout << "Capacity: " << occlusions.capacity() << std::endl;
// 注意:不需要手动释放内存!
// 当occlusions离开作用域时,vector会自动清理
}
2. malloc:C风格的动态内存分配
2.1 基本语法与参数
// 基本语法 T* name = (T*)malloc(count * sizeof(T)); // 实际示例 unsigned char* occlusions = (unsigned char*)malloc(points_cnt * sizeof(unsigned char));
参数详解:
malloc函数参数:需要分配的字节数 =points_cnt * sizeof(unsigned char)- 返回值类型转换:
malloc返回void*,需要强制转换为目标指针类型 - 指针变量
occlusions:指向分配内存起始地址的指针
2.2 如何访问和操作元素
访问单个元素:
// 使用数组下标语法 occlusions[0] = 255; // 设置第一个元素 unsigned char value = occlusions[0]; // 获取第一个元素 // 使用指针算术 *(occlusions) = 255; // 等价于occlusions[0] = 255 *(occlusions + 1) = 128; // 等价于occlusions[1] = 128 unsigned char value = *(occlusions + 2); // 等价于occlusions[2]
遍历所有元素:
// 使用指针遍历
unsigned char* ptr = occlusions;
for (size_t i = 0; i < points_cnt; ++i) {
*ptr = static_cast<unsigned char>(i * 25);
++ptr; // 移动指针
}
// 使用下标遍历
for (size_t i = 0; i < points_cnt; ++i) {
occlusions[i] = i % 256;
}
2.3 完整示例
#include <iostream>
#include <cstdlib> // malloc, free
#include <cstring> // memset
void mallocExample() {
size_t points_cnt = 10;
// 分配内存
unsigned char* occlusions =
(unsigned char*)malloc(points_cnt * sizeof(unsigned char));
// 重要:检查分配是否成功
if (occlusions == nullptr) {
std::cerr << "Memory allocation failed!" << std::endl;
return;
}
// 重要:初始化内存(malloc不初始化内存!)
memset(occlusions, 0, points_cnt * sizeof(unsigned char));
// 填充数据
for (size_t i = 0; i < points_cnt; ++i) {
occlusions[i] = static_cast<unsigned char>(i * 25);
}
// 显示内容
std::cout << "Malloc array contents: ";
for (size_t i = 0; i < points_cnt; ++i) {
std::cout << (int)occlusions[i] << " ";
}
std::cout << std::endl;
// 重要:必须手动释放内存!
free(occlusions);
occlusions = nullptr; // 避免野指针
}
3. 详细对比与选择指南
3.1 内存管理对比
| 特性 | std::vector | malloc/free |
|---|---|---|
| 初始化 | 默认初始化元素 | 不初始化,内容是随机的 |
| 内存释放 | 自动(RAII) | 必须手动调用free() |
| 异常安全 | 是,构造函数失败会抛出异常 | 否,需要检查返回值 |
| 重新分配 | 自动(resize/push_back) | 需要realloc(),数据可能被移动 |
3.2 性能与安全对比
// std::vector的安全性示例
void safeVectorExample() {
std::vector<uint8_t> vec(10);
try {
vec.at(20) = 100; // 抛出std::out_of_range异常
} catch (const std::out_of_range& e) {
std::cout << "安全地捕获了越界访问: " << e.what() << std::endl;
}
// vec[20] = 100; // 未定义行为,可能崩溃或数据损坏
}
// malloc的不安全性示例
void unsafeMallocExample() {
unsigned char* arr = (unsigned char*)malloc(10);
// 常见错误1:忘记检查分配是否成功
if (arr == nullptr) { /* 必须检查! */ }
// 常见错误2:忘记初始化
// arr[0]可能是任意值!
// 常见错误3:越界访问
arr[15] = 100; // 未定义行为,可能破坏其他内存
// 常见错误4:忘记释放内存(内存泄漏)
// free(arr); // 如果忘记这行,内存泄漏!
}
4. 高级用法与技巧
4.1 std::vector的高级特性
void advancedVectorUsage() {
// 多种初始化方式
std::vector<uint8_t> v1(10, 0xFF); // 10个元素,每个都是0xFF
std::vector<uint8_t> v2 = {0, 1, 2, 3, 4}; // 初始化列表(C++11)
std::vector<uint8_t> v3(v1); // 拷贝构造
// 内存预分配
v1.reserve(1000); // 预分配内存,避免多次重新分配
// 安全访问
if (!v1.empty()) {
uint8_t first = v1.front(); // 第一个元素
uint8_t last = v1.back(); // 最后一个元素
}
// 范围操作
v1.insert(v1.begin(), {10, 20, 30}); // 开头插入多个元素
v1.erase(v1.begin() + 1, v1.begin() + 3); // 删除第2-3个元素
// C++17:并行算法支持
std::sort(std::execution::par, v1.begin(), v1.end());
}
4.2 malloc的高级用法与陷阱
void advancedMallocUsage() {
size_t count = 10;
// 正确:使用calloc自动初始化为0
unsigned char* arr1 = (unsigned char*)malloc(count * sizeof(unsigned char));
// arr1的所有元素都是0
// 正确:使用realloc调整大小
arr1 = (unsigned char*)realloc(arr1, count * 2);
// 注意:realloc可能移动内存,原有指针失效
// 陷阱:错误的大小计算
// 错误:可能溢出
size_t large = 1000000000;
// unsigned char* bad = malloc(large * large); // 溢出!
// 正确:检查溢出
if (large > SIZE_MAX / sizeof(unsigned char)) {
// 处理溢出错误
}
// 释放内存
free(arr1);
}
到此这篇关于C++动态数组两种实现方式详解(std::vector vs malloc)的文章就介绍到这了,更多相关C++动态数组内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
