这是我的答案https://stackoverflow.com/a/8284829/524368的几乎逐字副本,稍作修改,使其与此问题相符。
C定义(和C ++跟随它),指针可以转换为整数,即类型为uintptr_t
,如果以这种方式获得的整数转换回它来自的原始指针类型,则会产生原始指针。
然后是指针算术,这意味着如果我有两个指针指向所以相同的对象我可以得到它们的差异,产生一个整数(类型为ptrdiff_t
),并且该整数被添加或减去任何一个原始指针,将产生另一个。 它还定义了,通过向指针添加1,可以产生指向索引对象的下一个元素的指针。 另外两个uintptr_t
的差值除以同一个对象的指针的sizeof(type pointed to)
必须等于它们自己被减去的指针。 最后但并非最不重要的是, uintptr_t
值可能是任何值。 它们也可以是不透明的手柄。 它们不需要是地址(尽管大多数实现都是这样做的,因为它有意义)。
现在我们可以看看臭名昭着的空指针。 C定义从uintptr_u
值为0的类型为无效指针的指针。 请注意,源代码中的值始终为0 。 在后端,在编译的程序中,用于实际将其表示到机器的二进制值可能完全不同! 通常它不是,但它可能是。 C ++是相同的,但是C ++不允许比C更多的隐式转换,因此必须将0明确地转换为void*
。 另外,因为空指针不引用对象,因此没有解除引用的大小指针算术未定义为空指针 。 引用无对象的空指针也意味着,没有定义可以将它明智地转换为类型指针。
所以,如果这一切都未定义,那为什么这个宏会起作用呢? 因为大多数实现(意味着编译器)都非常容易上当,而编译器编码器在最高程度上是懒惰的。 大多数实现中指针的整数值只是指针本身在后端的值。 所以空指针实际上是0.虽然没有检查空指针上的指针算法,但是如果指针得到某种类型的指定,大多数编译器都会默默地接受它,即使它没有意义。 如果你想这么说, char
是“单位大小”的C类型。 因此,关于强制转换的指针算法就像后端地址上的artihmetic一样。
简而言之,尝试使用预期结果做指针魔术是C语言方面的偏移是没有意义的,它只是不起作用。
让我们退一步,记住,我们实际上要做的是:最初的问题是,原始OpenGL顶点数组函数将指针作为其数据参数,但对于顶点缓冲区对象,我们实际上想要指定一个字节基于我们数据的偏移量,这是一个数字。 对于C编译器,该函数采用指针(我们学习的不透明的东西)。 相反,OpenGL定义的是利用编译器的工作方式。 大多数编译器将指针及其整数等价物实现为相同的二进制表示。 所以我们要做的是,它使编译器用我们的数字而不是指针来调用这些函数。
所以在技术上我们唯一需要做的就是告诉编译器“是的,我知道你认为这个变量a
是一个整数,你是对的,而且函数glDrawElements
只为它的数据参数取一个void*
。但是猜猜:该整数是从void*
“产生的,通过将其转换为(void*)
然后握住拇指,编译器实际上是如此愚蠢以传递整数值,因为它是函数。
所以这一切都归结为以某种方式绕过旧的函数签名。 转换指针是恕我直言的脏方法。 我做的有点不同:我搞乱了功能签名:
typedef void (*TFPTR_DrawElementsOffset)(GLenum,GLsizei,GLenum,uintptr_t);
TFPTR_DrawElementsOffset myglDrawElementsOffset =
(TFPTR_DrawElementsOffset)glDrawElements;
现在你可以使用myglDrawElementsOffset
而不做任何愚蠢的强制转换,并且offset参数将传递给函数,没有任何危险,编译器可能会搞乱它。 这也是我在程序中使用的方法。