当我一起调用这两个函数时,为什么我的 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?