2016-07-07 77 views
0

嚴格的別名讓我偏執。有時我用* int指針設置值,並且期望目標內存讀取相同的數據,而不管讀指針類型是什麼。嚴格的別名不能保證這一點,有時甚至會導致這種情況並非如此。memcpy別名int到char產生UB?

如果我在循環中讀取char [],並且在該char []數組中有一個* int chainging東西,我打破了其他標準C事物之間的別名規則。

我正在製作一個JIT編譯器,因爲我使用的是x86,所以我確信我不必關心int對齊。直到我們解決了別名問題之前,讓我們保持這一點。

考慮這個片段:

unsigned char x86[] = {0x11, 0x44, 0x42, ... }; 
uint32_t *specific_imm = (x86+10); 

現在,* specific_imm = 42;在x86平臺上仍然是UB,因爲允許編譯器假定* specific_imm不會與x86 []混疊。通過做出這樣的假設,它不需要立即設置這些字節,而是可以進行各種優化。將x86 []和* specific_imm設置爲volatile將解決我的問題,但這不夠好,因爲我想正確學習C.

我們已經解決了現在的別名問題。有人建議這個解決方案: memcpy(x86 + 10,specific_imm,4);

但是C標準似乎也有關於別名指針的問題(如果我正確地理解了事情),如以下代碼所示。

/* naive implementation of memcpy */ 
inline void _memcpy(unsigned char *a, unsigned char *b){ 
    *a = *b; 
} 

int main(void) { 
    long i = 0xFFFFFFFF; 
    unsigned char c = 1; 
    ++i; 
    _memcpy(&c,&i); 
    return c; 
} 

由於編譯器是免費的假設,「我」是不是在這種情況下,(?)影響到C不知何故,主要是免費進行優化,以剛剛返回1?

我更喜歡在解決問題之前解決問題。

在此先感謝

+1

我建議你修復你的例子,你可能在答案中看到了我的評論:我的價值並不明顯。 – 2501

回答

1

通過進行這樣的假設,它並不需要設置這些字節的時候了,但可能做各種優化

的它並不需要設置它們。它可以做任何事情。


設置於x86 []和* specific_imm揮發性會解決我的問題

不是真的。嚴格別名表示某個變量不能通過指向不相關類型的指針進行更改。這樣做會導致程序執行標準未指定的操作。通常這會體現在各種優化器相關的錯誤中,但不一定。該方案可能不會做任何事情,或崩潰和燃燒。

volatile不會解決這個問題(特別是因爲你聲明指針的東西指向volatile數據,而不是讓實際的數據變量volatile)。

一些編譯器如GCC優化代碼,假設您的程序永遠不會違反嚴格的別名(從而調用未定義的行爲)。但是,這並不意味着關閉優化會自動刪除未定義的行爲,它只會關閉優化器的依賴性,因爲它假定您的程序沒有調用未定義的行爲。它不會修復實際的錯誤。


一些人認爲這個解決方案的原因:有效類型規則的memcpy

這將解決這個問題。 6.5/6:

如果一個值被拷貝到一個對象具有使用 的memcpy的memmove沒有聲明的類型,或者被複製爲字符類型的陣列,然後 有效類型的改性的對於該訪問,並且對於不修改該值的後續訪問是該值被複制的對象的有效類型 (如果該對象具有該對象)。

這滿足嚴格別名規則的第一部分中,6.5/7:

一個目的應具有其存儲的值僅由具有 之一以下類型的左值表達式獲得:

- 一個類型與有效類型的對象的兼容,


但C標準似乎與太走樣有關指針的問題(如果我理解正確的事情)

不,那是不正確的。真正的memcpy函數使用void指針,並且由於上述原因不能違反嚴格的別名。您的家庭釀造版本使用unsigned char*,這也很好,6.5/7:

- 一種字符類型。

請閱讀What is the strict aliasing rule?,特別是this answer

+0

如果標準的作者已經指定,當'memcpy'的源操作數是'void *'以外的類型時,指針類型必須適合源,並且同樣適用於目標,這將適用於大多數用途不需要悲觀別名假設的類型雙關的memcpy。不幸的是,'memcpy'的實際規則允許編譯器惡作劇的機會,而沒有許多有用的優化機會。 – supercat

1

你錯了。 C編譯器可以使用而不是假定任意指針和指向char變體的指針都不是別名。它也不能假定兩個指向signed和unsigned int的指針,或者兩個指向signed和unsigned long的指針都沒有對齊。

在你的最後一個例子中,任何一個理智的軟件開發者都會以這樣的方式設置編譯器警告,以免編譯。

+0

'不對齊'應該是'不是別名'?還是我錯過了重要的東西? – 4386427

+0

你可以在沒有GCC警告的情況下獲得UB的別名指針。 – jdoeblink33

+0

@ jdoeblink33:你也可以在gcc忽略他們不喜歡的標準方面(例如,如果代碼需要能夠訪問多個結構類型的公共初始序列,則Standard指定聲明包含這些結構的聯合類型將允許任何此類類型讀取任何其他類型的成員,但自從gcc的作者不喜歡這個規則,他們只是忽略它。 – supercat