我想知道glShaderSource在这个边缘情况下的行为是否正确:
glShaderSource(shader, 1, (const char**)ptr, length)
其中:
char * tmp = (char*)alloca(0);
const char ** ptr = &tmp;
GLint length[1] = { 0 };
所以基本上OpenGL被告知读取一个零长度的字符串,但它仍然会调用strlen,即使它知道它的长度。在规范中,它表示只有当我们传递NULL作为第四个参数或者给定的长度低于0时,OpenGL才会假设字符串为零终止。为什么要调用strlen?这种行为是否正确?
答案 0 :(得分:2)
有两种情况允许OpenGL驱动程序假设着色器数据为空终止,如OpenGL 4参考页中所示。
来自glShaderSource文档:
如果
length
为NULL
,则假定每个字符串都为空终止。 如果length
是NULL
以外的值,则它指向包含的数组string
的每个对应元素的字符串长度。每 length数组中的元素可以包含相应的长度 string(空字符不算作字符串长度的一部分) 或小于0的值表示该字符串为空终止。 此时不扫描或解析源代码字符串;他们是 只需复制到指定的着色器对象中。
由于强调的约束都不适用于所提出的情况,因此不允许驱动程序直接对所提供的数据使用不安全的c-string函数,如strlen
。如果确实如此,它可能会在数据结束后运行,并可能最终访问未分配的内存,从而导致程序崩溃。
但是,驱动程序 允许将数据副本复制到安全缓冲区中,然后在其上调用strlen
。例如,实现可能包含如下代码:
void glShaderSource(GLuint shader,
GLsizei count,
const GLchar **string,
const GLint *length)
{
if (length == NULL) { /* each string is null terminated. */ }
else for (GLsizei i = 0; i < count; ++i)
{
size_t len = length[i];
if (len < 0) { /* this string is null terminated. */ }
else
{
char* buffer = (char*)malloc(len + 1);
memcpy(buffer, string[i], len);
buffer[len] = 0;
size_t str_len = strlen(buffer);
// This is OK, because we copied the
// data into a null terminated buffer.
free(buffer);
}
}
}
因此,如果您的驱动程序在这种情况下实际上在用户提供的指针上调用strlen
,那么它违反了OpenGL规范,并且可能导致程序崩溃,但仅仅看到strlen
被glShaderSource
调用并不一定意味着它是用户提供的字符串之一传递给它。
在一个略微切线的说明中,当标准未指定在具有显式长度的字符串内存在空字符时会发生什么,这意味着实现可以按照它的喜好进行。它可以将字符串视为空终止(因此跳过其后的所有内容),忽略空字符,或者导致着色器的编译失败,因为它不在GLSL识别为空格的字符中。