确认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
相关产品推荐
相关产品推荐

