close

壹、記憶體視窗簡易操作

 

以 malloc / new 而言,無法使用監看式方式查看記憶體內含值,此時必須藉助記憶體視窗監看。考慮以下程式碼

#include <stdio.h>
#define N 10
int main()
{
    int i, arr[N];

    for(i=0; i!=N; ++i)
        arr[i]=100+i;

    for(i=0; i!=N; ++i) /* break point here */
        printf("arr[%d]=%d\n", i, arr[i]);
    return 0; /* break point here */
}

 

雖可以監看式觀查靜態陣列 arry,但此處以「記憶體」方式監看。注意上面的結果,arr[0] = 100,arr[1]=101,依此類推。

1.  按下 F5 (偵錯執行)後,再按「偵錯」->「視窗」->「記憶體」->「記憶體1」

Watch_051.png

2. 完成後將跳出如下視窗,先將焦點放到「位址」裡

Watch_052.png

3. 輸入 arr (注意,此處為欲監看之記憶體位址)

Watch_053.png

4. 最後會跳到 arr 之位址上去

在此需注意,arr 宣告為整數陣列,在我手邊之環境,sizeof(int) = 4,且為 little endian,下述之反白部份即為 &arr[0] 之內含值,也代表著 arr[0] 內含值為 0x00000064,換成 10 進制即為 100。

Watch_054.png  

5. 調整資料顯示方式

也由於是 little endian ,在這例子而言,為 4 bytes 看作一筆資料,故做適當調整。於記憶體視窗上,按「右鍵」,選擇「4個位元組整數」。

 Watch_055.png

顯示結果如下,變成 4 個位元組做一次顯示

Watch_056.png

甚至若覺得看 16 進位麻煩,可再次按右鍵,勾選「帶正負號顯示」,即可變成有號數方式顯示。

 Watch_057.png

顯示結果如下。

 Watch_058.png  

若覺得想要固定每行有幾筆資料,在「資料行」那裡做選擇。如下所示。

Watch_059.png

個人較為偏好資料行使用「自動」,因在調整視窗大小時,每行可顯示之資料筆數會隨之調整。

6. 更改記憶體內含值

在做更改記憶體內含值時,必須以十六進位方式顯示方可更改。

假設欲更改 arr[0] 與 arr[5] 內含值,先在「位址」裡輸入 arr (或 &arr[0]),按 Enter 後,將游標放在最前面 bit 上

 Watch_060.png

開始輸入欲更改之內容,假設欲改成 0x00009999,直接輸入 00009999 即可。注意,要修改時,不能選取整個數值,必須讓游標一次只選一個數字,輸入過去即可。

Watch_061.png

改完之後,繼續在記憶體「位址」上,輸入 &arr[5],完成按 Enter

Watch_062.png  

有時按完 Enter 後,「位址」數字會跳動,代表即是 &arr[5] 之位址值。繼續再次輸入 00009999。

Watch_063.png

接下來,改完後記得按一下「自動重新評估」,以確保能順利改過。

Watch_064.png

按完之後,那些剛被改過變紅色的值,應會變回黑色,代表已順利改過。完成後,再按一次 F5,切到 console 視窗裡面去,可看到 arr[0] 與 arr[5] 已被改為39321,即 16 進位之 0x00009999

Watch_065.png


貮、 監看動態記憶體

 

考慮以下程式碼

 

#include <stdio.h>
#include <stdlib.h>
#define N 10
int main()
{
    int i, *ptr;
    ptr = (int*)malloc(sizeof(int)*N);
    for(i=0; i!=N; ++i)
        ptr[i]=i+100;
    free(ptr); /* break point here */
    return 0;
}

 

如之前所說,這段程式碼在一般之監看式裡只看得到指標,看不到配置出來之記憶體位址。但用記憶體視窗方式會較好觀查。

在記憶體視窗裡,直接輸入 ptr

Watch_066.png  

上面便把 ptr[0]~ptr[9] 所有內容都顯示出來,若用監看式要自己 keyin  的話,則必須要 ptr[0], ptr[1]... ptr[9], 一筆一筆慢慢輸入,沒太大效率。

但若是分配二維動態記憶體的話 (假設欲配置 ptr[3][4]),再考慮以下程式碼

 

#include <stdio.h>
#include <stdlib.h>

#define X 3
#define Y 4

int main()
{
    int i, j;
    int **ptr;

    // Allocate
    ptr = (int**)malloc(sizeof(int*)*X);
    for(i=0; i!=X; ++i) 
        ptr[i] = (int*)malloc(sizeof(int)*Y);

    // Set Value
    for(i=0; i!=X; ++i)
        for(j=0; j!=Y; ++j)
            ptr[i][j] = i*Y+j;

    // Release
    for(i=0; i!=X; ++i)  /* break point here */
        free(ptr[i]);
    free(ptr);
    return 0;
}

 

一樣於記憶體「位址」裡面,輸入 ptr ,裡面內容卻如下所示

Watch_067.png

這是因為,ptr 先配置了 3 個指標出來,故只輸入 ptr,只會查到 ptr[i] 之三個 address。

若要繼續查 ptr[0] 裡面的 ptr[0][0]~ptr[0][3],在上圖看來,位址是 00395dc8,

但不用輸入那個 magic number,直接輸入 ptr[0] 即可

Watch_068.png

一樣,要查 ptr[1][0]~ptr[1][3],直接輸入 ptr[1] 即可。

 Watch_069.png

這樣查,很費時間,因配置出來的記憶體內含值並不連續。


參、 較佳之動態配置策略

 

在上一個例子裡面用 malloc 去配置一個二維之動態陣列 ptr[3][4],在記憶體裡面,元素值不一定是連續之狀態,因所配置的先是 3 個指標,再用這 3 個指標去配置 4 個整數陣列。這種可能發生不連續配置之問題,稱作 記憶體碎片化 。有一說未查證過,記憶體碎片化目前大多之作業系統都可處理掉, Coder 可不必考慮這問題,但我較乃把這問題考慮進去,反正也只是一點程式寫作上之技巧而已。

假設欲配置 int ptr[X][Y],在配置的過程中,

(1) 先直接配置一維之 int trunk[X*Y]
(2) ptr 再配置一維之 (int*) ptr[X]
(3) ptr[0] = trunk[0]
(4) ptr[i] = trunk[i] + Y * sizeof(int)

說得似乎很抽象,看個程式碼想一下很快理解。

 

#include <stdio.h>
#include <stdlib.h>

#define X 3
#define Y 4

int main()
{
    int i, j;
    int **ptr;
    int *trunk;

    // Allocate
    trunk = (int*)malloc(sizeof(int)*X*Y);
    ptr = (int**)malloc(sizeof(int*)*X);
    for(i=0; i!=X; ++i) {
        ptr[i] = trunk;
        trunk+=Y;
    }

    // Set Value
    for(i=0; i!=X; ++i)
        for(j=0; j!=Y; ++j)
            ptr[i][j] = i*Y+j;

    // Release
    free(*ptr); /* break point here */
    free(ptr);
    return 0;
}

 

好了之後,這次再次於記憶體視窗之位址裡面,輸入 &ptr[0][0] (或輸入 ptr[0] ),這次發現所配出來的記憶體都是連續的。

Watch_070.png

一樣的方式,也可用在三維配置上,於此不再示範。

 

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 edisonx 的頭像
    edisonx

    Edison.X. Blog

    edisonx 發表在 痞客邦 留言(1) 人氣()