返回std::string转char*异常:单行调用ReadShader后指针失效问题
你碰到的这个坑,核心是C++临时对象的生命周期规则,属于很容易忽略的基础知识点,咱们一步步拆解:
两种调用方式的本质区别
1. 两行代码的安全写法
std::string vertexShaderString = ReadShader("Shader/Vertex_Shader.vs"); const GLchar * vertexShaderSource = vertexShaderString.c_str();
这里vertexShaderString是一个命名的std::string对象,它的生命周期会持续到当前作用域结束(比如当前函数的花括号闭合、或者变量所在的代码块结束)。调用c_str()返回的指针,指向的是这个对象内部管理的字符数组——只要vertexShaderString还活着,这个指针就是有效的,所以运行正常。
2. 单行代码的危险写法
const GLchar * vertexShaderSource = (ReadShader("Shader/Vertex_Shader.vs")).c_str();
ReadShader返回的是一个临时std::string对象,C++里这类临时对象的生命周期非常短:只存在于当前语句中(也就是到分号;为止)。当你调用c_str()拿到指针后,这个临时对象立刻会被销毁,它内部的字符数组也会被释放。此时vertexShaderSource就变成了悬垂指针,指向已经被回收的内存,后续访问自然会出现未定义行为(你看到的0xdd是调试模式下编译器给已释放内存填充的标记值)。
解决办法
推荐方案:保留命名对象
继续用两行代码的写法,用一个命名的std::string持有返回值,确保在使用c_str()指针的整个期间,这个对象都处于存活状态,从根源避免悬垂指针。可选简化方案:绑定临时对象生命周期到操作
如果想简化代码,可以把OpenGL的着色器编译操作和临时对象放在同一条语句里——这样临时对象会存活到语句结束,等编译完成后再销毁就不会有问题:GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShader, 1, &(ReadShader("Shader/Vertex_Shader.vs").c_str()), nullptr); glCompileShader(vertexShader);不过这种写法可读性稍差,还是推荐第一种更清晰的命名对象方式。
额外提醒
std::string::c_str()返回的指针是依赖原对象的:一旦原std::string被销毁、或者内部内存重新分配(比如调用resize()、append()等方法),这个指针就会失效。所以一定要保证原对象的生命周期覆盖指针的使用周期。
内容的提问来源于stack exchange,提问作者user34299

