将指针转换为int并稍后再返回指针是否安全?
如果我们知道指针是否为32位长且int是32位长怎么样?
long* juggle(long* p) {
static_assert(sizeof(long*) == sizeof(int));
int v = reinterpret_cast<int>(p); // or if sizeof(*)==8 choose long here
do_some_math(v); // prevent compiler from optimizing
return reinterpret_cast<long*>(v);
}
int main() {
long* stuff = new long(42);
long* ffuts = juggle(stuff);
std::cout << "Is this always 42? " << *ffuts << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
这是否包含在标准中?
ken*_*ytm 37
没有.
例如,在x86-64上,指针长度为64位,但int只有32位长.将指针转换为int并再次返回会使指针值的高32位丢失.
如果你想要一个保证与指针一样长的整数类型,你可以使用intptr_t类型<cstdint>.您可以安全地从指向intptr_t和返回的指针重新解释.
Dam*_*mon 22
是的,如果...... (或"是的,但......"),否则没有.
该标准规定了(3.7.4.3)以下内容:
reinterpret_cast安全派生的指针值的结果[或] reinterpret_cast安全派生的整数表示的结果指针值std::intptr_t和[...]的结果reinterpret_cast安全派生指针值的整数表示的有效转换[或]加法或按位运算的结果,其中一个操作数是安全派生指针值的整数表示形式std::intptr_t该标准进一步指出可以放宽实现或者可以严格执行安全派生的指针.这意味着未指定使用或取消引用不安全派生的指针是否会调用未定义的行为(这是一个有趣的事情!)
这一切意味着没有更多也不少于"不同的东西可能会起作用,但唯一安全的事情就是如上所述".
因此,如果您要么std::intptr_t首先使用(最好的事情要做!),或者如果您知道您使用的任何整数类型的存储大小(例如long)至少是大小std::intptr_t,那么它是允许的,并且是 -定义(即"安全")以强制转换为整数类型并返回.标准保证.
如果不是这种情况,则从指针到整数表示的转换可能(或至少可能)丢失一些信息,并且转换返回将不会给出有效指针.或者,它可能是偶然的,但这不能保证.
一个有趣的轶事是C++标准根本没有直接定义std::intptr_t; 它只是说"与C标准中的7.18相同".
另一方面,C标准声明"指定一个带符号的整数类型,其属性是任何有效的void指针都可以转换为这种类型,然后转换回指向void的指针,结果将等于原始指针".
这意味着,如果没有上面相当复杂的定义(特别是第一个项目符号点的最后一位),就不允许转换为/来自任何东西void*.
AnT*_*AnT 21
是的,不是.
语言规范明确指出它是安全的(意味着最终你将获得原始指针值),只要整数类型的大小足以存储指针的[依赖于实现的]整数表示.
因此,在一般情况下,它不是"安全的",因为一般情况下int很容易变得太小.在您的特定情况下它虽然可能是安全的,因为您int可能足够大以存储您的指针.
通常,当您需要执行类似的操作时,您应该使用intptr_t/ uintptr_ttypes,这是为此目的专门引入的.不幸的是,intptr_t/ uintptr_t不是当前C++标准的一部分(它们是标准的C99类型),但是许多实现仍然提供它们.当然,您可以自己定义这些类型.
一般来说,没有; 指针可能大于int,在这种情况下,无法重建值.
如果已知整数类型足够大,则可以; 根据标准(5.2.10/5):
转换为足够大小的整数...并返回相同指针类型的指针将具有其原始值
但是,在C++ 03中,没有标准的方法来判断哪些整数类型足够大.C++ 11和C99(以及实际上大多数 C++ 03实现)以及Boost.Integer定义intptr_t并uintptr_t为此目的.或者您可以定义自己的类型并断言(最好在编译时)它足够大; 或者,如果您没有特殊原因使其成为整数类型,请使用void*.