使用ifstream读取GLSL着色器文件异常:顶点着色器读取错误
GLSL着色器文件读取异常问题排查与解决
问题现象
在C++项目中读取GLSL着色器文件时出现异常:读取顶点着色器后,输出内容却是片元着色器的最后几行,片元着色器读取正常;使用GDB调试时控制台出现乱码,手动重新输入文件内容也无法解决问题。
相关代码与输出
调用代码
char* vertexShaderSource; char* fragmentShaderSource; std::string pathToVertexShader = "./src/shaders/vertexShader.vert"; std::string pathToFragmentShader = "./src/shaders/fragmentShader.frag"; readFileToString(pathToVertexShader, &vertexShaderSource); readFileToString(pathToFragmentShader, &fragmentShaderSource); std::cout << vertexShaderSource << std::endl; std::cout << fragmentShaderSource << std::endl;
控制台输出(vertexShaderSource内容)
5, 0.2, 1.0); <--- thats it? } #version 330 core out vec4 FragColor; void main() { FragColor = vec4(0.3, 0.5, 0.2, 1.0); }
readFileToString函数实现
void readFileToString(std::string pathToFile, char** fileContentCString) { std::string fileContent; std::ifstream filestream(pathToFile); if (!filestream) { std::cerr << "Failed to load file: " << pathToFile << std::endl; return; } std::string currentLine; while (getline(filestream, currentLine)) { fileContent += currentLine + "\n"; } filestream.close(); *fileContentCString = (char*)fileContent.c_str(); }
编译命令
g++ -g -I./include -I./src -L./lib src/glad.c src/main.cpp src/window.cpp -lglfw3dll -o donut.exe
问题根源
核心问题在于readFileToString函数中,fileContent是函数内的局部std::string对象,当函数执行完毕后,该对象会被销毁,其内部的字符缓冲区也会被释放。而fileContent.c_str()返回的指针仅指向这个临时缓冲区,函数结束后,这个指针就变成了野指针。
两次调用该函数时,第二个局部std::string对象(读取片元着色器时的fileContent)会复用之前第一个std::string对象释放的内存空间,导致vertexShaderSource这个野指针最终指向了第二个std::string的缓冲区,所以输出的是片元着色器的内容。GDB调试时的乱码也是野指针访问无效内存导致的。
解决方案
方案1:返回std::string(推荐,无需手动管理内存)
修改函数返回std::string,在调用处使用c_str()获取指针:
std::string readFileToString(std::string pathToFile) { std::string fileContent; std::ifstream filestream(pathToFile); if (!filestream) { std::cerr << "Failed to load file: " << pathToFile << std::endl; return ""; } std::string currentLine; while (getline(filestream, currentLine)) { fileContent += currentLine + "\n"; } filestream.close(); return fileContent; }
调用代码修改为:
std::string vertexShaderStr = readFileToString(pathToVertexShader); std::string fragmentShaderStr = readFileToString(pathToFragmentShader); const char* vertexShaderSource = vertexShaderStr.c_str(); const char* fragmentShaderSource = fragmentShaderStr.c_str(); std::cout << vertexShaderSource << std::endl; std::cout << fragmentShaderSource << std::endl;
方案2:手动分配堆内存(需注意内存释放)
如果必须返回char*,可以在函数内部分配堆内存,将字符串内容复制过去:
void readFileToString(std::string pathToFile, char** fileContentCString) { std::string fileContent; std::ifstream filestream(pathToFile); if (!filestream) { std::cerr << "Failed to load file: " << pathToFile << std::endl; *fileContentCString = nullptr; return; } std::string currentLine; while (getline(filestream, currentLine)) { fileContent += currentLine + "\n"; } filestream.close(); // 分配堆内存并复制内容 *fileContentCString = new char[fileContent.size() + 1]; strcpy(*fileContentCString, fileContent.c_str()); }
使用后记得释放内存,避免泄漏:
// 使用完后 delete[] vertexShaderSource; delete[] fragmentShaderSource;
方案3:使用智能指针管理内存(更安全)
用std::unique_ptr自动管理内存,避免手动释放的麻烦:
#include <memory> std::unique_ptr<char[]> readFileToString(std::string pathToFile) { std::string fileContent; std::ifstream filestream(pathToFile); if (!filestream) { std::cerr << "Failed to load file: " << pathToFile << std::endl; return nullptr; } std::string currentLine; while (getline(filestream, currentLine)) { fileContent += currentLine + "\n"; } filestream.close(); std::unique_ptr<char[]> buf(new char[fileContent.size() + 1]); strcpy(buf.get(), fileContent.c_str()); return buf; }
调用代码:
auto vertexShaderSource = readFileToString(pathToVertexShader); auto fragmentShaderSource = readFileToString(pathToFragmentShader); std::cout << vertexShaderSource.get() << std::endl; std::cout << fragmentShaderSource.get() << std::endl;
内容的提问来源于stack exchange,提问作者Lance Dy Chua
相关产品推荐
相关产品推荐

