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

确认std::fseek配合std::fwrite/fread替代pread/pwrite的逻辑正确性

问题与验证思路

我需要将一段C代码转换为C++,要求尽可能移除原生C调用,替换为C++ STL函数。其中需将pread和pwrite分别替换为std::fread和std::fwrite,但原代码大量依赖pread/pwrite的offset参数(用于操作Linux或BSD系统的/dev/sda1这类设备)。由于std::fread/std::fwrite没有offset参数,我认为需要先通过std::fseek定位到指定偏移,再执行读写操作。我已编写测试程序验证该逻辑,但仍需确认此理解是否正确。

测试程序编译命令:clang++ -std=c++17 main.cpp

测试代码

#include <vector>
#include <string>

#include <ctime>
#include <algorithm>

#include <cstdio>

void write(const std::string& device, const size_t offset, const std::vector<uint8_t>& data)
{

    std::FILE* fp = std::fopen(device.c_str(), "wb");

    if (offset != 0)
    {
        std::fseek(fp, 0, offset);
    }
    
    std::fwrite(data.data(), sizeof(uint8_t), data.size(), fp);
    std::fclose(fp);
}

std::vector<uint8_t> read(const std::string& device, size_t offset, const size_t size)
{
    std::vector<uint8_t> data(size);

    std::FILE* fp = std::fopen(device.c_str(), "rb");

    if (offset != 0)
    {
        std::fseek(fp, 0, offset);
    }

    std::fread(data.data(), sizeof(uint8_t), size, fp);

    std::fclose(fp);
    return data;
}


std::vector<std::vector<uint8_t>> generate(const size_t num_vec, const size_t data_size)
{
    std::vector<std::vector<uint8_t>> vectors;

    for (size_t i = 0; i < num_vec; ++i)
    {
        std::vector<uint8_t> data(data_size);
        std::generate(data.begin(), data.end(), rand);
        vectors.push_back(data);
    }
    return vectors;
}

int main(void)
{
    srand(static_cast<uint32_t>(time(0)));
    
    const std::string device = "/dev/sda1";
    const size_t size = 4096;
    const size_t num_vec = 4;

    auto vectors = generate(num_vec, size);

    size_t offset = 0;
    
    for (const auto& vec : vectors)
    {
        write(device, offset, vec);
        auto other = read(device, offset, size);

        if (vec != other)
        {
            std::puts("success");
        }
        else
        {
            std::puts("failure");
        }

        offset = offset + size;
    }    
}
结论与代码修正

你的核心思路是正确的:用std::fseek先定位到目标偏移,再执行std::fread/std::fwrite,确实可以替代pread/pwrite的offset功能,但测试代码存在多个关键问题,需要修正:

  • fseek参数错误:std::fseek的参数顺序是(文件指针, 偏移量, 起始位置),你写的std::fseek(fp, 0, offset)完全错误,正确写法是std::fseek(fp, offset, SEEK_SET),无论offset是否为0都应该显式调用,确保定位准确。
  • 文件打开模式错误:操作块设备时,"wb"模式会尝试截断设备文件,这在Linux/BSD下是不允许的,会导致打开失败。写操作应改用"rb+"或"wb+"模式;同时频繁打开/关闭设备文件效率极低,建议保持文件句柄持续打开。
  • 缺失错误处理:未检查fopen、fseek、fread、fwrite的返回值,一旦操作失败(比如权限不足、设备不存在),程序会出现异常行为。
  • 逻辑判断颠倒:main函数中vec != other时输出"success"完全搞反,应该是vec == other时表示读写一致,输出"success"。

修正后的示例代码

#include <vector>
#include <string>
#include <ctime>
#include <algorithm>
#include <cstdio>
#include <cstdlib>

bool write(const std::string& device, size_t offset, const std::vector<uint8_t>& data)
{
    // 用rb+模式打开,支持读写且不截断设备
    std::FILE* fp = std::fopen(device.c_str(), "rb+");
    if (!fp) {
        perror("Failed to open device for writing");
        return false;
    }

    // 定位到指定偏移,SEEK_SET表示从文件起始位置计算
    if (std::fseek(fp, offset, SEEK_SET) != 0) {
        perror("Failed to seek to offset");
        std::fclose(fp);
        return false;
    }

    // 检查实际写入的字节数
    size_t written = std::fwrite(data.data(), sizeof(uint8_t), data.size(), fp);
    if (written != data.size()) {
        perror("Failed to write all data");
        std::fclose(fp);
        return false;
    }

    // 刷新缓冲区,确保数据写入设备
    std::fflush(fp);
    std::fclose(fp);
    return true;
}

std::vector<uint8_t> read(const std::string& device, size_t offset, size_t size, bool* success = nullptr)
{
    std::vector<uint8_t> data(size, 0);
    std::FILE* fp = std::fopen(device.c_str(), "rb");
    if (!fp) {
        perror("Failed to open device for reading");
        if (success) *success = false;
        return data;
    }

    if (std::fseek(fp, offset, SEEK_SET) != 0) {
        perror("Failed to seek to offset");
        std::fclose(fp);
        if (success) *success = false;
        return data;
    }

    size_t read_bytes = std::fread(data.data(), sizeof(uint8_t), size, fp);
    if (read_bytes != size) {
        perror("Failed to read all data");
        if (success) *success = false;
    } else if (success) {
        *success = true;
    }

    std::fclose(fp);
    return data;
}

std::vector<std::vector<uint8_t>> generate(size_t num_vec, size_t data_size)
{
    std::vector<std::vector<uint8_t>> vectors;
    vectors.reserve(num_vec); // 预分配空间提升效率

    for (size_t i = 0; i < num_vec; ++i) {
        std::vector<uint8_t> data(data_size);
        std::generate(data.begin(), data.end(), rand);
        vectors.push_back(std::move(data));
    }
    return vectors;
}

int main(void)
{
    srand(static_cast<uint32_t>(time(nullptr)));
    
    const std::string device = "/dev/sda1";
    const size_t size = 4096;
    const size_t num_vec = 4;

    auto vectors = generate(num_vec, size);
    size_t offset = 0;
    
    for (const auto& vec : vectors) {
        bool write_ok = write(device, offset, vec);
        if (!write_ok) {
            std::puts("Write failed");
            offset += size;
            continue;
        }

        bool read_ok = false;
        auto other = read(device, offset, size, &read_ok);
        if (!read_ok) {
            std::puts("Read failed");
            offset += size;
            continue;
        }

        if (vec == other) {
            std::puts("success");
        } else {
            std::puts("failure");
        }

        offset += size;
    }    

    return 0;
}

内容的提问来源于stack exchange,提问作者Lars Kakavandi-Nielsen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 04:00:46