在 C++ 中通过迭代器将向量写入二进制文件的正确方法

Posted

技术标签:

【中文标题】在 C++ 中通过迭代器将向量写入二进制文件的正确方法【英文标题】:The right way to write a vector to a binary file by iterator in C++ 【发布时间】:2021-08-24 19:35:06 【问题描述】:

我已经搜索了很多此类问题,但没有得到满意的答案。

我正在研究使用 C++ 中的 ofstream::write 方法将矢量容器写入二进制文件的函数。

#include <vector>
#include <fstream>

typedef std::vector<unsigned char> byteArray;

byteArray data;
data.push_back('a');
data.push_back('b');
data.push_back('c');
data.push_back('d');

std::ofstream fout("out_file", std::ios::out | std::ios::trunc | std::ios::binary);

// I know the right way should be as following:
fout.write((char *)&data[0], data.size());

// But what I cannot understand is why the following expression is wrong, since gcc compiler says
// invalid cast from type ‘__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char> >’ to type ‘char*’

for(auto iter = data.begin(); iter != data.end(); iter++)

    fout.write((char *)iter, 1);


fout.close();

据我了解,迭代器 iter 是一个指针,它指向向量“数据”中的一个元素。所以我认为(char *) iter会将底层数据类型重新解释为char,那么它应该满足fout.write的参数要求。但实际上,情况并非如此。我也很困惑 gcc 说 iter 是一种 iterator,对吧? iter 不应该是 unsigned char * 类型吗?

我怎么了?非常感谢您的帮助。

【问题讨论】:

“据我了解,iterator iter 是一个指针”。否。std::vector::iterator 是未指定的类型。如果需要指针,则需要&amp;*iter 不要对类型做出假设。即使您理解正确,只要行为保持不变,实际类型可能会随着时间而改变。这就是为什么C-Cast 如此危险,它会因为假设而隐藏错误。 我不会假设数组总是每个字符包含 1 个字节。如果有人更改了数组的类型并忘记更新写入,它将默默地中断。 fout.write((char *)&amp;data[0], data.size() * size_of(byteArray[0]); Martin York 是对的,我应该在 fout.write 函数中添加 size_of(byteArray[0])。万分感谢。 :) 对于 C++,我应该还有很长的路要走。 【参考方案1】:

据我了解,iterator iter 是一个指针

事实并非如此。 std::vector::iterator 是未指定的类型。

您可以期望它具有一些类似指针的属性,但您不应假设它可能是什么特定类型。

因为您需要一个指针,所以取消对迭代器的引用并获取该结果的地址。那是一个指针。

fout.write((char *)&*iter, 1);

更好的是,使用 C++ 转换而不是难以跟踪的 C 转换。

fout.write( static_cast<char*>(&*iter), 1 );

甚至更安全(Martin York 始终是一颗珍贵的宝石)——不要假设包含类型的大小。未来可能会改变。此更改不会增加任何开销。

fout.write( static_cast<char*>(&*iter), size_of(byteArray[0]) );

【讨论】:

最好不要假设迭代器指向一个字符。 这很好用!似乎我仍然太天真,无法理所当然地将迭代器作为指针。 :-) "取消引用迭代器并获取该结果的地址" - 请注意,如果类型重载 &amp; 运算符,则必须使用 std::addressof() 来获取地址,例如:std::addressof(*iter) @RemyLebeau 总。但是是真的。但是很恶心。【参考方案2】:

指针可以用作迭代器,但迭代器不必是指针。

通过提供取消引用operator*()、递增operator++() 等操作,迭代器的行为或多或少类似于指针,参见例如要求LegacyInputIterator

虽然具体定义会随着时间而变化,但请参阅 std::input_or_output_iterator 了解 C++20 中的定义。

【讨论】:

以上是关于在 C++ 中通过迭代器将向量写入二进制文件的正确方法的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C++ 中通过引用返回向量

在 C++ 中通过块异步保存大型二进制文件的最佳方法

如何访问在 C++ 中通过引用传递的列表/向量的元素

在 C++ 中通过引用与按值将向量传递给函数

在 C++ 中通过网格/矩阵找到成本优化路径

如何仅使用 C++ 中的迭代器正确迭代 3D 向量? [关闭]