You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++ std::list对象二进制存读问题及编译错误排查

自定义Order列表的二进制序列化问题与解决方案

问题背景

尝试将存储自定义Order类对象的std::list写入二进制文件并读取,最初直接读写std::list的内存数据,加载后遍历列表触发segmentation fault(段错误);改为逐字段序列化后,编译时出现std::string.data()无法转换为char*的错误。

原始Order类代码

class Order{
private:
    string name;
    string address;
    int number_of_tickets;
public:
    bool operator == (const Order& o) const {
        return name == o.getName() && address == o.getAddress() && number_of_tickets == o.getTickets();
    }

    bool operator != (const Order& o) const {
        return !operator==(o);
    }

    Order(string n, string a, int num){
        this->name = n;
        this->address = a;
        this->number_of_tickets = num;
    }

    Order copy() const{
        return Order(this->name, this->address, this->number_of_tickets);
    }

    void display() const{
        cout << getName() << " " << getAddress() << " " << getTickets() << endl;
    }
    string getName() const{
        return name;
    }
    string getAddress() const{
        return address;
    }
    int getTickets() const{
        return number_of_tickets;
    }
};

最初的实现与运行错误

直接读写std::list内存的save/load函数:

void save(list<Order>& l){
    fstream save;
    save.open("orders.list", ios::out | ios::binary);
    long size = sizeof(l);
    save.write(reinterpret_cast<char*>(&size), sizeof(size));
    cout << endl << "Size is = " << size << endl; 
    save.write(reinterpret_cast<char*>(&l), size);
    cout << "Orders Saved!\n";
}
void load(list<Order>& l){
    fstream load;
    load.open("orders.list", ios::in | ios::binary);
    long size;
    load.read(reinterpret_cast<char*>(&size), sizeof(size));
    cout << endl << "Size is = " << size << endl; 
    load.read(reinterpret_cast<char*>(&l), size);
    cout << "Orders Loaded!\n";
}

错误表现

程序运行时可正常显示列表,但加载保存的数据后调用displayAll遍历列表时触发segmentation fault。

修改后的代码与编译错误

改为逐字段序列化的save/load函数:

void save(list<Order>& l){
    fstream save;
    save.open("orders.list", ios::out | ios::binary);
    int size = l.size();
    save.write(reinterpret_cast<char*>(&size), sizeof(size));

    for(const auto& order : l){
        string name = order.getName();
        string::size_type size_name = name.length();
        save.write(reinterpret_cast<char*>(&size_name), sizeof(size_name));
        save.write(name.data(), size_name);

        string address = order.getAddress();
        string::size_type size_address = address.length();
        save.write(reinterpret_cast<char*>(&size_address), sizeof(size_address));
        save.write(address.data(), size_address);

        int tickets = order.getTickets();
        save.write(reinterpret_cast<char*>(&tickets), sizeof(tickets));
    }
}
void load(list<Order>& l){
    ifstream load;
    load.open("orders.list", ios::binary);
    int size;
    load.read(reinterpret_cast<char*>(&size), sizeof(size));

    for(int i = 0; i < size; i++){
        string name;
        string::size_type size_name;
        load.read(reinterpret_cast<char*>(&size_name), sizeof(size_name));
        name.resize(size_name);
        load.read(name.data(), size_name);

        string address;
        string::size_type size_address;
        load.read(reinterpret_cast<char*>(&size_address), sizeof(size_address));
        address.resize(size_address);
        load.read(address.data(), size_address);

        int tickets;
        load.read(reinterpret_cast<char*>(&tickets), sizeof(tickets));

        l.push_back(Order(name, address, tickets));
    }
}

编译错误信息

