x64 转换后指针截断
pointer truncation after x64 conversion
我是 C++ 的新手,我正在尝试编译一个小型遗留 class 库,我一直将其用作 x64 的 x86。
现在编译器显示一些警告:
IUnknown* _p;
// warning C4311: 'type cast': pointer truncation from 'IUnknown *' to 'int'
// warning C4302: 'type cast': truncation from 'IUnknown *' to 'int'
virtual int GetHashCode() override
{
return (int)_p;
}
// 1> warning C4311: 'type cast': pointer truncation from 'void *' to 'long'
// 1> warning C4302: 'type cast': truncation from 'void *' to 'long'
void MyMethod(IntPtr hwnd, String^ str)
{
CComBSTR bstrValue = (BSTR)Marshal::StringToBSTR(str).ToPointer();
HRESULT result = SomeClass()->SomeMethod((long)hwnd.ToPointer(), bstrValue);
}
其中 SomeMethod 定义为
#ifdef _X86_
typedef long CUSTOMHWND;
#else
typedef LONGLONG CUSTOMHWND;
#endif
virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE SomeMethod(
/* [in] */ CUSTOMHWND hwndOwner,
/* [in] */ __RPC__in BSTR pValue) = 0;
我必须如何更改代码才能使 x64 安全?
https://docs.microsoft.com/en-us/cpp/build/common-visual-cpp-64-bit-migration-issues?view=vs-2019
- 在引用指针时使用 DWORD_PTR 和 LONG_PTR 而不是 long、int 等(大多数 Windows 句柄是指针)
- 当应用程序不使用 LARGEADDRESSAWARE (https://docs.microsoft.com/en-us/cpp/build/reference/largeaddressaware-handle-large-addresses?view=vs-2019) 链接器选项时,所有指针都限制在较低的 4GB,因此截断不会造成伤害。这是为了节省您修复所有警告的时间。
更多迁移技巧:https://docs.microsoft.com/en-us/windows/desktop/WinProg64/migration-tips
一般来说,在 [expr.cast]/4 的基础上,
中的 C 风格转换
return (int)_p;
最终将执行
return reinterpret_cast<int>(_p);
现在,严格来说,我认为这种情况下的行为实际上是不确定的。根据 [expr.reinterpret.cast]/4:
A pointer can be explicitly converted to any integral type large enough to hold all values of its type.
请注意,标准仅在此处针对将指针值转换为大到足以容纳该指针类型的任何可能值的整数类型的情况指定了行为。我不知道标准中的任何措辞会指定将指针值转换为太小的整数类型时的行为,这是您在这里有效执行的操作,因为 int
并不大足以(在 MSVC 上)表示一个 64 位对象指针值。实际上,我听说过的任何编译器都只会生成 return 此处包含的地址的低 32 位(假设 int
是 32 位宽)的代码,但您很可能不应该依赖它。
一般来说,如果可以的话,我会尽量避免将指针强制转换为整数。如果确实需要,请确保使用足够大的整数类型来保存指针值。在这种情况下,std::intptr_t
或 std::uintptr_t
将是我的首选。
综上所述,您的函数名称表明它只是应该 return 一个散列值,大概是为了在散列 table 中识别某个对象。如果这是目标,我建议您只使用 std::hash
为您计算这样的哈希值,而不是自己依赖将指针转换为整数值:
virtual std::size_t GetHashCode() override
{
return std::hash<IUnknown*>{}(_p);
}
让 std::hash
为您获取地址的哈希值。无论目标平台是什么,您都可以依靠它始终做任何正确的事情来获得这样的哈希值……
我是 C++ 的新手,我正在尝试编译一个小型遗留 class 库,我一直将其用作 x64 的 x86。
现在编译器显示一些警告:
IUnknown* _p;
// warning C4311: 'type cast': pointer truncation from 'IUnknown *' to 'int'
// warning C4302: 'type cast': truncation from 'IUnknown *' to 'int'
virtual int GetHashCode() override
{
return (int)_p;
}
// 1> warning C4311: 'type cast': pointer truncation from 'void *' to 'long'
// 1> warning C4302: 'type cast': truncation from 'void *' to 'long'
void MyMethod(IntPtr hwnd, String^ str)
{
CComBSTR bstrValue = (BSTR)Marshal::StringToBSTR(str).ToPointer();
HRESULT result = SomeClass()->SomeMethod((long)hwnd.ToPointer(), bstrValue);
}
其中 SomeMethod 定义为
#ifdef _X86_
typedef long CUSTOMHWND;
#else
typedef LONGLONG CUSTOMHWND;
#endif
virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE SomeMethod(
/* [in] */ CUSTOMHWND hwndOwner,
/* [in] */ __RPC__in BSTR pValue) = 0;
我必须如何更改代码才能使 x64 安全?
https://docs.microsoft.com/en-us/cpp/build/common-visual-cpp-64-bit-migration-issues?view=vs-2019
- 在引用指针时使用 DWORD_PTR 和 LONG_PTR 而不是 long、int 等(大多数 Windows 句柄是指针)
- 当应用程序不使用 LARGEADDRESSAWARE (https://docs.microsoft.com/en-us/cpp/build/reference/largeaddressaware-handle-large-addresses?view=vs-2019) 链接器选项时,所有指针都限制在较低的 4GB,因此截断不会造成伤害。这是为了节省您修复所有警告的时间。
更多迁移技巧:https://docs.microsoft.com/en-us/windows/desktop/WinProg64/migration-tips
一般来说,在 [expr.cast]/4 的基础上,
中的 C 风格转换return (int)_p;
最终将执行
return reinterpret_cast<int>(_p);
现在,严格来说,我认为这种情况下的行为实际上是不确定的。根据 [expr.reinterpret.cast]/4:
A pointer can be explicitly converted to any integral type large enough to hold all values of its type.
请注意,标准仅在此处针对将指针值转换为大到足以容纳该指针类型的任何可能值的整数类型的情况指定了行为。我不知道标准中的任何措辞会指定将指针值转换为太小的整数类型时的行为,这是您在这里有效执行的操作,因为 int
并不大足以(在 MSVC 上)表示一个 64 位对象指针值。实际上,我听说过的任何编译器都只会生成 return 此处包含的地址的低 32 位(假设 int
是 32 位宽)的代码,但您很可能不应该依赖它。
一般来说,如果可以的话,我会尽量避免将指针强制转换为整数。如果确实需要,请确保使用足够大的整数类型来保存指针值。在这种情况下,std::intptr_t
或 std::uintptr_t
将是我的首选。
综上所述,您的函数名称表明它只是应该 return 一个散列值,大概是为了在散列 table 中识别某个对象。如果这是目标,我建议您只使用 std::hash
为您计算这样的哈希值,而不是自己依赖将指针转换为整数值:
virtual std::size_t GetHashCode() override
{
return std::hash<IUnknown*>{}(_p);
}
让 std::hash
为您获取地址的哈希值。无论目标平台是什么,您都可以依靠它始终做任何正确的事情来获得这样的哈希值……