2010-07-18 28 views
11

在回答另外一個問題,我想下面的例子:ç別名規則和memcpy

void *p; 
unsigned x = 17; 

assert(sizeof(void*) >= sizeof(unsigned)); 
*(unsigned*)&p = 17;  // (1) 
memcpy(&p, &x, sizeof(x)); // (2) 

1個線斷裂重疊規則。然而,第2行是可以的。別名規則。問題是:爲什麼?編譯器是否有特殊的關於memcpy等函數的內置知識,還有其他一些規則可以使memcpy成功?是否有一種在不破壞別名規則的情況下在標準C中實現類似memcpy的函數的方法?

回答

13

C標準很清楚。由p命名的對象的有效類型爲void*,因爲它具有聲明的類型,請參閱6.5/6。 C99中的別名規則適用於讀取寫入,並且根據6.5/7寫入void*unsigned左值(1)是未定義的行爲。

相比之下,(2)memcpy是精細的,因爲unsigned char*可以別名任何對象(6.5/7)。標準在7.21.2/1定義memcpy作爲

對於本節的所有功能,每一個字符應被解釋爲,如果它有類型無符號的字符(並因此每個可能的對象表示是有效的,並具有一個不同的值)。

memcpy函數將s2指向的對象中的n個字符複製到s1指向的對象中。如果在重疊對象之間進行復制,則行爲不確定。

但是,如果之後存在對p的使用,則可能會導致取決於bitpattern的未定義行爲。如果這樣的使用不會發生,該代碼是C.


精細按照C++標準,這在我看來是很不明朗的問題,我覺得有以下成立。請不要將這種解釋視爲唯一可能 - 模糊/不完整的規範留下了很多投機空間。

(1)有問題,因爲&p的對齊方式可能不適用於unsigned類型。它將存儲在p中的對象的類型更改爲unsigned int。只要您稍後通過p不訪問該對象,則別名規則不會被破壞,但對齊要求可能仍然存在。然而(2)

線沒有對齊的問題,因此是有效的,只要你不訪問p事後作爲void*,取決於void*類型如何解釋存儲位模式這可能會導致不確定的行爲。我不認爲對象的類型會因此而改變。

還有一個很長的GCC Bugreport,它還討論了通過這樣一個轉換導致的指針寫入的影響,以及與新位置的不同之處是什麼(該列表中的人不同意它是什麼)。

+0

請在Marcelo的回答的評論中看到問題。對此有何評論? – zvrba 2010-07-18 17:48:22

+1

@zvrba,哦,沒有什麼不同。您只需在中間加入'void *',這等於直接投射。如果你想「模擬」memcpy,你必須像'unsigned char * pc =(unsigned char *)&p,* i =(unsigned char *)&x; * pc = * x; * pC++ = * x ++; ......,這就是它所做的。 – 2010-07-18 19:17:38

+0

如果srcptr和destptr相等,memcpy()涉及的對象是否被認爲是「重疊的」,或者是否有任何明確允許該場景的語言? – supercat 2011-11-20 21:44:16