【问题标题】:Why my C++ program is not running correctly when I call these two function together?当我同时调用这两个函数时,为什么我的 C++ 程序运行不正常?
【发布时间】:2021-06-26 07:48:30
【问题描述】:

所以我创建了这个程序来将整数向量写入二进制文件,然后再次检索数据。

//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() 函数,然后我运行它,它再次按预期运行

int main()
{
    std::vector<int> mykeys = readKey<int>("test.key");

    for(auto e: mykeys)
        std::cout<<e<<' ';
    std::cout<<'\n';

    return 0;
}

当我在 main() 函数中调用这两个函数时出现问题,然后在此处编译并运行它 readkey 函数中的限制变量具有某种溢出值,而不是我在开头插入的值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

【问题讨论】:

  • 好像是size_t的限制;当我在 main() 中将这两个函数一起调用时是不对的,为什么会这样?
  • write_bin.write((char *) &amp;arr[i], sizeof(int)); 为什么使用sizeof(int) 而不是sizeof(T)?另外为什么要通过副本而不是通过 const ref 传递向量?另外最好不要使用 c 风格的演员表;请改用static_cast/reinterpret_cast
  • 还有一个“小问题”,您正在向文件写入(签名)int 并将此数据读取到 size_t,这可能使用不同的数字除了无符号之外的字节数。让我们考虑int使用32位和size_t使用64位的情况:由于size_t limit没有初始化,它可能包含任意数据,但是你只在读取文件时覆盖数据的前32位;此外,即使您在大端机器上将 limit 初始化为 0,您也只是将值乘以 2^32...
  • @fabian 如您所见,我在向量的开头插入了一个元素,这就是为什么我传递了它的副本,因为我不想更改原始向量,将 (int) 更改为(T) 也没有帮助,static_cast 和 reinterpret_cast 也没有解决,我也尝试过使用 unsigned long 作为向量类型,但是当我在 main() 函数中同时调用这两个函数时仍然出现相同的错误
  • “它抛出一个错误”什么错误?

标签: c++ fstream binaryfiles stdvector


【解决方案1】:

正如@fabian 在 cmets 中已经提到的,当您的 arr.size() 实际上来自 std::size_t 类型时,您将其视为 int (T)。问题是 std::size_t 长 8 字节(如果您在 64 位机器上运行程序)并且 int 只有 4 字节。

首先,您将 std::size_t 转换为 arr.insert(arr.begin(),(T)arr.size()); 行中的 int ,因此它“适合”到您的向量中。在执行此操作时,您有效地摆脱了一半的字节。然后,当您将向量写入文件时,您写入了 4 个字节而不是所需的 8 个字节。 现在,如果您使用read_bin.read((char*)&amp;limit, sizeof(T)); 读回您的值,那么您正在读取 4 个字节到限制,现在它的类型为 std::size_t,即再次由 8 个字节组成,因此只有限制的前 4 个字节被更改,其余的是原封不动。在 VS 中以调试模式运行此代码,您可以检查会发生什么(更改的字节标记为红色):

只有前 4 个字节发生变化,其余的保持不变。因为我在调试模式下运行它,所以其余设置为cc,因此限制的值变为 14757395255531667460。这个值对于向量来说太大了,所以抛出了 std::length_error。如果你在发布模式下运行它,你会得到未定义的行为,因为你不知道在限制变量之前的位置上有什么字节。也许一切运行正常(因为所有字节均为 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 传递给函数并通过单个写入/读取调用删除 for 循环。另请参阅When should static_cast, dynamic_cast, const_cast and reinterpret_cast be used?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-29
    • 2019-11-13
    • 2017-03-21
    • 2019-06-08
    相关资源
    最近更新 更多