2

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

4

1 回答 1

2

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

来自glShaderSource文档:

如果lengthNULL,则假定每个字符串都以空值结尾。如果length是 以外的值NULL,则它指向一个数组,该数组包含 的每个对应元素的字符串长度string。长度数组中的每个元素可能包含相应字符串的长度(空字符不计入字符串长度的一部分)或小于 0 的值以指示字符串以空字符结尾。此时不扫描或解析源代码字符串;它们被简单地复制到指定的着色器对象中。

由于所强调的约束都不适用于所提出的情况,因此不允许驱动程序strlen直接在提供的数据上使用不安全的 c-string 函数。如果是这样,它很可能会在数据结束后运行,并可能最终访问未分配的内存,从而使程序崩溃。

但是,允许驱动程序数据复制到安全缓冲区中,然后调用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 识别为空格的字符中。

于 2013-11-01T00:23:19.163 回答