2013-10-01 65 views
3

在此鏈接:Raymond Chen的單身實施使用狡猾的演員?

http://blogs.msdn.com/b/oldnewthing/archive/2011/04/06/10150261.aspx

這是最近向我指出的是,下面一行:

Widget *pwidOld = reinterpret_cast<Widget*> 
       (InterlockedCompareExchangePointerRelease(
        &reinterpret_cast<PVOID&>(g_pwidCached), 
        pwid, NULL)); 

有一個良性的,一個嚴重的問題。

良性的是static_cast可以在返回類型上完成。

嚴重者似乎是:

&reinterpret_cast<PVOID&>(g_pwidCached) 

有人告訴我說,與嚴格別名,當你通過&(void *的&)g_pwidCached的函數,編譯器允許假設值g_pwidCached不會改變,因爲這種改變將通過不是對象類型的指針類型發生,並且不是char *(因爲g_pwidCached不是void *,而是Widget *)。 3.10/10似乎是相關的。

這是隻是一個特定的編譯器實現的功能,只是Visual C++保證該行將正常工作?

+0

該代碼當然依賴於實現特定的屬性。甚至不能保證'Widget *'的大小與'void *'的大小相同,不用擔心一些名爲'InterlockedCompareExchangePointerRelease'的函數在傳遞指向'Widget *'的'void **'時會正常工作。 –

+0

@SteveJessop:大多數代碼通常會:Widget * wid; ICEPR((無效**)婦女參與發展,...);我相信這也是不正確的,對嗎? – ForeverLearning

+0

@SteveJessop就C++標準而言。但是這是非常特定於Windows的代碼,並且Posix和(我想)Windows都要求所有指針(包括指向Posix情況下的函數的指針)具有相同的大小和表示形式。 –

回答

1

該代碼當然依賴於實現特定的屬性。甚至不能保證Widget*void*的尺寸相同,不必擔心在通過指向Widget*void**時,稱爲InterlockedCompareExchangePointerRelease的某些函數可以正常工作。

我可能會忽略一些東西,但我認爲實際的問題是,「優化程序中的引用轉義碼見reinterpret_cast<PVOID&>,並且假定沒有引用gpwidCached已經逃脫了該函數?如果答案是「是」,那麼我們有一個問題,因爲編譯器在實際發生修改時將不會進行修改。但答案是「否」,前提是它將InterlockedCompareExchangePointerRelease視爲黑匣子,因爲它知道該函數在訪問它之前將該點轉換回正確類型,在這種情況下,編譯器而不是有空手假定沒有修改發生。

[編輯:其實,答案比我剛開始意識到時更「不」。據推測g_pwidCached是全球性的,所以編譯器可以從決不會假設它沒有被修改任何它調用的未知代碼,不管參數如何。該代碼使用名稱g_pwidCached,這當然會具有正確的類型有是沒有走樣可以修改它。]

的回答也是「沒有」如果InterlockedCompareExchangePointerRelease內聯和/或實現爲編譯器的內部,因爲實施將(如果它是正確的)做任何必要的事情以確保沒有任何問題。請注意,該函數採用void *volatile*,因此無論如何實現它,必須執行特定於實現的事情以確保不會出現別名問題,因爲傳遞類型雙引號指針是預期的用例。

有人告訴我說,與嚴格別名,當你通過 &(void *的&)g_pwidCached的函數,編譯器允許 假設g_pwidCached的價值沒有改變,因爲 通過不是對象的類型 的指針類型發生改變,並且不是字符*

這並不完全正確。如果函數實際上通過不正確的類型訪問值,則行爲不明確。毫無疑問,Windows的實現確實如此。但是沒有看到該函數的定義,編譯器不知道它是通過它傳遞的類型來訪問它,還是以某種方式計算出正確的類型以將其轉換回來,以便在沒有任何違反嚴格別名的情況下進行訪問。這就是爲什麼(就像我上面所說的)編譯器不能對未知函數的調用進行任何嚴格別名相關的優化。

+0

非常感謝您確認我的懷疑。如果您要重新編寫這樣的代碼行,那麼「最安全」的和符合標準的編寫方式是什麼? – ForeverLearning

+1

@Dilip:我不會重寫它,現在很好。針對Windows的編譯器只需完成這項工作即可。 –

+0

感謝您的詳細解釋。你對這個東西的命令使我感到羞恥:-) – ForeverLearning

0

類型的g_pwidCached是錯誤的,應該是

void* g_pwidCached; 

,因爲曾經使用它的唯一的事,將其視爲void*

(可以說delete g_pwidCached;是錯誤的,無論如何,並且應該通過InterlockedExchangePointer(nullptr, &g_pwidCached)做才能獲得價值爲本地和使用本地刪除)

但是這一切,包括更好的代碼,在該意見被解決博客發佈。

+0

這就是我所做的,但是Jessop先生覺得,因爲這是一個特定於平臺的代碼,VC++將保證它需要的任何實現特定屬性。 – ForeverLearning