V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
SmartTom
V2EX  ›  程序员

关于 C++读取大小为 2.51 GB (2,701,131,776 字节)到 char* yuanshuju 数组中去

  •  
  •   SmartTom · 37 天前 · 2609 次点击
    这是一个创建于 37 天前的主题,其中的信息可能已经有所发展或是发生改变。
    如题,现在想实现将这个大文件(大小超过 int 最大范围)的文件放入到 char* yuanshuju = new char[2,701,131,776]中
    现在就卡在第一步定义 yuanshuju 这一步,qt 一直提示"terminate called after throwing an instance of 'std::bad_alloc'
    what(): std::bad_alloc",本人没太做过 c++,这个提示是代表不能定义那么大的数组吗?还有别的可行方案吗。
    系统:win11 64 位
    QT: 5.12.12
    第 1 条附言  ·  37 天前

    发现问题了。 问题代码:

    qint64 dataSize = 2701131776;
    char* yuanshuju = new char[dataSize];
    

    可行代码:

    char* yuanshuju = new char[2701131776];
    

    c++,牛 我反正是没搞懂

    45 条回复    2024-03-21 21:40:25 +08:00
    yougg
        1
    yougg  
       37 天前   ❤️ 3
    先把变量名改为 metadata 吧, 看到这里就难受无法继续阅读了......
    proxytoworld
        2
    proxytoworld  
       37 天前
    同意一楼的
    iOCZS
        3
    iOCZS  
       37 天前   ❤️ 2
    是 rawdata ,不是 metadata
    @yougg
    deplives
        4
    deplives  
       37 天前
    这个 yuanshuju 血压升高
    Mithril
        5
    Mithril  
       37 天前   ❤️ 4
    用 memory mapping ,不要手动去读它。
    silentx
        6
    silentx  
       37 天前
    是啥东西要一次性读到内存里处理?不考虑分段读吗?不是每个人的机器都是 32G 内存起步的啊。。。
    maokg
        7
    maokg  
       37 天前
    不知道一次性读 2.51GB 数据的应用场景是什么
    SmartTom
        8
    SmartTom  
    OP
       37 天前
    @deplives 哈哈 我也是接手别人的项目,这不是我的风格。
    geelaw
        9
    geelaw  
       37 天前   ❤️ 2
    new char[2,701,131,776] 会分配长度是 776 个元素的 char 数组,不是你期待的 2.51 GB 。

    int main() { new char[2701131776] { }; }

    这个程序在我的电脑上编译为 64 位的话可以正常运行,并且确实占用了 2.51 GB 内存,如果用 32 位编译器则编译失败,因为分配的数组规模超过了 size_t 。

    抛出 std::bad_alloc 的意思就是分配失败,或许你的页面文件和实体内存不够大。

    如果数据来自于文件,可以用内存映射文件,Windows 的文档是 https://learn.microsoft.com/en-us/windows/win32/memory/file-mapping
    POSIX 的文档是 https://pubs.opengroup.org/onlinepubs/9699919799/functions/mmap.html
    SmartTom
        10
    SmartTom  
    OP
       37 天前
    @maokg 光谱数据处理,体谅挺大的
    Crawping
        11
    Crawping  
       37 天前
    理论 64 位程序的寻址空间是可以到 2'64 次字节
    ```C++
    //你是真的这么写的 还是手写拷贝的? 中间为啥会有 ',' 逗号
    char* yuanshuju = new char[2,701,131,776]
    ```
    学其他语言问 GPT 比发帖要来得快
    geelaw
        12
    geelaw  
       37 天前   ❤️ 1
    @geelaw #9 没有超过 size_t (((φ(◎ロ◎;)φ))) 是 MSVC 只支持至多 2147483647 个元素。
    SmartTom
        13
    SmartTom  
    OP
       37 天前
    @Crawping 这个我的问题,我是手动在编辑标题的时候赋值计算器里面的值,实际是去掉','的
    yougg
        14
    yougg  
       37 天前
    @iOCZS #3 "元数据"不是"原始数据"
    tool2d
        15
    tool2d  
       37 天前
    我用 malloc ,别说是 malloc(2701131776)可以, 就算 malloc(4701131776)也行。

    应该只是你内存不够的原因。”不够“不是说总剩余内存不够,而是无法分配一块巨大内存。
    shuax
        16
    shuax  
       37 天前
    改成 64 位轻轻松松。
    greycell
        17
    greycell  
       37 天前
    笑死,元数据 2 个 g ,还在嘴硬。
    MoYi123
        18
    MoYi123  
       37 天前   ❤️ 1
    建议发一下编译器的版本

    或者试试看这样写
    auto alloc = vector<int64_t>(2701131776 / 8 + 1);
    char* yuanshuju = reinterpret_cast<char*>(alloc.data());
    clue
        19
    clue  
       37 天前
    能流式处理不? 为什么一定要全读到内存里?
    royking930911
        20
    royking930911  
       37 天前
    从原数据着手吧 一次申请几个 G 的内存就算这次运行不出问题 下次也会崩
    修改方法:1,分段读取 分段解析
    2,优化算法 保存分段前后缓存数据和状态
    yuzii
        21
    yuzii  
       37 天前
    yolee599
        22
    yolee599  
       37 天前
    这么大一个数组,我看着都头疼,考虑分多次处理吧,每次大概分配 4096 字节
    shawnsh
        23
    shawnsh  
       37 天前 via Android
    大力出奇迹啊,先设计设计
    SmartTom
        24
    SmartTom  
    OP
       37 天前
    发现问题了。
    问题代码:
    qint64 dataSize = 2701131776;
    char* yuanshuju = new char[dataSize];
    可行代码:
    char* yuanshuju = new char[2701131776];

    c++,牛 我反正是没搞懂
    SmartTom
        25
    SmartTom  
    OP
       37 天前
    @yolee599 是头疼,等一次采集任务结束,还要处理 50GB 的数据,哎~
    iold
        26
    iold  
       37 天前   ❤️ 2
    @SmartTom #24 试试使用文档中提供的宏呢?
    Geekerstar
        27
    Geekerstar  
       37 天前
    我还以为是 yushengjun (余胜军)
    amorphobia
        28
    amorphobia  
       37 天前
    @SmartTom

    C++ 标准不支持“柔性数组/变长数组”,数组大小是变量的情况,应该用 vector

    一定要使用变量作数组长度的话,换 C 语言 (C99) 吧
    vsomeone
        29
    vsomeone  
       37 天前   ❤️ 1
    根据你给的附言,推测可能是你 qtglobal 头文件引入的有问题,导致 qint64 被认为是 int32_t 。因为 2701131776 > INT_MAX(1 << 32 - 1),这个值在 assign 给 int32_t dataSize 之后被认为是一个负数,导致 bad_alloc 。
    vsomeone
        30
    vsomeone  
       37 天前
    @amorphobia 他这个不是变长数组呀,用了 new 分配在堆上的,变长数组指的是分配在栈上的
    nagisaushio
        31
    nagisaushio  
       37 天前 via Android
    这么大文件为什么不 mmap?
    MrKrabs
        32
    MrKrabs  
       37 天前
    2.51G 就大了?你们用的都是黄金内存吗
    amorphobia
        33
    amorphobia  
       37 天前 via iPhone
    @vsomeone 是我想错了
    march1993
        34
    march1993  
       37 天前
    一个动态申请的,一个是放在静态区的?
    JustdoitSoso
        35
    JustdoitSoso  
       37 天前 via Android   ❤️ 1
    @vsomeone 感觉你说的是最有可能得情况,人家来请教问题,一群没分析没讨论的在说变量命名。
    yuruizhe
        36
    yuruizhe  
       37 天前
    试试用
    constexpr qint64 dataSize = 2701131776;
    行不行?
    NEO17
        37
    NEO17  
       37 天前
    看了这段代码,不建议再写 C++ :)
    litguy
        38
    litguy  
       37 天前
    这个不是应该 mmap 的方式访问么 ?
    araraloren
        39
    araraloren  
       37 天前
    Why you need load whole data to memory?
    ltyj2003
        40
    ltyj2003  
       37 天前 via Android
    动态长度用 vector
    或者定义一个 char* ,然后 malloc 分配内存空间。
    cnbatch
        41
    cnbatch  
       37 天前
    @amorphobia 这么大的 VLA ,存在爆栈的可能性吧(视乎编译器做法而定)
    amorphobia
        42
    amorphobia  
       37 天前
    @cnbatch

    是我想错了,这里不是 VLA

    如果真有这么大的 VLA ,我也高度怀疑会爆
    cnbatch
        43
    cnbatch  
       37 天前   ❤️ 2
    等等,为什么要用 new 直接创建呢?最后还得手动 delete 。
    不如用 std::vector 或者 std::make_unique<char[]>(数据长度)
    这两个好多了

    std::vector 已经有人发了,那么 make_unique 的用法是:

    size_t data_size{2701131776};
    std::unique_ptr<char[]> raw_data_ptr = std::make_unique<char[]>(data_size);
    char* raw_data = raw_data_ptr.get();

    在我的 Windows 11 + VS2022 测了下,很成功,没任何报错。

    另外呢,直接用 malloc 、new 创建的空间,按照 C 语言留下来的“惯例”,是需要手动初始化的。
    通常会用 memset 初始化为零,用 std::fill 也可以。

    如果改用 std::vector 或者 std::make_unique ,就可以跳过这一步,它们都会自动初始化。
    yougotme
        44
    yougotme  
       36 天前 via iPhone
    @SmartTom 立即数后面加上 LL ,明确指定长度。否则编译器可能会误判你是 int32 转 int64 ,可能变负数、可能溢出。
    yougotme
        45
    yougotme  
       36 天前 via iPhone
    vs2022 肯定会抛一个警告出来的,也许你忽略了或者编译器不够新
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2994 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 09:14 · PVG 17:14 · LAX 02:14 · JFK 05:14
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.