Re: [問題] 有無malloc?
恕刪。我懶得畫記憶體配置圖了,特別是這次要說明三個不同之 sub function。
口述若不清楚請再反應。
假設 main 裡, short x = 0xffff,看三份副函式之呼叫情況。
void f0(short x2)
{
unsigned *p = (unsigned*)malloc(sizeof(unsigned));
*p = *(unsigned*)&x2;
printf("*p=%08x\n", *p);
*p = 0x12345678;
}
int main() {
short x = 0xffff;
short y = 0xffff;
f0(x);
}
(1) 主程式裡 f0(x)
f0 是做 pass by value,意指在記憶體裡面,會再多配置一個空間給 x2,
主程式 x □□ ---> 位置開頭為 0x12 ( x_value = 0xff 0xff)
副程式 x2 □□ ---> 位置開頭為 0x20 (x2_value = 0xff 0xff)
(2) unsigned *p = (unsigned*)malloc(sizeof(unsigned));
爾後 p 馬上配置一個 unsigned 的空間,也就是 4bytes。
主程式 x □□ ---> 位置開頭為 0x12 ( x_value = 0xff 0xff)
副程式 x2 □□ ---> 位置開頭為 0x20 (x2_value = 0xff 0xff)
副程式 p □□□□ ---> 配置開頭為 0x40 ( p = 0x40,剛配置的位置)
(3) *p = *(unsigned*)&x2;
直接將 (x2 記憶體位置值)給 (p指向位置值),但由於 p 是 pointer to unsigned,
所以一次會給 4bytes,至於原本的 x2 不到 4 bytes,就向後抓到二個問號。
注意到,pointer 抓值的原則是,以該 pointer 指向之資料型別為一 size,
不足並不會補零進來。
主程式 x □□ ---> 位置開頭為 0x12 ( x_value = 0xff 0xff)
副程式 x2 □□???? ---> 位置開頭為 0x20 (x2_value = 0xff 0xff)
|<---->|
p 取出空間
副程式 p □□□□ ---> 配置開頭為 0x40 ( p = 0x40,剛配置的位置)
(*p = 0x????ffff)
(4) printf("*p=%08x\n", *p);
這裡又是另一個小問題了,看第三步驟的圖,有沒有發現,
為什麼 *p 我是寫 0x????ffff,而不是寫 0xffff????
原因是: little endian。至於輸出結果如果剛好為 0x0000ffff的話,
那就別意外了,只是湊巧裡面的 ?? ?? 剛好都是零而已,原因是,
當 pointer 在做 dereference 時,一次取幾個 bytes ,視該 pointer 型態而定,
由於 pointer 型態是指向 unsigned,故抓了 4 bytes 。
(5) *p = 0x12345678;
輸出完後,再對 *p 做 assigned value,*p=0x12345678;
實際 assign 到的是這塊
主程式 x □□ ---> 位置開頭為 0x12 ( x_value = 0xff 0xff)
副程式 x2 □□???? ---> 位置開頭為 0x20 (x2_value = 0xff 0xff)
副程式 p □□□□ ---> 配置開頭為 0x40 ( p = 0x40,剛配置的位置)
(*p = 0x12 0x34 0x56 0x78)
配置到的是 p 配出來的記憶體空間,所以它不會動作主程式的 x ,
也不會動到副程式的 x2,最後它的值是從
0x????ffff 變成 0x12345678 而已。
(6) 而副函式結束時,x2 變數會被系統收回去,但 p 是用 malloc 出來的,
系統收不回去,造成了 memory leak,那塊 0x40 (p配置出來的) 就死在那。
----
void f1(short x2)
{
unsigned *p = (unsigned*)&x2;
printf("*p = %08x\n", *p);
*p = 0xffff5678;
}
這例子和剛剛相似,
(1) 主程式裡 f1(x);
當主程式之 x 傳遞指數到 f1 時,x2 會是另一塊空間,是一個副本
主程式 x □□ ---> 位置開頭為 0x12 ( x_value = 0xff 0xff)
副程式 x2 □□ ---> 位置開頭為 0x20 (x2_value = 0xff 0xff)
(2) unsigned *p = (unsigned*)&x2;
設一個指標變數 p ,裡面存的是 x2 之位置值
主程式 x □□ ---> 位置開頭為 0x12 ( x_value = 0xff 0xff)
副程式 x2 □□???? ---> 位置開頭為 0x20 (x2_value = 0xff 0xff)
副程式 p □□□□ ---> 存 0x20, 即 x2 之位置
(3) printf("*p = %08x\n", *p);
主程式 x □□ ---> 位置開頭為 0x12 ( x_value = 0xff 0xff)
副程式 x2 □□???? ---> 位置開頭為 0x20 (x2_value = 0xff 0xff)
|<---->|
p 取出空間
副程式 p □□□□ ---> 存 0x20, 即 x2 之位置
目前 p 是 unsigned pointer 型態,當做 *p 時,將去位置 0x20 (也就是 x2位置)
取出裡面的值出來。承上述原則,當用 *p 做 deference 時,由於 p 為
unsigned pointer ,所以一次讀出 4 bytes 出來,又因 little endian,
所以顯示為 0x????ffff 。
(4) *p = 0xffff5678;
這就是一個非常危險的動作了,它主要是將副函式的 x2
以 4 bytes 方式寫入 0xffff5678。可能有些人會以為,寫出去的結果是
副程式 x2 □□???? ---> 位置開頭為 0x20 (x2_value = 0xffff)
ffff5678
因為 x2 原本就是 0xffff, 所以寫入只改變了 ???? 這數值變 5678,
這是錯的!因為寫入的時候,還是以 little endian 方式寫入
副程式 x2 □□???? ---> 位置開頭為 0x20 (x2_value = 0x5678)
5678ffff
這樣改變的就不只是問號,連原本前半段看起來與 x2 一樣,但實際上
寫入不一樣,導致整個記憶體錯亂。
另,若 ???? 本身記憶體位置,
(a) 有其它變數在使用 --> 改寫了程式碼中其它變數,程式出包。
(b) 留給系統核心使用 --> 意思是會跳出「記憶體0x5f6ebacf為唯讀」之類的錯誤
都會導致出包。
(5) 最後 f1 副程式結束,所有記憶體收回。
主程式 x □□ ---> 位置開頭為 0x12 ( x_value = 0xff 0xff)
副程式 x2 □□???? ---> 回收
副程式 p □□□□ ---> 回收
但所謂的收回,並不代表會把 x2 與 p 直接清空,只是由作業系統
標示「這二個空間目前沒人在用」而已,然而如果原本的 ????
是主程式裡面配置的變數,那會發生什麼事情就真的沒人知道了。
(a) 從副程式收回 ???? 的觀點看來, ???? 目前沒人用,
要等到其他程式碼再宣告變數,恰巧 os 又配置到 ???? 時,才可復用。
(b) 從主程式觀念看來,一開始宣告變數後,在變數生命週期裡都可使用。
不覺得 (a)、(b) 整個就是相互衝突嗎?
-----
void f2(short *x2)
{
unsigned *p = (unsigned*)x2;
printf("*p = %08x\n", *p);
*p = 0xffff5678;
}
short x=0xffff;
f2(&x);
(1) 呼叫 f2(&x)
副函式之 x2 為指標,呼叫時存的是 x 之位置值。
注意, x2 是 pointer, size 是固定的,
這裡做普遍性假設為 4bytes。
主程式 x □□ ---> 位置開頭為 0x12345678,其值為 0xffff
副程式 x2 □□□□ ---> x2存之值為 (x2_value = 0x12345678)
(2) unsigned *p = (unsigned*)x2;
副函式再宣告一個指標,同時將 x2 裡面的內容,直接丟給 p。
主程式 x □□???? ---> 位置開頭為 0x12345678,其值為 0xffff
副程式 x2 □□□□ ---> x2存之值為 (x2_value = 0x12345678)
副程式 p □□□□ ---> p存之值為 ( p_value = 0x12345678)
(3) printf("*p = %08x\n", *p);
p 去找 0x12345678 這個位置,由於為 unsigned pointer,
所以一次抓 4 bytes (sizeof(unsigned)=4) 出來解讀,
再考慮 little endia 問題,所以解讀到的是 0x????ffff
再強調一次,如果輸出 0x0000ffff 不要認為是補零,
是「恰巧」後面的 ???? 是 0x0000 而已。
(4) *p = 0xffff5678;
再將 0xffff5678 這值寫入 (p 所指向之位置) 中,即 0x12345678 ,
也就是主程式裡面的 x,再考慮 little endian 問題
0x5678ffff
主程式 x □□???? ---> 位置開頭為 0x12345678,其值為 0xffff
副程式 x2 □□□□ ---> x2存之值為 (x2_value = 0x12345678)
副程式 p □□□□ ---> x2存之值為 ( p_value = 0x12345678)
(5) 副函式 f2 結束, 裡面的 x2, p 被收回,
最後不只改變了一塊莫名的 ???? ,這次連主程式裡面的 x 都改掉了。
----
上面該講的應該都講了,為求正確性,
little endian 講很多,這部份不熟的話再去翻翻書補起來。
--
No matter how gifted you are,
alone, can not change the world.
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 180.177.78.41
推
11/10 16:56, , 1F
11/10 16:56, 1F
→
11/10 17:04, , 2F
11/10 17:04, 2F
推
11/10 17:05, , 3F
11/10 17:05, 3F
→
11/10 17:06, , 4F
11/10 17:06, 4F
→
11/10 17:06, , 5F
11/10 17:06, 5F
是的,short x=0xffff; 但如果再多這兩行的話
unsigned *p = (unsigned*)&x;
printf("%08x\n", *p); // 到這裡 x 值都不會變, 因為只有 read
*p = 0xffff5678; // 這行就會改變 x 值
最後 x 會變 0x5678, little endian 問題。
→
11/10 17:06, , 6F
11/10 17:06, 6F
→
11/10 17:07, , 7F
11/10 17:07, 7F
c++ reference 不會有這問題,原因在於 reference 不能向 pointer 這樣亂搞。
short a=10;
int &b = static_cast<int>(a);
上面這段光在 compiler 那裡就出 error,所以想亂搞也沒辦法。
→
11/10 17:10, , 8F
11/10 17:10, 8F
→
11/10 17:10, , 9F
11/10 17:10, 9F
→
11/10 17:10, , 10F
11/10 17:10, 10F
→
11/10 17:14, , 11F
11/10 17:14, 11F
推
11/10 17:25, , 12F
11/10 17:25, 12F
→
11/10 17:29, , 13F
11/10 17:29, 13F
→
11/10 17:31, , 14F
11/10 17:31, 14F
→
11/10 17:32, , 15F
11/10 17:32, 15F
→
11/10 17:33, , 16F
11/10 17:33, 16F
→
11/10 17:34, , 17F
11/10 17:34, 17F
由小轉大 : short* ---> unsigned* ,目前還真沒看過有這種做法,頂多只有這樣 :
short x[8]={1,2,3,4};
unsigned long long *p = (unsigned long long*)&x[0];
*p=0ULL;
本來要設 4 次 0,現在只要設一次就好,但這作法是安全的,
除了 array、struct(不考慮 padding) 這種保證連續配置空間連續之條件,
其它沒什麼方式可保證可以安全執行。因那個 ???? 可能是從頭到尾連
「訪問」都不能「訪問」的,只要一訪問就出包。
除了這個例外之外,其他的全部都是二種做法
(1) 直接挑一樣的 size :
像要觀查 float bit ,就用 unsigned* 去接
(2) 直接用 unsigned char* :
這方法除了傳 address 外,還要再傳 bytes 數,
而且因為 little endian 關係,還要從後面翻譯回來 。
比較特例的是這個東西
unsigned long long x=0x1234567890123456;
printf("%016llx\n", x); // C99 以後支援, VC 不支援。
最後我用 unsigned 處理
unsigned *p = (unsigned*)&x ;
printf("%08x%08x\n", p[1], p[0]); // for little endian
這算是我看過 (不是用一樣size,也不是用 unsigned char) 特例。
※ 編輯: tropical72 來自: 180.177.78.41 (11/10 17:53)
→
11/10 17:35, , 18F
11/10 17:35, 18F
→
11/10 17:36, , 19F
11/10 17:36, 19F
→
11/10 17:38, , 20F
11/10 17:38, 20F
→
11/10 17:40, , 21F
11/10 17:40, 21F
→
11/10 17:42, , 22F
11/10 17:42, 22F
→
11/10 17:43, , 23F
11/10 17:43, 23F
→
11/10 17:46, , 24F
11/10 17:46, 24F
→
11/10 17:47, , 25F
11/10 17:47, 25F
→
11/10 17:47, , 26F
11/10 17:47, 26F
→
11/10 17:48, , 27F
11/10 17:48, 27F
→
11/10 17:49, , 28F
11/10 17:49, 28F
→
11/10 17:51, , 29F
11/10 17:51, 29F
→
11/10 17:52, , 30F
11/10 17:52, 30F
→
11/10 17:53, , 31F
11/10 17:53, 31F
→
11/10 17:54, , 32F
11/10 17:54, 32F
→
11/10 17:55, , 33F
11/10 17:55, 33F
推
11/10 22:07, , 34F
11/10 22:07, 34F
推
11/10 23:14, , 35F
11/10 23:14, 35F
推
11/10 23:46, , 36F
11/10 23:46, 36F
推
11/11 11:05, , 37F
11/11 11:05, 37F
推
12/08 17:38, , 38F
12/08 17:38, 38F
討論串 (同標題文章)