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

更多迁移技巧: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_tstd::uintptr_t 将是我的首选。

综上所述,您的函数名称表明它只是应该 return 一个散列值,大概是为了在散列 table 中识别某个对象。如果这是目标,我建议您只使用 std::hash 为您计算这样的哈希值,而不是自己依赖将指针转换为整数值:

        virtual std::size_t GetHashCode() override
        {
            return std::hash<IUnknown*>{}(_p);
        }

std::hash 为您获取地址的哈希值。无论目标平台是什么,您都可以依靠它始终做任何正确的事情来获得这样的哈希值……