mechy88@LAPTOP-SJIPE7SP:/mnt/c/Users/Miguel/Desktop/ADNU-CS-repo/CSDC102_E3$ g++ PAJARILLO_E3.cpp
PAJARILLO_E3.cpp: In function ‘void load(std::__cxx11::list&)’:
PAJARILLO_E3.cpp:215:28: error: invalid conversion from ‘const char*’ to ‘std::basic_istream::char_type*’ {aka ‘char*’} [-fpermissive]
215 |         load.read(name.data(), size_name);
|                   ~~~~~~~~~^~
|                            |
|                            const char*
In file included from /usr/include/c++/9/iostream:40,
from PAJARILLO_E3.cpp:1:
/usr/include/c++/9/istream:486:23: note:   initializing argument 1 of ‘std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::read(std::basic_istream<_CharT, _Traits>::char_type*, std::streamsize) [with _CharT = char; _Traits = std::char_traits; std::basic_istream<_CharT, _Traits>::char_type = char; std::streamsize = long int]’
486 |       read(char_type* __s, streamsize __n);
|            ~~~~~~~~~~~^~~
PAJARILLO_E3.cpp:221:31: error: invalid conversion from ‘const char*’ to ‘std::basic_istream::char_type*’ {aka ‘char*’} [-fpermissive]
221 |         load.read(address.data(), size_address);
|                   ~~~~~~~~~~~~^~
|                               |
|                               const char*
In file included from /usr/include/c++/9/iostream:40,
from PAJARILLO_E3.cpp:1:
/usr/include/c++/9/istream:486:23: note:   initializing argument 1 of ‘std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::read(std::basic_istream<_CharT, _Traits>::char_type*, std::streamsize) [with _CharT = char; _Traits = std::char_traits; std::basic_istream<_CharT, _Traits>::char_type = char; std::streamsize = long int]’
486 |       read(char_type* __s, streamsize __n);
|            ~~~~~~~~~~~^~~

解决方案

1. 最初段错误的原因

std::list是链表容器,内部存储的是节点指针、链表头尾指针等结构,直接读写容器的内存数据,保存的是当前进程中的内存地址。加载到新进程后,这些地址已经无效,访问时就会触发segmentation fault。正确的做法是逐对象、逐字段进行序列化/反序列化。

2. 编译错误的解决

C++17之前,std::string::data()返回的是const char*,而istream::read()要求传入可写的char*。有两种安全的解决方式:

方式一:使用const_cast转换

因为已经通过name.resize(size_name)为字符串分配了足够的可写内存,这里用const_cast去除const属性是安全的:

load.read(const_cast<char*>(name.data()), size_name);
load.read(const_cast<char*>(address.data()), size_address);

方式二:取字符串首元素的地址

字符串resize后,内部的字符数组是连续且可写的,直接取首元素的地址即可得到char*:

load.read(&name[0], size_name);
load.read(&address[0], size_address);

方式三:升级到C++17及以上

C++17为std::string::data()提供了非const重载,直接调用即可返回char*,但需要确保编译器支持(编译时加-std=c++17参数)。

3. 额外优化建议

  • 检查文件状态:打开文件后要检查是否成功,避免后续操作无意义执行:
    if (!save.is_open()) {
        cerr << "Failed to open save file\n";
        return;
    }
    
  • 检查流状态:读取数据后检查流是否正常,避免因文件损坏或读取不完整导致错误:
    if (!load.read(reinterpret_cast<char*>(&size_name), sizeof(size_name))) {
        cerr << "Failed to read name length\n";
        break;
    }
    
  • 封装序列化逻辑:把序列化/反序列化代码封装到Order类的成员函数中,代码更整洁:
    class Order {
        // ... 现有代码 ...
        void serialize(ostream& os) const {
            string::size_type name_len = name.length();
            os.write(reinterpret_cast<const char*>(&name_len), sizeof(name_len));
            os.write(name.data(), name_len);
            
            string::size_type addr_len = address.length();
            os.write(reinterpret_cast<const char*>(&addr_len), sizeof(addr_len));
            os.write(address.data(), addr_len);
            
            os.write(reinterpret_cast<const char*>(&number_of_tickets), sizeof(number_of_tickets));
        }
        void deserialize(istream& is) {
            string::size_type name_len;
            is.read(reinterpret_cast<char*>(&name_len), sizeof(name_len));
            name.resize(name_len);
            is.read(const_cast<char*>(name.data()), name_len);
            
            string::size_type addr_len;
            is.read(reinterpret_cast<char*>(&addr_len), sizeof(addr_len));
            address.resize(addr_len);
            is.read(const_cast<char*>(address.data()), addr_len);
            
            is.read(reinterpret_cast<char*>(&number_of_tickets), sizeof(number_of_tickets));
        }
    };
    
  • 字节序处理:如果需要跨平台序列化,要统一字节序(比如转成大端),避免不同字节序的平台读取数据错误。

内容的提问来源于stack exchange,提问作者Miguel Andre Repatacodo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 20:17:03