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

结构体中std::string转char*出现首字符未定义问题排查

问题:std::string转char*存储到结构体后首段字符出现未定义值

我希望将std::string转换为带有size_t长度信息的char并存储到结构体中,之后再还原为std::string。但后续获取该char或还原字符串时,首段字符始终呈现未定义值,以下是最小示例代码及运行输出:

#include <iostream>
#include <string>

struct Data {
    Data(std::string string) : m_data(string.data()), m_size(string.size()) {
        std::cout << "String size: " << m_size << std::endl;
        std::cout << "String in constructor: ";
        for (int j = 0; j < m_size; ++j) {
            std::cout << (m_data)[j];
        }
        std::cout << std::endl;
    }
    char* m_data;
    size_t m_size;

    static void print_string(Data model_data) {
        std::cout << "String in static function: ";
        for (int j = 0; j < model_data.m_size; ++j) {
            std::cout << model_data.m_data[j];
        }
        std::cout << std::endl;
    }
};

int main() {

    std::string test_string = "Hello World! The next characters might appear...";
    Data test_data(test_string);
    Data::print_string(test_data);
    std::cout << "String from member: ";
    for (int j = 0; j < test_data.m_size; ++j) {
        std::cout << test_data.m_data[j];
    }        
    return 0;
}

运行输出:

String size: 48
String in constructor: Hello World! The next characters might appear...
String in static function: �~v��� next characters might appear...
String from member: �~v��� next characters might appear...%

问题原因

核心问题是你存储的是临时std::string对象的内部指针,具体细节:

  • 构造函数Data(std::string string)的参数是按值传递,传入的test_string会被复制出一个临时std::string对象。
  • string.data()返回的指针指向这个临时对象内部的字符数组。
  • 构造函数执行完成后,临时std::string对象会被销毁,其内部的字符数组也会被释放,此时m_data就变成了悬空指针。
  • 后续访问悬空指针指向的内存属于未定义行为,首段字符乱码就是这种行为的表现,后续内容看似正常只是内存还没被覆盖的巧合。

正确实现方案

方案1:直接存储std::string(推荐)

让结构体持有std::string,无需手动管理内存,简单安全:

#include <iostream>
#include <string>

struct Data {
    std::string m_data;
    Data(std::string string) : m_data(std::move(string)) {
        std::cout << "String size: " << m_data.size() << std::endl;
        std::cout << "String in constructor: " << m_data << std::endl;
    }

    static void print_string(Data model_data) {
        std::cout << "String in static function: " << model_data.m_data << std::endl;
    }
};

int main() {
    std::string test_string = "Hello World! The next characters might appear...";
    Data test_data(test_string);
    Data::print_string(test_data);
    std::cout << "String from member: " << test_data.m_data << std::endl;
    return 0;
}

方案2:手动分配内存存储char*(需注意内存管理)

如果必须使用char*,需要手动分配内存并复制字符串内容,同时要处理内存释放:

#include <iostream>
#include <string>
#include <algorithm>

struct Data {
    char* m_data;
    size_t m_size;

    Data(const std::string& string) : m_size(string.size()) {
        // 分配内存,+1可选,用于兼容C风格字符串的结束符
        m_data = new char[m_size + 1];
        std::copy(string.begin(), string.end(), m_data);
        m_data[m_size] = '\0'; // 可选,添加结束符

        std::cout << "String size: " << m_size << std::endl;
        std::cout << "String in constructor: " << m_data << std::endl;
    }

    // 析构函数释放内存
    ~Data() {
        delete[] m_data;
    }

    // 禁用拷贝构造和赋值,避免浅拷贝导致重复释放内存
    Data(const Data&) = delete;
    Data& operator=(const Data&) = delete;

    // 可选:实现移动构造和移动赋值
    Data(Data&& other) noexcept : m_data(other.m_data), m_size(other.m_size) {
        other.m_data = nullptr;
        other.m_size = 0;
    }

    Data& operator=(Data&& other) noexcept {
        if (this != &other) {
            delete[] m_data;
            m_data = other.m_data;
            m_size = other.m_size;
            other.m_data = nullptr;
            other.m_size = 0;
        }
        return *this;
    }

    static void print_string(Data model_data) {
        std::cout << "String in static function: ";
        for (size_t j = 0; j < model_data.m_size; ++j) {
            std::cout << model_data.m_data[j];
        }
        std::cout << std::endl;
    }
};

int main() {
    std::string test_string = "Hello World! The next characters might appear...";
    Data test_data(test_string);
    Data::print_string(std::move(test_data)); // 使用移动避免拷贝(因为拷贝被禁用)
    // 注意:test_data此时已处于移动后的状态,不能再使用
    return 0;
}

内容的提问来源于stack exchange,提问作者faressc

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 14:17:33