2017-02-11 35 views
5

例如,爲下面的函數法律:使用帶有常量成員的目標結構的memcpy是否合法?

struct two_int { 
    const int a, b; 
} 

void copy_two(const two_int *src, two_int *dest) { 
    memcpy(dest, src, sizeof(two_int)); 
} 

看起來至少一些類型常義值的修改是不允許的,但它不是我清楚,如果這個資格。

如果答案是「是允許的,在一般情況」,我也想知道在哪裏dest新分配與malloc內存的特殊情況(因此尚未指派任何值),如:

two_int s = {.a = 1, .b = 2}; 
two_int *d = malloc(sizeof(two_int)); 
copy_two(&s, d); 

更新:這似乎是後一個問題似乎是肯定的,以回答(這是確定),對於一個剛剛malloc「d結構的情況下,但原來的,更普遍的問題仍然有效, 我認爲。

+4

否。嘗試修改常數變量*任何地方*是*未定義的行爲*。 –

+2

這意味着'two_int'''不能通過'malloc'或其他動態分配,對嗎?由於您無法將任何施工信息傳遞給'malloc' ... – SODIMM

+1

此問題可能符合[language-lawyer]標籤。 –

回答

0

僅當實際目標對象沒有靜態或自動持續時間時,爲此目的使用memcpy纔會定義行爲。

鑑於代碼:

struct foo { double const x; }; 
void outsideFunction(struct foo *p); 

double test(void) 
{ 
    struct foo s1 = {0.12345678}; 
    outsideFunction(&s1); 
    return 0.12345678; 
} 

編譯器將有權優化功能:

double test(void) 
{ 
    static struct foo const s1 = {0.12345678}; 
    outsideFunction(&s1); 
    return s1.x; 
} 

在許多的處理器,只有這樣加載雙用任意常數是從保存該常量的對象中加載它的值,在這種情況下,編譯器會方便地知道必須保持常量0.12345678的對象(s1.x)。最終效果是使用memcpy寫入s1.x的代碼可能會破壞數字常數0.12345678的其他用途。俗話說,「變數不會,常數不變」。討厭的東西。

因爲memcpy的要求編譯器「忘記」什麼以前一直存儲在目標存儲,包括任何「有效型」一切問題不會爲分配時間的物體存在。靜態和自動對象的聲明類型獨立於其中存儲的任何內容而存在,並且不能通過「memcpy」或任何其他方式擦除,但分配持續時間對象只有「有效類型」纔會被擦除。

+0

這是一個有效的例子,但這不是一般問題的特例嗎?如果排除只包含const成員的結構,那麼它是否可以複製? – jxh

+0

@jxh:我從一個更復雜的例子開始,在這種情況下會失敗。假設'struct foo'具有非常量成員,但函數接受了'v'參數,將's1.x'初始化爲'v + 0.12345678',然後返回'v + 0.12345678'。編譯器有權注意到's1.x'中的值精確地匹配返回所需的值,並因此加載該值。我可以看到有一種強大的'const'形式,它可以保證如果一個代碼讀取一個對象的值,然後讀取該對象*使用派生的指針... – supercat

+0

...從那個對象的地址沒有任何類型轉換*都會產生相同的值,但這需要認識到類型轉換會影響編譯器對指針目標的假設。我認爲它們應該是顯而易見的,但像gcc這樣的編譯器的設計會讓這很困難。 – supercat