windows下文件映射本质是os api能力,通过createfilemapping+mapviewoffile将文件页映射到进程地址空间,返回指针可直接类型转换访问;linux对应mmap,需注意prot/flags、页对齐及sigbus;跨平台禁用shared_ptr管理,应raii封装;结构体布局与字节序须严格匹配。

Windows下用CreateFileMapping和MapViewOfFile映射大文件
直接用指针读取文件内容,本质是绕过标准I/O缓冲,让操作系统把文件页映射进进程地址空间——这不是C++语言特性,而是OS API能力。Windows上最常用的是CreateFileMapping + MapViewOfFile组合。
关键点在于:映射后返回的void*可强制转为任意类型指针,之后所有访问都像操作内存数组一样快,不触发系统调用。
常见错误包括:CreateFile没加GENERIC_READ、映射大小设为0、MapViewOfFile返回NULL却没检查、忘记UnmapViewOfFile和CloseHandle导致句柄泄漏。
- 文件必须以
CREATE_ALWAYS或OPEN_EXISTING打开,且权限含GENERIC_READ -
CreateFileMapping的dwMaximumSizeHigh/dwMaximumSizeLow要填真实文件大小(64位需拆成高低32位) -
MapViewOfFile的dwNumberOfBytesToMap可以小于文件大小,但起始偏移必须是系统页对齐(通常是4KB),否则失败 - 映射区域不能跨进程共享时,
hFile传INVALID_HANDLE_VALUE并指定大小,此时是纯内存映射(类似malloc)
Linux下用mmap替代方案及PROT_READ陷阱
Linux没有CreateFileMapping,对应的是mmap系统调用。它返回的void*同样是可直接转类型的指针,但参数语义差异大,容易出错。
最常踩的坑是prot参数设错:PROT_READ只读映射没问题,但如果后续想用memcpy往里写(比如预填充结构体),必须同时设PROT_WRITE,且flags中得有MAP_SHARED(否则写入不落盘)。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
-
fd必须是已打开的只读/读写文件描述符,且lseek位置不影响映射——映射范围由offset和length决定 -
offset必须是页对齐地址(getpagesize()返回值),否则mmap失败并返回MAP_FAILED - 映射后若文件被其他进程截断,访问超出新长度的部分会触发
SIGBUS信号,不是段错误 - 用完必须调
munmap,否则虚拟内存泄漏(比堆内存泄漏更隐蔽)
跨平台封装时为什么避免std::shared_ptr管理映射内存
很多人想用std::shared_ptr自动释放映射内存,但这是危险的——shared_ptr析构时调用的是delete或自定义deleter,而UnmapViewOfFile(Windows)和munmap(Linux)根本不是free语义,也不能传给default_delete。
正确做法是写一个RAII类,构造时完成映射,析构时显式调用对应解映射API。如果非要智能指针,必须配自定义Deleter,且该Deleter内部必须区分平台调用不同函数。
- 不要把映射返回的指针直接传给
shared_ptr<void></void>,析构时delete会导致未定义行为 - 映射区域可能包含多个连续结构体,但
shared_ptr只能管理单个对象生命周期,无法表达“一段内存块”的语义 - 多线程访问同一映射区域时,
shared_ptr引用计数本身是线程安全的,但映射内容读写仍需额外同步
映射后指针运算要注意sizeof和字节序
映射成功后拿到的是void*,转成MyStruct*后用ptr[i]访问,看似简单,实则暗藏两层风险:结构体实际内存布局是否与文件中一致?CPU字节序是否匹配?
例如文件里存的是网络字节序(大端)的uint32_t,而x86是小端,直接reinterpret_cast后读ptr->id会得到错误值。同样,结构体如果有#pragma pack(1)或成员对齐设置,映射进来的内容可能因编译器填充而错位。
- 务必确认文件写入时的结构体布局(padding、对齐、字节序)与当前程序完全一致
- 推荐在结构体定义前加
static_assert校验:static_assert(sizeof(MyStruct) == 128, "struct layout mismatch") - 跨平台二进制文件建议用
htons/ntohl系列函数做显式转换,别依赖reinterpret_cast直读 - 映射区域首地址不一定能被
MyStruct*整除对齐,强制转换前最好用alignas或std::aligned_storage确保对齐
PrefetchVirtualMemory或madvise(MADV_WILLNEED)),或者在映射区里做了大量随机跳转访问。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










