长度设置为0的glShaderSource

时间:2013-10-31 21:03:48

标签: c opengl

我想知道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?这种行为是否正确?

1 个答案:

答案 0 :(得分:2)

有两种情况允许OpenGL驱动程序假设着色器数据为空终止,如OpenGL 4参考页中所示。

来自glShaderSource文档:

  

如果lengthNULL,则假定每个字符串都为空终止。   如果lengthNULL以外的值,则它指向包含的数组   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规范,并且可能导致程序崩溃,但仅仅看到strlenglShaderSource调用并不一定意味着它是用户提供的字符串之一传递给它。

在一个略微切线的说明中,当标准未指定在具有显式长度的字符串内存在空字符时会发生什么,这意味着实现可以按照它的喜好进行。它可以将字符串视为空终止(因此跳过其后的所有内容),忽略空字符,或者导致着色器的编译失败,因为它不在GLSL识别为空格的字符中。