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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:05:32