在c++开发中,文件映射是一种高效的文件读写方式,它可以将磁盘文件直接映射到进程的虚拟地址空间,避免频繁的磁盘IO操作。但使用文件映射后,必须正确释放映射的内存,否则会导致内存泄漏甚至程序异常,其中UnmapViewOfFile是释放映射视图的核心函数。

文件映射的基本使用流程
在使用UnmapViewOfFile之前,需要先了解完整的文件映射创建流程,通常分为以下几个步骤:
- 调用CreateFile打开目标文件,获取文件句柄
- 调用CreateFileMapping创建文件映射对象,获取映射对象句柄
- 调用MapViewOfFile将映射对象映射到进程地址空间,获取映射内存的指针
- 使用映射的内存进行读写操作
- 操作完成后调用UnmapViewOfFile释放映射视图,再依次关闭映射对象句柄和文件句柄
UnmapViewOfFile函数说明
UnmapViewOfFile是Windows API提供的函数,用于取消文件映射视图与进程地址空间的映射关系,函数原型如下:
// 函数原型 BOOL UnmapViewOfFile( LPCVOID lpBaseAddress // 映射视图的起始地址,即MapViewOfFile返回的指针 );
函数调用成功返回非零值,失败返回零,可以通过GetLastError获取错误码。需要注意的是,传入的lpBaseAddress必须是之前MapViewOfFile调用成功返回的地址,否则调用会失败。
UnmapViewOfFile安全调用实战
1. 基础安全调用示例
下面是一个完整的文件映射创建、使用、释放的示例代码,展示UnmapViewOfFile的正确调用方式:
#include <windows.h>
#include <iostream>
int main() {
// 1. 打开文件
HANDLE hFile = CreateFile(
L"test.txt", // 文件路径
GENERIC_READ | GENERIC_WRITE, // 读写权限
0, // 不共享
NULL, // 默认安全属性
OPEN_EXISTING, // 文件必须存在
FILE_ATTRIBUTE_NORMAL, // 普通文件属性
NULL // 无模板文件
);
if (hFile == INVALID_HANDLE_VALUE) {
std::cout << "打开文件失败,错误码:" << GetLastError() << std::endl;
return 1;
}
// 2. 创建文件映射对象
HANDLE hMapFile = CreateFileMapping(
hFile, // 文件句柄
NULL, // 默认安全属性
PAGE_READWRITE, // 可读可写
0, // 映射大小高32位,0表示使用文件实际大小
0, // 映射大小低32位,0表示使用文件实际大小
NULL // 映射对象名称,不需要则设为NULL
);
if (hMapFile == NULL) {
std::cout << "创建文件映射对象失败,错误码:" << GetLastError() << std::endl;
CloseHandle(hFile); // 释放文件句柄
return 1;
}
// 3. 映射视图到进程地址空间
LPVOID pMapView = MapViewOfFile(
hMapFile, // 映射对象句柄
FILE_MAP_ALL_ACCESS, // 所有访问权限
0, // 文件偏移高32位
0, // 文件偏移低32位,从文件开头开始映射
0 // 映射字节数,0表示映射整个文件
);
if (pMapView == NULL) {
std::cout << "映射视图失败,错误码:" << GetLastError() << std::endl;
CloseHandle(hMapFile); // 释放映射对象句柄
CloseHandle(hFile); // 释放文件句柄
return 1;
}
// 4. 使用映射的内存,这里示例写入数据
char* pData = (char*)pMapView;
strcpy_s(pData, 100, "这是通过文件映射写入的测试数据");
// 5. 安全释放映射视图
BOOL unmapResult = UnmapViewOfFile(pMapView);
if (unmapResult == 0) {
std::cout << "释放映射视图失败,错误码:" << GetLastError() << std::endl;
} else {
std::cout << "释放映射视图成功" << std::endl;
pMapView = NULL; // 释放后将指针置为NULL,避免野指针
}
// 6. 依次关闭映射对象句柄和文件句柄
CloseHandle(hMapFile);
CloseHandle(hFile);
return 0;
}
2. 常见错误与规避方法
在实际开发中,调用UnmapViewOfFile容易出现以下错误,需要特别注意:
- 重复调用UnmapViewOfFile:同一个映射视图指针只能调用一次UnmapViewOfFile,重复调用会导致未定义行为,建议在调用后将指针置为NULL,后续调用前先判断指针是否为NULL。
- 传入错误的地址:如果传入的地址不是MapViewOfFile返回的地址,或者地址已经被释放过,调用会失败,需要确保传入的地址是有效的映射视图起始地址。
- 忽略返回值:UnmapViewOfFile调用失败后不会抛出异常,需要主动检查返回值,根据错误码排查问题,比如错误码为ERROR_INVALID_ADDRESS表示传入的地址无效。
- 未关闭相关句柄:UnmapViewOfFile只是释放映射视图,还需要依次关闭文件映射对象句柄和文件句柄,否则会导致资源泄漏。
3. 异常处理场景下的安全调用
如果在使用映射内存的过程中可能发生异常,需要确保在异常发生时也能正确释放映射视图,示例代码如下:
#include <windows.h>
#include <iostream>
void SafeFileMappingDemo() {
HANDLE hFile = INVALID_HANDLE_VALUE;
HANDLE hMapFile = NULL;
LPVOID pMapView = NULL;
try {
// 打开文件
hFile = CreateFile(L"test.txt", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
throw GetLastError();
}
// 创建映射对象
hMapFile = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
if (hMapFile == NULL) {
throw GetLastError();
}
// 映射视图
pMapView = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (pMapView == NULL) {
throw GetLastError();
}
// 模拟业务逻辑,可能抛出异常
// ...
// 正常释放
UnmapViewOfFile(pMapView);
pMapView = NULL;
} catch (DWORD errorCode) {
std::cout << "发生异常,错误码:" << errorCode << std::endl;
// 异常时释放资源
if (pMapView != NULL) {
UnmapViewOfFile(pMapView);
pMapView = NULL;
}
}
// 释放句柄
if (hMapFile != NULL) {
CloseHandle(hMapFile);
}
if (hFile != INVALID_HANDLE_VALUE) {
CloseHandle(hFile);
}
}
注意事项
除了正确调用UnmapViewOfFile之外,还需要注意以下几点:
- 如果映射的内存被多个进程共享,一个进程调用UnmapViewOfFile只会释放当前进程的映射视图,不会影响其他进程的映射。
- 调用UnmapViewOfFile之后,之前指向映射内存的指针会变为无效,不能再对其进行读写操作,否则会导致访问违规。
- 如果文件映射对象还有其他的映射视图没有被释放,关闭映射对象句柄不会立即销毁映射对象,直到所有映射视图都被释放。
UnmapViewOfFile文件映射内存释放c_plus_plus修改时间:2026-07-10 19:48:32