參考Is using memcmp on array of int strictly conforming?
以下代碼一定會(huì)輸出ok嗎?
#include <stdio.h>
#include <string.h>
struct S { int array[2]; };
int main () {
struct S a = { { 1, 2 } };
struct S b;
b = a;
if (memcmp(b.array, a.array, sizeof(b.array)) == 0) {
puts("ok");
}
return 0;
}
我在vs2005以及gcc4.4.3上做了測(cè)試,都輸出了ok。但這并不意味這個(gè)代碼會(huì)永遠(yuǎn)輸出ok。問(wèn)題主要集中于這里使用了賦值語(yǔ)句來(lái)復(fù)制值,但卻使用了memcmp這個(gè)基于內(nèi)存數(shù)據(jù)比較的函數(shù)來(lái)比較值。
c語(yǔ)言中的賦值運(yùn)算符(=)被定義為基于值的復(fù)制,而不是基于內(nèi)存內(nèi)容的復(fù)制。
C99 section 6.5.16.1 p2: In simple assignment (=), the value of the right operand is converted to the type of the assignment expression and replaces the value stored in the object designated by the left operand.
這個(gè)其實(shí)很好理解,尤其在不同類(lèi)型的數(shù)字類(lèi)型間復(fù)制時(shí),例如:
float a = 1.1;
int b = a;
因?yàn)楦↑c(diǎn)數(shù)和整形數(shù)的內(nèi)存布局不一樣,所以肯定是基于值的一種復(fù)制。另外,按照語(yǔ)言標(biāo)準(zhǔn)的思路來(lái)看,內(nèi)存布局這種東西一般都屬于實(shí)現(xiàn)相關(guān)的,所以語(yǔ)言標(biāo)準(zhǔn)是不會(huì)依賴(lài)實(shí)現(xiàn)去定義語(yǔ)言的。
上面的定理同樣用于復(fù)雜數(shù)據(jù)類(lèi)型,例如結(jié)構(gòu)體。我們都知道結(jié)構(gòu)體每個(gè)成員之間可能會(huì)有字節(jié)補(bǔ)齊,而使用賦值運(yùn)算符來(lái)復(fù)制時(shí),會(huì)不會(huì)復(fù)制這些補(bǔ)齊字節(jié)的內(nèi)容,是語(yǔ)言標(biāo)準(zhǔn)未規(guī)定的。這意味著使用memcmp比較兩個(gè)通過(guò)賦值運(yùn)算符復(fù)制的兩個(gè)結(jié)構(gòu)體時(shí),其結(jié)果是未定的。
但是上面的代碼例子中,比較的其實(shí)是兩個(gè)int數(shù)組。這也無(wú)法確認(rèn)結(jié)果嗎?這個(gè)問(wèn)題最終集中于,難道int也會(huì)有不確定的補(bǔ)齊字節(jié)數(shù)據(jù)?
C99 6.2.6.2 integer types For signed integer types, the bits of the object representation shall be divided into three groups: value bits, padding bits, and the sign bit. […] The values of any padding bits are unspecified.
這話(huà)其實(shí)我也不太懂。一個(gè)有符號(hào)整數(shù)int,其內(nèi)也有補(bǔ)齊二進(jìn)制位(bits)?
但無(wú)論如何,這個(gè)例子都不算嚴(yán)謹(jǐn)?shù)拇a。人們的建議是使用memcpy來(lái)復(fù)制這種數(shù)據(jù),因?yàn)閙emcpy和memcmp都是基于內(nèi)存內(nèi)容來(lái)工作的。