在将指针转换为“uintptr_t”之前是否需要转换为“void *”,反之亦然?
Is a conversion to `void *` required before converting a pointer to `uintptr_t` and vice versa?
C99 标准的 7.18.1.4 节指出:
The following type designates an unsigned integer type with the property that any valid pointer to void
can be converted to this type, then converted back to pointer to void
, and the result will compare equal to the original pointer:
uintptr_t
这是否意味着只有 void *
类型可以转换为 uintptr_t
并返回而不更改原始指针的值?
特别想知道是否需要下面的代码才能使用uintptr_t
:
int foo = 42;
void * bar = &foo;
uintptr_t baz = bar;
void * qux = baz;
int quux = *(int *)qux; /* quux == foo == 42 */
或者如果这个更简单的版本由 C99 标准保证产生相同的效果:
int foo = 42;
uintptr_t bar = &foo;
int baz = *(int *)bar; /* baz == foo == 42 */
在将指针转换为 uintptr_t
之前是否需要转换为 void *
,反之亦然?
区别还存在,因为虽然任何指向 对象的指针 都可以转换为 void *
,但 C 并不要求函数指针可以转换为 void *
又回来了!
至于指向其他类型对象的指针,C标准说any指针可以转为整数,整数可以转为任意指针,但是这样结果是实现定义的。因为标准说只有 void *
可以来回转换,所以最安全的选择是先 cast 指向 void *
的指针,因为它可能是指针转换为 uintptr_t
时具有不同的表示形式也会导致不同的整数表示形式,因此可以想象:
int a;
uintptr_t up = (uintptr_t)&a;
void *p = (void *)up;
p == &a; // could be conceivably false, or it might be even that the value is indeterminate.
是的,需要演员 to/from void *
。
这段话的措辞类似于 %p
格式说明符 printf
的措辞,后者需要 void *
作为参数。
来自第 7.19.6.1 节:
p
The argument shall be a pointer to void. The value of the pointer
is converted to a sequence of printing characters, in an
implementation-defined manner.
您可以使用较少的中间变量执行上述操作,但是:
int foo = 42;
uintptr_t baz = (void *)&foo;
int quux = *(int *)((void *)baz); /* quux == foo == 42 */
是的,为了便于移植,需要中间转换为 void*
。这是因为指向整数转换的指针是 "implementation defined",所以您的平台可以做任何事情,只要他们记录它。
顺便说一句,您在问题中混淆了 "cast" 和 "conversion"。 "Conversion" 是更通用的术语,"cast" 是 "explicit conversion"。
如果我们在 C
中谈论指针,我们应该考虑它们的表示方式。指针值的大小基本上取决于体系结构,而不是它所指向的类型。 void*
的值和 uintptr_t
的值相同,但表示形式可能不同。
如果 uintptr_t
根据文档
The following type designates an unsigned integer type with the
property that any valid pointer to void can be converted to this type,
then converted back to pointer to void, and the result will compare
equal to the original pointer: uintptr_t.
这意味着此类型可用于安全地存储指针的值,并且可以等于、大于或什至小于 void*
本身,因为具体实现细节。
所以总结一下,使用转换为 void*
可以确保转换 to/from uintptr_t
保证有效。
在很多地方,标准的作者认为没有必要强制编译器行为明智,因为他们希望编译器无论如何都这样做。
如果实现定义 uintptr_t
并且 p
是 void*
,标准对 void *q = (void*)(uintptr_t)p;
的唯一要求是 q==p
比较相等.该标准不保证 q
可用于 p
可用于的任何特定目的,甚至不保证可用于与其他指针比较以外的任何目的。
从实用的角度来看,没有理由要求实现在通过 uintptr_t
的转换的任何一侧强制转换 to/from void*
,但标准将允许实现以任意方式运行如果省略了这些额外的演员表,就会很时尚。另一方面,由于该标准还允许实现在几乎所有实际情况下以任意方式运行
涉及 uintptr_t
,唯一真正的问题是是否愿意依赖实施者不以迟钝的方式行事。这种依赖对于一些商业编译器来说可能是合理的,但对于一些免费的编译器来说就不那么重要了。
C99 标准的 7.18.1.4 节指出:
The following type designates an unsigned integer type with the property that any valid pointer to
void
can be converted to this type, then converted back to pointer tovoid
, and the result will compare equal to the original pointer:
uintptr_t
这是否意味着只有 void *
类型可以转换为 uintptr_t
并返回而不更改原始指针的值?
特别想知道是否需要下面的代码才能使用uintptr_t
:
int foo = 42;
void * bar = &foo;
uintptr_t baz = bar;
void * qux = baz;
int quux = *(int *)qux; /* quux == foo == 42 */
或者如果这个更简单的版本由 C99 标准保证产生相同的效果:
int foo = 42;
uintptr_t bar = &foo;
int baz = *(int *)bar; /* baz == foo == 42 */
在将指针转换为 uintptr_t
之前是否需要转换为 void *
,反之亦然?
区别还存在,因为虽然任何指向 对象的指针 都可以转换为 void *
,但 C 并不要求函数指针可以转换为 void *
又回来了!
至于指向其他类型对象的指针,C标准说any指针可以转为整数,整数可以转为任意指针,但是这样结果是实现定义的。因为标准说只有 void *
可以来回转换,所以最安全的选择是先 cast 指向 void *
的指针,因为它可能是指针转换为 uintptr_t
时具有不同的表示形式也会导致不同的整数表示形式,因此可以想象:
int a;
uintptr_t up = (uintptr_t)&a;
void *p = (void *)up;
p == &a; // could be conceivably false, or it might be even that the value is indeterminate.
是的,需要演员 to/from void *
。
这段话的措辞类似于 %p
格式说明符 printf
的措辞,后者需要 void *
作为参数。
来自第 7.19.6.1 节:
p
The argument shall be a pointer to void. The value of the pointer is converted to a sequence of printing characters, in an implementation-defined manner.
您可以使用较少的中间变量执行上述操作,但是:
int foo = 42;
uintptr_t baz = (void *)&foo;
int quux = *(int *)((void *)baz); /* quux == foo == 42 */
是的,为了便于移植,需要中间转换为 void*
。这是因为指向整数转换的指针是 "implementation defined",所以您的平台可以做任何事情,只要他们记录它。
顺便说一句,您在问题中混淆了 "cast" 和 "conversion"。 "Conversion" 是更通用的术语,"cast" 是 "explicit conversion"。
如果我们在 C
中谈论指针,我们应该考虑它们的表示方式。指针值的大小基本上取决于体系结构,而不是它所指向的类型。 void*
的值和 uintptr_t
的值相同,但表示形式可能不同。
如果 uintptr_t
根据文档
The following type designates an unsigned integer type with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer: uintptr_t.
这意味着此类型可用于安全地存储指针的值,并且可以等于、大于或什至小于 void*
本身,因为具体实现细节。
所以总结一下,使用转换为 void*
可以确保转换 to/from uintptr_t
保证有效。
在很多地方,标准的作者认为没有必要强制编译器行为明智,因为他们希望编译器无论如何都这样做。
如果实现定义 uintptr_t
并且 p
是 void*
,标准对 void *q = (void*)(uintptr_t)p;
的唯一要求是 q==p
比较相等.该标准不保证 q
可用于 p
可用于的任何特定目的,甚至不保证可用于与其他指针比较以外的任何目的。
从实用的角度来看,没有理由要求实现在通过 uintptr_t
的转换的任何一侧强制转换 to/from void*
,但标准将允许实现以任意方式运行如果省略了这些额外的演员表,就会很时尚。另一方面,由于该标准还允许实现在几乎所有实际情况下以任意方式运行
涉及 uintptr_t
,唯一真正的问题是是否愿意依赖实施者不以迟钝的方式行事。这种依赖对于一些商业编译器来说可能是合理的,但对于一些免费的编译器来说就不那么重要了。