You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在unique_ptr字节数组中访问指定范围的字节?

问题解决与优化建议

更新:改用新读取方法(见下文)

原问题背景

刚接触C++,有少量Unreal开发经验,正在编写独立文件解析器。需求是从unique_ptr管理的字节数组中,从未知起始位置读取未知长度的字节,同时希望优化代码的整洁度与执行效率。

当前已实现文件读取函数,将文件内容存入unique_ptr<std::byte[]>并返回给fileData;同时编写了从二进制字节转换为uint32_t的函数,但逐个字节移位的方式效率较低;最后在读取压缩字节段时,memcpy调用失败,卡在相关代码行。

当前代码

文件读取函数

auto ReadFileData(std::string path) {
    auto fileSize = std::filesystem::file_size(path);
    auto buf = std::make_unique<std::byte[]>(fileSize);
    std::basic_ifstream<std::byte> ifs(path, std::ios::binary);
    ifs.read(buf.get(), fileSize);
    return buf;
}

uint32_t解析函数

uint32_t ParseUint32(auto& fileData, uint32_t dataLocation) {
    return uint32_t((unsigned char)(fileData[dataLocation+3]) << 24 |
        (unsigned char)(fileData[dataLocation + 2]) << 16 |
        (unsigned char)(fileData[dataLocation + 1]) << 8 |
        (unsigned char)(fileData[dataLocation]));
}

出错的压缩段读取代码

for(uint16_t i = 0; i<tileOffsetCount; i++)
{
    tileOffsets[i] = ParseUint32(fileData,tileOffset + (i*4));
    uint32_t tileByteSize = ParseUint32(fileData,tileByteCountsOffset +(i*4)) ;
    char* tileData = new char[tileByteSize];
    std::memcpy(tileData, fileData+tileOffsets[i],tileByteSize); // 此行报错
}

问题解决与优化方案

1. 修复memcpy调用失败问题

std::unique_ptr<std::byte[]>不支持直接用operator+做指针偏移,必须先通过get()获取底层指针再进行偏移。同时建议用智能指针管理tileData避免内存泄漏:

for(uint16_t i = 0; i<tileOffsetCount; i++)
{
    tileOffsets[i] = ParseUint32(fileData,tileOffset + (i*4));
    uint32_t tileByteSize = ParseUint32(fileData,tileByteCountsOffset +(i*4));
    // 用unique_ptr自动管理内存,避免手动delete
    auto tileData = std::make_unique<char[]>(tileByteSize);
    // 获取底层指针后再偏移,同时确保类型兼容
    std::memcpy(tileData.get(), fileData.get() + tileOffsets[i], tileByteSize);
    // 后续处理tileData...
}

2. 优化ParseUint32的执行效率

逐个字节移位拼接的方式效率较低,可直接读取4字节内存并处理字节序,推荐使用C++20的std::bit_cast(类型安全),或结合字节序判断实现跨平台兼容:

方案1:C++20+ 类型安全实现(小端转主机字节序)

#include <bit>

uint32_t ParseUint32(const std::byte* data) {
    uint32_t value;
    std::memcpy(&value, data, sizeof(value));
    // 假设原始数据是小端字节序,转换为主机字节序
    if constexpr (std::endian::native == std::endian::big) {
        value = std::byteswap(value);
    }
    return value;
}

// 调用方式:ParseUint32(fileData.get() + dataLocation)

方案2:兼容旧标准的实现

#include <cstring>

uint32_t ParseUint32(const std::byte* data) {
    uint32_t value;
    std::memcpy(&value, data, sizeof(value));
    // 手动转换小端到主机字节序(如果主机是大端)
    #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
        value = ((value & 0x000000FF) << 24) |
                ((value & 0x0000FF00) << 8) |
                ((value & 0x00FF0000) >> 8) |
                ((value & 0xFF000000) >> 24);
    #endif
    return value;
}

3. 代码整洁度优化建议

  • 增加错误处理:ReadFileData中未处理文件不存在、读取失败等情况,需补充检查:
    std::unique_ptr<std::byte[]> ReadFileData(const std::string& path) {
        std::error_code ec;
        const auto fileSize = std::filesystem::file_size(path, ec);
        if (ec) {
            // 处理文件大小获取失败,比如返回空指针或抛出异常
            return nullptr;
        }
        auto buf = std::make_unique<std::byte[]>(fileSize);
        std::basic_ifstream<std::byte> ifs(path, std::ios::binary);
        if (!ifs) {
            // 处理文件打开失败
            return nullptr;
        }
        ifs.read(buf.get(), fileSize);
        if (ifs.gcount() != fileSize) {
            // 处理读取不完整的情况
            return nullptr;
        }
        return buf;
    }
    
  • 使用std::span简化数组操作:用std::span<const std::byte>代替直接传递unique_ptr,让函数更通用且安全:
    uint32_t ParseUint32(std::span<const std::byte> data, size_t offset) {
        // 先检查offset+4是否超出data范围,避免越界
        if (offset + sizeof(uint32_t) > data.size()) {
            // 处理越界错误
            throw std::out_of_range("ParseUint32: offset out of bounds");
        }
        return ParseUint32(data.data() + offset); // 复用上面的优化函数
    }
    
  • 避免裸数组,用std::vector管理动态数组:比如tileOffsets建议用std::vector<uint32_t>代替固定大小数组,自动管理内存并避免越界:
    std::vector<uint32_t> tileOffsets(tileOffsetCount);
    

内容的提问来源于stack exchange,提问作者r3dstar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 23:42:11