当我一起调用这两个函数时,为什么我的 C++ 程序不正确 运行?
Why my C++ program is not running correctly when I call these two function together?
所以我创建了这个程序来将整数向量写入二进制文件,然后再次检索数据。
//vec.cpp
#include <iostream>
#include <vector>
#include <fstream>
template<typename T>
void writeKey(std::string filename, std::vector<T> arr)
{
arr.insert(arr.begin(),(T)arr.size());
std::ofstream write_bin(filename, std::ios::out | std::ios::binary);
if(!write_bin)
{
std::cout << "ERROR: writing vector to file!\n";
exit(1);
}
for(size_t i = 0; i < arr.size(); i++)
{
write_bin.write((char *) &arr[i], sizeof(int));
}
write_bin.close();
if(!write_bin.good())
{
std::cout<<"ERROR: writing time error\n";
exit(1);
}
}
template<typename T>
std::vector<T> readKey(std::string filename)
{
std::ifstream read_bin(filename, std::ios::out | std::ios::binary);
if(!read_bin)
{
std::cout<<"ERROR: reading binary file!\n";
exit(1);
}
size_t limit;
read_bin.read((char*)&limit, sizeof(T));
std::cout<<"limit : "<<limit<<'\n';
std::vector<T> arr(limit,0);
for(size_t i=0; i<limit; ++i)
{
read_bin.read((char*)&arr[i], sizeof(T));
}
read_bin.close();
return arr;
}
int main()
{
std::vector<int> mykey = {5,10,15,20};
writeKey("test.key", mykey);
std::vector<int> mykeys = readKey<int>("test.key");
for(auto e: mykeys)
std::cout<<e<<' ';
std::cout<<'\n';
return 0;
}
所以你看我在这里做的是我编译只调用 writeKey() 函数的程序然后 运行 它...程序它 运行 完美
int main()
{
std::vector<int> mykey = {5,10,15,20};
writeKey("test.key", mykey);
return 0;
}
然后我再次编译它,但这次我只调用了 readkey() 函数,然后我 运行 它,然后再次 运行s 按预期
int main()
{
std::vector<int> mykeys = readKey<int>("test.key");
for(auto e: mykeys)
std::cout<<e<<' ';
std::cout<<'\n';
return 0;
}
当我在 main() 函数中调用这两个函数,然后编译并 运行 readkey 函数中的 limit 变量具有某种溢出值而不是我在 writekey 函数的向量开头插入
int main()
{
std::vector<int> mykey = {5,10,15,20};
writeKey("test.key", mykey);
std::vector<int> mykeys = readKey<int>("test.key");
for(auto e: mykeys)
std::cout<<e<<' ';
std::cout<<'\n';
return 0;
}
这里发生了什么?我该如何解决这个问题?
这是我的编译标志:g++ -o vec.o vec.cpp -Wall -Wextra -fsanitize=address
正如@fabian 的评论中已经提到的,您将 arr.size()
视为 int (T),而实际上它来自 std::size_t 类型。问题是 std::size_t 有 8 个字节长(如果你 运行 在 64 位机器上运行你的程序)而 int 只有 4 个字节。
首先,您要将 std::size_t 转换为 arr.insert(arr.begin(),(T)arr.size());
行中的一个整数,以便它“适合”您的向量。执行此操作时,您可以有效地摆脱一半的字节。然后,当您将向量写入文件时,您写入了 4 个字节而不是所需的 8 个字节。
现在,如果您使用 read_bin.read((char*)&limit, sizeof(T));
读回您的值,那么您正在读取 4 个字节到限制,现在具有类型 std::size_t,即再次由 8 个字节组成,因此只有您的限制的前 4 个字节被更改其余的保持不变。 运行 调试模式下 VS 中的这段代码您可以检查发生了什么(更改的字节标记为红色):
只有前 4 个字节发生变化,其余的保持不变。因为我在调试模式下 运行 其余设置为 cc
所以限制值变为 14757395255531667460。这个值对于向量来说太大了,所以 std::length_error 被抛出。如果你在发布模式下 运行 这样做,你会得到未定义的行为,因为你不知道在 limit 变量之前的位置上有哪些字节。也许一切都 运行 没问题(因为所有字节都是 0)但是你可能会得到错误的限制值。
要解决此问题,只需将 arr.size()
视为原样,一个 std::size_t 并且不要将其存储在向量的第一个位置(只能容纳 T),而只需编写在写入值并将 sizeof(int)
替换为 sizeof(T)
之前将其写入文件(这仅适用于此示例),因此您的写入函数变为:
template<typename T>
void writeKey(const std::string& filename, const std::vector<T>& arr) {
std::ofstream write_bin(filename, std::ios::out | std::ios::binary);
if (!write_bin) {
std::cout << "ERROR: writing vector to file!\n";
exit(1);
}
std::size_t limit = arr.size();
write_bin.write(reinterpret_cast<char*>(&limit), sizeof(std::size_t));
if (limit != 0)
write_bin.write(reinterpret_cast<const char*>(&arr[0]), sizeof(T) * limit);
write_bin.close();
if (!write_bin.good()) {
std::cout << "ERROR: writing time error\n";
exit(1);
}
}
现在,您只需在读取所有其他值之前先读取您的限制,这样您的读取函数就变成了:
template<typename T>
std::vector<T> readKey(const std::string& filename) {
std::ifstream read_bin(filename, std::ios::out | std::ios::binary);
if (!read_bin) {
std::cout << "ERROR: reading binary file!\n";
exit(1);
}
std::size_t limit;
read_bin.read(reinterpret_cast<char*>(&limit), sizeof(std::size_t));
std::vector<T> arr(limit, 0);
if (limit != 0)
read_bin.read(reinterpret_cast<char*>(&arr[0]), sizeof(T) * limit);
read_bin.close();
std::cout << "limit : " << limit << '\n';
return arr;
}
我改变了一些其他的东西,比如传递 const references to the functions and removing the for-loops with a single write/read call. Also, see When should static_cast, dynamic_cast, const_cast and reinterpret_cast be used?。
所以我创建了这个程序来将整数向量写入二进制文件,然后再次检索数据。
//vec.cpp
#include <iostream>
#include <vector>
#include <fstream>
template<typename T>
void writeKey(std::string filename, std::vector<T> arr)
{
arr.insert(arr.begin(),(T)arr.size());
std::ofstream write_bin(filename, std::ios::out | std::ios::binary);
if(!write_bin)
{
std::cout << "ERROR: writing vector to file!\n";
exit(1);
}
for(size_t i = 0; i < arr.size(); i++)
{
write_bin.write((char *) &arr[i], sizeof(int));
}
write_bin.close();
if(!write_bin.good())
{
std::cout<<"ERROR: writing time error\n";
exit(1);
}
}
template<typename T>
std::vector<T> readKey(std::string filename)
{
std::ifstream read_bin(filename, std::ios::out | std::ios::binary);
if(!read_bin)
{
std::cout<<"ERROR: reading binary file!\n";
exit(1);
}
size_t limit;
read_bin.read((char*)&limit, sizeof(T));
std::cout<<"limit : "<<limit<<'\n';
std::vector<T> arr(limit,0);
for(size_t i=0; i<limit; ++i)
{
read_bin.read((char*)&arr[i], sizeof(T));
}
read_bin.close();
return arr;
}
int main()
{
std::vector<int> mykey = {5,10,15,20};
writeKey("test.key", mykey);
std::vector<int> mykeys = readKey<int>("test.key");
for(auto e: mykeys)
std::cout<<e<<' ';
std::cout<<'\n';
return 0;
}
所以你看我在这里做的是我编译只调用 writeKey() 函数的程序然后 运行 它...程序它 运行 完美
int main()
{
std::vector<int> mykey = {5,10,15,20};
writeKey("test.key", mykey);
return 0;
}
然后我再次编译它,但这次我只调用了 readkey() 函数,然后我 运行 它,然后再次 运行s 按预期
int main()
{
std::vector<int> mykeys = readKey<int>("test.key");
for(auto e: mykeys)
std::cout<<e<<' ';
std::cout<<'\n';
return 0;
}
当我在 main() 函数中调用这两个函数,然后编译并 运行 readkey 函数中的 limit 变量具有某种溢出值而不是我在 writekey 函数的向量开头插入
int main()
{
std::vector<int> mykey = {5,10,15,20};
writeKey("test.key", mykey);
std::vector<int> mykeys = readKey<int>("test.key");
for(auto e: mykeys)
std::cout<<e<<' ';
std::cout<<'\n';
return 0;
}
这里发生了什么?我该如何解决这个问题?
这是我的编译标志:g++ -o vec.o vec.cpp -Wall -Wextra -fsanitize=address
正如@fabian 的评论中已经提到的,您将 arr.size()
视为 int (T),而实际上它来自 std::size_t 类型。问题是 std::size_t 有 8 个字节长(如果你 运行 在 64 位机器上运行你的程序)而 int 只有 4 个字节。
首先,您要将 std::size_t 转换为 arr.insert(arr.begin(),(T)arr.size());
行中的一个整数,以便它“适合”您的向量。执行此操作时,您可以有效地摆脱一半的字节。然后,当您将向量写入文件时,您写入了 4 个字节而不是所需的 8 个字节。
现在,如果您使用 read_bin.read((char*)&limit, sizeof(T));
读回您的值,那么您正在读取 4 个字节到限制,现在具有类型 std::size_t,即再次由 8 个字节组成,因此只有您的限制的前 4 个字节被更改其余的保持不变。 运行 调试模式下 VS 中的这段代码您可以检查发生了什么(更改的字节标记为红色):
只有前 4 个字节发生变化,其余的保持不变。因为我在调试模式下 运行 其余设置为 cc
所以限制值变为 14757395255531667460。这个值对于向量来说太大了,所以 std::length_error 被抛出。如果你在发布模式下 运行 这样做,你会得到未定义的行为,因为你不知道在 limit 变量之前的位置上有哪些字节。也许一切都 运行 没问题(因为所有字节都是 0)但是你可能会得到错误的限制值。
要解决此问题,只需将 arr.size()
视为原样,一个 std::size_t 并且不要将其存储在向量的第一个位置(只能容纳 T),而只需编写在写入值并将 sizeof(int)
替换为 sizeof(T)
之前将其写入文件(这仅适用于此示例),因此您的写入函数变为:
template<typename T>
void writeKey(const std::string& filename, const std::vector<T>& arr) {
std::ofstream write_bin(filename, std::ios::out | std::ios::binary);
if (!write_bin) {
std::cout << "ERROR: writing vector to file!\n";
exit(1);
}
std::size_t limit = arr.size();
write_bin.write(reinterpret_cast<char*>(&limit), sizeof(std::size_t));
if (limit != 0)
write_bin.write(reinterpret_cast<const char*>(&arr[0]), sizeof(T) * limit);
write_bin.close();
if (!write_bin.good()) {
std::cout << "ERROR: writing time error\n";
exit(1);
}
}
现在,您只需在读取所有其他值之前先读取您的限制,这样您的读取函数就变成了:
template<typename T>
std::vector<T> readKey(const std::string& filename) {
std::ifstream read_bin(filename, std::ios::out | std::ios::binary);
if (!read_bin) {
std::cout << "ERROR: reading binary file!\n";
exit(1);
}
std::size_t limit;
read_bin.read(reinterpret_cast<char*>(&limit), sizeof(std::size_t));
std::vector<T> arr(limit, 0);
if (limit != 0)
read_bin.read(reinterpret_cast<char*>(&arr[0]), sizeof(T) * limit);
read_bin.close();
std::cout << "limit : " << limit << '\n';
return arr;
}
我改变了一些其他的东西,比如传递 const references to the functions and removing the for-loops with a single write/read call. Also, see When should static_cast, dynamic_cast, const_cast and reinterpret_cast be used?。