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

为何出现std::bad_alloc异常?C++缓冲区分配问题求助

解决std::bad_alloc异常的内存扫描优化方案

这个std::bad_alloc异常说白了就是内存分配撑爆了——你代码里尝试一次性申请整个内存区域的大小,而Windows进程里有些内存区域可能大到几个GB,堆内存根本扛不住这么大的单次分配,直接就抛出异常了。咱们从核心问题入手,一步步修复:

核心问题分析

你原来的代码里char* chunk = new char[info.RegionSize];这行是罪魁祸首:VirtualQueryEx返回的RegionSize可能是非常大的数值(比如系统预留的大内存块),一次性在堆上申请这么多内存,直接超出了堆的可用空间,触发内存分配失败。

具体修复步骤

1. 改用小缓冲区分批读取

不要一次性读取整个内存区域,用固定大小的小缓冲区(比如4KB、64KB)分批次读取,这样不管区域多大,都不会瞬间占用大量内存。比如定义一个4KB的缓冲区:

const size_t BUFFER_SIZE = 4096; // 可根据实际情况调整大小
std::vector<char> chunk(BUFFER_SIZE); // 用vector更安全,自动管理内存

然后在读取时,循环处理每个缓冲区,每次读取的字节数取「剩余区域大小」和「缓冲区大小」的最小值。

2. 过滤不可读的内存区域

不是所有内存区域都能读取,提前过滤掉无效区域,避免无效读取和资源浪费:

  • 跳过未提交的区域:info.State != MEM_COMMIT
  • 跳过无访问权限的区域:(info.Protect & (PAGE_NOACCESS | PAGE_GUARD)) != 0
  • 只保留可读的区域:(info.Protect & (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY)) != 0

3. 修复循环逻辑的小bug

你原来代码里的p = (char*)info.BaseAddress;是多余的,VirtualQueryEx已经把当前区域的基地址存在info.BaseAddress里,循环里的p += info.RegionSize已经能正确移动到下一个区域,这行代码会导致重复处理同一区域。

修改后的完整代码示例

#include <iostream>
#include <vector>
#include <windows.h>
#include <cstring>

using namespace std;

int main() {
    DWORD systemPid = 123; // 替换成目标进程的PID
    // 只申请需要的权限,PROCESS_ALL_ACCESS太容易失败
    HANDLE systemH = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, systemPid);
    
    if (systemH && systemPid > 4) {
        SYSTEM_INFO si;
        GetSystemInfo(&si);
        MEMORY_BASIC_INFORMATION info;
        char* p = reinterpret_cast<char*>(si.lpMinimumApplicationAddress); // 从最小应用地址开始
        
        const size_t BUFFER_SIZE = 4096;
        std::vector<char> chunk(BUFFER_SIZE);

        while (p < reinterpret_cast<char*>(si.lpMaximumApplicationAddress)) {
            if (VirtualQueryEx(systemH, p, &info, sizeof(info)) == sizeof(info)) {
                // 过滤不可读的内存区域
                if (info.State == MEM_COMMIT && 
                    !(info.Protect & (PAGE_NOACCESS | PAGE_GUARD)) &&
                    (info.Protect & (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY))) {

                    char* regionBase = reinterpret_cast<char*>(info.BaseAddress);
                    size_t remainingSize = info.RegionSize;

                    // 分批读取整个区域
                    while (remainingSize > 0) {
                        size_t readSize = min(remainingSize, BUFFER_SIZE);
                        SIZE_T bytesRead;

                        if (ReadProcessMemory(systemH, regionBase, chunk.data(), readSize, &bytesRead)) {
                            // 扫描缓冲区中的"exe"字符串
                            for (size_t i = 0; i < (bytesRead - 2); ++i) { // 注意是bytesRead-2,因为要匹配3个字符
                                if (memcmp("exe", &chunk[i], 3) == 0) {
                                    cout << "Found \"exe\" at address: " << reinterpret_cast<void*>(regionBase + i) << endl;
                                }
                            }
                        }

                        regionBase += bytesRead;
                        remainingSize -= bytesRead;
                    }
                }
                p += info.RegionSize; // 移动到下一个内存区域
            } else {
                // VirtualQueryEx失败,移动一页继续扫描
                p += si.dwPageSize;
            }
        }
        cout << "Finish" << endl;
        CloseHandle(systemH); // 记得关闭进程句柄,避免资源泄漏
    } else {
        cout << "Failed to open process" << endl;
    }
    return 0;
}

额外注意事项

  • 尽量用std::vector代替裸指针的new/delete,避免手动管理内存时出现泄漏或者释放错误
  • 读取内存后一定要检查bytesRead的实际值,避免越界访问缓冲区
  • 申请进程权限时,只申请需要的权限(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ),过高的权限不仅容易申请失败,还存在安全风险

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 19:42:52