这里的用法与将read()直接用于C++ std:vector相同,但具有重新分配的数量.
输入文件的大小未知,因此当文件大小超过缓冲区大小时,通过加倍大小来重新分配缓冲区.这是我的代码:
#include <vector>
#include <fstream>
#include <iostream>
int main()
{
const size_t initSize = 1;
std::vector<char> buf(initSize); // sizes buf to initSize, so &buf[0] below is valid
std::ifstream ifile("D:\\Pictures\\input.jpg", std::ios_base::in|std::ios_base::binary);
if (ifile)
{
size_t bufLen = 0;
for (buf.reserve(1024); !ifile.eof(); buf.reserve(buf.capacity() << 1))
{
std::cout << buf.capacity() << std::endl;
ifile.read(&buf[0] + bufLen, buf.capacity() - bufLen);
bufLen += ifile.gcount();
}
std::ofstream ofile("rebuild.jpg", std::ios_base::out|std::ios_base::binary);
if (ofile)
{
ofile.write(&buf[0], bufLen);
}
}
}
Run Code Online (Sandbox Code Playgroud)
程序按预期打印矢量容量,并将输出文件写入与输入BUT相同的大小,只有与偏移前输入相同的字节initSize,之后全部为零...
使用&buf[bufLen]in read()肯定是一个未定义的行为,但&buf[0] + bufLen得到正确的写入,因为连续分配是有保证的,不是吗?(提供initSize != 0.请注意,std::vector<char> buf(initSize);尺寸buf为initSize.是的,如果initSize == 0,朗姆酒在我的环境中发生致命错误.)我会错过什么吗?这也是UB吗?标准是否说明了std :: vector的这种用法?
PS:是的,我知道我们可以计算出文件的大小第一和分配完全相同的缓冲区大小,但在我的项目,可以预期的是,输入文件几乎总是比某个小SIZE,所以我可以设置initSize到SIZE并期望没有开销(如文件大小计算),并仅为"异常处理"使用重新分配.是的,我知道我可以代替reserve()使用resize(),并capacity()用size(),然后得到的东西用小的开销(零中的每个调整大小的缓冲区)工作,但我还是想摆脱任何redundent操作,只是一种偏执的...
更新1:
事实上,我们可以从&buf[0] + bufLen获得正确信息的标准中逻辑推断,考虑:
std::vector<char> buf(128);
buf.reserve(512);
char* bufPtr0 = &buf[0], *bufPtrOutofRange = &buf[0] + 200;
buf.resize(256); std::cout << "standard guarantees no reallocation" << std::endl;
char* bufPtr1 = &buf[0], *bufInRange = &buf[200];
if (bufPtr0 == bufPtr1)
std::cout << "so bufPtr0 == bufPtr1" << std::endl;
std::cout << "and 200 < buf.size(), standard guarantees bufInRange == bufPtr1 + 200" << std::endl;
if (bufInRange == bufPtrOutofRange)
std::cout << "finally we have: bufInRange == bufPtrOutofRange" << std::endl;
Run Code Online (Sandbox Code Playgroud)
输出:
standard guarantees no reallocation
so bufPtr0 == bufPtr1
and 200 < buf.size(), standard guarantees bufInRange == bufPtr1 + 200
finally we have: bufInRange == bufPtrOutofRange
Run Code Online (Sandbox Code Playgroud)
并且在这里可以用每个buf.size() <= i < buf.capacity()和类似的扣除保持替换200 .
更新2:
是的,我确实错过了一些东西......但问题不在于连续性(参见更新1),甚至没有写入内存失败.今天我有时间研究这个问题,程序得到正确的地址,将正确的数据写入保留的内存,但是接下来reserve(),buf重新分配并且仅将范围内的元素[0, buf.size())复制到新的内存中.所以这就是整个谜语的答案......
最后注意事项:如果在缓冲区填充了一些数据后无需重新分配,则可以使用reserve()/capatity()而不是resize()/size(),但如果需要,可以使用后者.
例:
const size_t initSize = 32;
std::vector<char> buf(initSize);
buf.reserve(1024*100); // reserve enough space for file reading
std::ifstream ifile("D:\\Pictures\\input.jpg", std::ios_base::in|std::ios_base::binary);
if (ifile)
{
ifile.read(&buf[0], buf.capacity()); // ok. the whole file is read into buf
std::ofstream ofile("rebuld.jpg", std::ios_base::out|std::ios_base::binary);
if (ofile)
{
ofile.write(&buf[0], ifile.gcount()); // rebuld.jpg just identical to input.jpg
}
}
buf.reserve(1024*200); // horror! probably always lose all data in buf after offset initSize
Run Code Online (Sandbox Code Playgroud)
PS:我没有发现任何权威来源(标准,TC++ PL等)明确同意或不同意我提出的上述建议.但是在这里可用的所有实现(VC++,g ++,ICC)下,上面的例子工作正常.
这是另一个例子,引自'TC++ PL,4e'第1041页,注意函数的第一行使用reserve()而不是resize():
void fill(istream& in, string& s, int max)
// use s as target for low-level input (simplified)
{
s.reserve(max); // make sure there is enough allocated space
in.read(&s[0],max);
const int n = in.gcount(); // number of characters read
s.resize(n);
s.shrink_to_fit(); // discard excess capacity
}
Run Code Online (Sandbox Code Playgroud)
reserve实际上并没有将空间添加到向量中,它只是确保在调整向量大小时不需要重新分配。而不是使用reserve你应该使用resizeresize,然后在知道实际读入的字节数后执行最终操作。
保证reserve做的就是防止当您将向量的大小增加到 时迭代器和指针失效capacity()。它不是保留这些保留字节的内容,除非它们是size().
例如,使用“调试”标志构建的代码通常会包含额外的功能,以便更轻松地查找错误。也许新分配的内存将填充有明确定义的模式。也许该类会定期扫描该内存以查看它是否已更改,如果它假设只有错误可能导致该更改,则抛出异常。这样的实现仍然符合标准。
的例子std::string就更好了,因为有一个案例几乎肯定会失败。 string::c_str()将返回一个指向末尾带有空终止符的字符串的指针。现在,符合要求的实现可以为终止 null 分配第二个缓冲区,并在复制字符串后返回该指针,但这将非常浪费。更有可能的是,字符串类将确保其保留的缓冲区有空间容纳额外的空字符,并根据需要在那里写入空值。但标准并没有规定何时写入 null,它可能是在调用中c_str,也可能在字符串可能被修改的任何点。因此,您无法知道某个字节何时将被覆盖。
如果您确实想要一个未初始化字节的缓冲区,std::vector<char>那么无论如何这可能是错误的工具。您应该查看智能指针,例如std::unique_ptr<char>。
| 归档时间: |
|
| 查看次数: |
5584 次 |
| 最近记录: |