C语言逐位读取磁盘:容量计算负值问题及代码正确性咨询
解决DeviceIoControl获取磁盘容量负值及读取磁盘内容的问题
我仔细看了你的代码,发现几个关键问题导致你计算磁盘容量时出现负值,同时代码里还有其他逻辑错误影响磁盘读取,下面逐一说明并给出修正方案:
问题分析
1. 变量打印错误导致逻辑混乱
你在打印磁盘参数的时候犯了低级错误,多次错误输出了t2变量(本该输出t3和t4的地方):
int t3 = dg.SectorsPerTrack; printf("%d\n", t2); // 这里应该是t3 int t4 = dg.BytesPerSector; printf("%d\n", t2); // 这里应该是t4
这不仅会让你误以为获取到的磁盘参数有误,还会干扰后续容量计算的排查。
2. 数据类型溢出与输出格式不匹配
你用unsigned long long存储总容量,但打印时用了%d格式符——%d是针对32位int类型的,当总容量超过int的范围(比如大磁盘的容量),就会出现符号溢出,表现为负值。正确打印unsigned long long应该用%llu格式符。
另外,计算容量时,t1.QuadPart是64位有符号整数,而t2/t3/t4是32位int,直接相乘会先触发32位溢出,再转成64位,导致结果错误。需要先把32位变量强制转换为64位类型再计算。
3. 循环逻辑错误:遍历总字节数而非总扇区数
你的循环变量j遍历的是总字节数,但实际上你需要遍历的是总扇区数——每个循环读取一个扇区,所以循环次数应该是总扇区数,而不是总字节数。原代码的循环会执行数百万次多余的操作,完全不符合预期。
4. 二进制内容输出错误
磁盘内容大多是二进制数据,用printf("%s", sectorData)会因为遇到\0字符就停止输出,而且很多不可打印字符会显示乱码。要正确查看磁盘内容,建议用十六进制格式输出。
修正后的完整代码
#include <windows.h> #include <stdio.h> #include <stdlib.h> int main() { TCHAR cOutputPath0[32000]; char strPath[] = "\\\\.\\PhysicalDrive0"; ExpandEnvironmentStrings((LPCTSTR)strPath, cOutputPath0, sizeof(cOutputPath0)/sizeof(TCHAR)); // 打开磁盘设备,一定要检查返回值 HANDLE drive = CreateFile(cOutputPath0, GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (drive == INVALID_HANDLE_VALUE) { printf("CreateFile failed! Error code: %d\n", GetLastError()); printf("注意:需要管理员权限才能访问物理磁盘\n"); return 1; } DWORD bytesReturned = 0; DISK_GEOMETRY diskGeometry; // 获取磁盘几何信息,检查操作是否成功 BOOL ioResult = DeviceIoControl(drive, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &diskGeometry, sizeof(diskGeometry), &bytesReturned, NULL); if (!ioResult) { printf("DeviceIoControl failed! Error code: %d\n", GetLastError()); CloseHandle(drive); return 1; } // 正确打印磁盘参数 LARGE_INTEGER cylinders = diskGeometry.Cylinders; printf("柱面数: %lld\n", cylinders.QuadPart); int tracksPerCyl = diskGeometry.TracksPerCylinder; printf("每柱面磁道数: %d\n", tracksPerCyl); int sectorsPerTrack = diskGeometry.SectorsPerTrack; printf("每磁道扇区数: %d\n", sectorsPerTrack); int bytesPerSector = diskGeometry.BytesPerSector; printf("每扇区字节数: %d\n", bytesPerSector); // 正确计算总扇区数和总容量,强制转换避免溢出 unsigned long long totalSectors = (unsigned long long)cylinders.QuadPart * tracksPerCyl * sectorsPerTrack; unsigned long long totalBytes = totalSectors * bytesPerSector; printf("总扇区数: %llu\n", totalSectors); printf("总容量(字节): %llu\n", totalBytes); // 分配扇区大小的缓冲区,用unsigned char避免符号问题 unsigned char* sectorBuffer = (unsigned char*)malloc(bytesPerSector); if (!sectorBuffer) { printf("内存分配失败!\n"); CloseHandle(drive); return 1; } // 遍历每个扇区读取并输出 for (unsigned long long sectorIdx = 0; sectorIdx < totalSectors; sectorIdx++) { LARGE_INTEGER seekPos; seekPos.QuadPart = sectorIdx * bytesPerSector; // 计算当前扇区的起始位置 if (!SetFilePointerEx(drive, seekPos, NULL, FILE_BEGIN)) { printf("定位到扇区 %llu 失败!错误码: %d\n", sectorIdx, GetLastError()); break; } DWORD bytesRead = 0; if (ReadFile(drive, sectorBuffer, bytesPerSector, &bytesRead, NULL) && bytesRead == bytesPerSector) { // 十六进制输出扇区内容,每行16字节,方便查看 printf("=== 扇区 %llu 内容 ===\n", sectorIdx); for (int i = 0; i < bytesPerSector; i++) { printf("%02X ", sectorBuffer[i]); if ((i + 1) % 16 == 0) { printf("\n"); } } printf("\n"); } else { printf("读取扇区 %llu 失败!错误码: %d,实际读取字节数: %d\n", sectorIdx, GetLastError(), bytesRead); break; } } // 清理资源 free(sectorBuffer); CloseHandle(drive); return 0; }
额外注意事项
- 管理员权限:访问物理磁盘需要管理员权限,否则
CreateFile会失败,代码里已经添加了相关提示 - 错误检查:所有系统调用(
CreateFile、DeviceIoControl、SetFilePointerEx、ReadFile)都要检查返回值,及时排查问题 - 二进制输出:用十六进制输出磁盘内容是最直观的方式,避免不可打印字符导致的乱码问题
- 数据类型安全:计算容量时强制转换为64位类型,彻底避免32位整数溢出导致的负值问题
内容的提问来源于stack exchange,提问作者Total Anime Immersion
相关产品推荐
相关产品推荐

