编译GLSL程序出现unexpected $end语法错误,是否为文件加载代码导致?
修复GLSL编译时“意外遇到文件结尾(unexpected $end)”报错
核心问题定位
- 悬空指针触发空源码读取(直接原因)
代码在把着色器源码指针传入OpenGL之前,就提前调用free释放了源码的堆内存。执行顺序为:loadShader返回的堆内存地址赋值给vst/fst,再赋值给vertexShaderSource/fragmentShaderSource- 立刻执行
free(vst)/free(fst),此时两个源码指针已经指向被回收的无效内存 - 后续调用
glShaderSource时,从无效内存中读到的内容为随机值,大概率是空字符串,直接触发空内容的语法错误
- 字符串缺少结束符
loadShader函数给缓冲区多分配了1字节空间(malloc(length+1)),但读取完文件内容后,从未给缓冲区最后一个字节赋值C字符串要求的结束符'\0'。即使不提前释放内存,OpenGL在glShaderSource传入长度参数为NULL的场景下,会一直读取内存直到碰到随机的'\0',很可能读到空内容触发报错。
修复步骤
- 调整内存释放时序:
glShaderSource接口会自动把传入的源码完整拷贝到OpenGL内部维护的内存空间,只有在glShaderSource调用完成之后,才能安全释放用户侧的源码内存。 - 补全字符串结束符:文件内容读取完成后,手动在缓冲区末尾写入
'\0',保证缓冲区是合法的C风格字符串。 - 增加文件读取校验:确认
fread实际读取的字节数和文件预期长度一致,避免读到不完整的脏内容。
修正后的核心代码
修正后的loadShader函数:
char* loadShader(const char* filename){ char* buffer = NULL; long length; FILE* f = fopen(filename, "rb"); if(!f){ printf("failed to open shader file: %s\n", filename); return NULL; } fseek(f, 0, SEEK_END); length = ftell(f); fseek(f, 0, SEEK_SET); buffer = (char*)malloc(length + 1); if (!buffer) { fclose(f); return NULL; } size_t readBytes = fread(buffer, 1, length, f); fclose(f); // 关键补全:添加字符串结束符 buffer[length] = '\0'; // 读取不完整时直接释放资源返回空 if (readBytes != length) { free(buffer); return NULL; } return buffer; }
修正后的着色器加载时序:
GLchar* vst = (GLchar*)loadShader("game_data/assets/shaders/simple_vert.glsl"); GLchar* fst = (GLchar*)loadShader("game_data/assets/shaders/simple_frag.glsl"); const GLchar* vertexShaderSource = vst; const GLchar* fragmentShaderSource = fst; int success; char infoLog[512]; unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); glCompileShader(vertexShader); // glShaderSource完成拷贝后再释放内存 if(vst){ free(vst); vst = NULL; } glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); if(!success) { glGetShaderInfoLog(vertexShader, 512, NULL, infoLog); printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", infoLog); } unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); glCompileShader(fragmentShader); if(fst){ free(fst); fst = NULL; } glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); if(!success) { glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog); printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n", infoLog); }
内容的提问来源于stack exchange,提问作者Riley
相关产品推荐
相关产品推荐

