GetMemory的典型例子

qkk123456789發表於2012-07-19

原文地址:http://blog.csdn.net/wl_haanel/article/details/5186568

NO.1: 程式首先申請一個char型別的指標str,並把str指向NULL(即str裡存的是NULL的地址,*str為NULL中的值為0),呼叫函式的過程 中做了如下動作:1申請一個char 型別的指標p,2把str的內容copy到了p裡(這是引數傳遞過程中系統所做的),3為p指標申請了100個空間,4返回Test函式.最後程式把字元 串hello world拷貝到str指向的記憶體空間裡.到這裡錯誤出現了!str的空間始終為NULL而並沒有實際的空間.深刻理解函式呼叫的第2步,將不難發現問題 所在!

因為 GetMemory 並不能傳遞動態記憶體,函式引數是傳值的,也就是說引數的變化不會影響到實參,因此這裡分配到的空間的地址不會存到全域性的str ,Test 函式中的 str 一直都是 NULL。 strcpy(str, "hello world");將使程式崩潰。

void GetMemory(char *p)

{

   p = (char*)malloc(100);

}

void Test(void)

{

   char *str = NULL;

   GetMemory(str);

   strcpy(str, "hello world");

   printf(str);

}

//請問執行Test函式後會是什麼樣的結果?

NO.2: 程式首先申請一個char型別的指標str,並把str指向NULL.呼叫函式的過程中做了如下動作:1申請一陣列p[]並將其賦值為hello world(陣列的空間大小為12),2返回陣列名p付給str指標(即返回了陣列的首地址).那麼這樣就可以列印出字串"hello world"了麼?當然是不能的!因為在函式呼叫的時候漏掉了最後一步.也就是在第2步return陣列名後,函式呼叫還要進行一步操作,也就是釋放記憶體 空間.當一個函式被呼叫結束後它會釋放掉它裡面所有的變數所佔用的空間.所以陣列空間被釋放掉了,也就是說str所指向的內容將不確定是什麼東西.

char *GetMemory(void)

{

   char p[] = "hello world";

   return p;

}

void Test(void)

{

   char *str = NULL;

   str = GetMemory();

   printf(str);

}

NO.3:問題同NO.1,正確答案為可以列印出hello.但記憶體洩漏了!

void GetMemory(char **p, int num)

{

   *p = (char*)malloc(num);

}

void Test(void)

{

   char *str = NULL;

   GetMemory(&str, 100);

   strcpy(str, "hello");

   printf(str);

}

NO.4: 申請空間,拷貝字串,釋放空間.前三步操作都沒有任何問題.到if語句裡的判斷條件開始出錯了,因為一個指標被釋放之後其內容並不是NULL,而是一個 不確定的值.所以if語句永遠都不能被執行.這也是著名的"野"指標問題.所以我們在編寫程式釋放一個指標之後一定要人為的將指標付成NULL.這樣就會 避免出現"野"指標的出現.有人說"野"指標很可怕,會帶來意想不到的錯誤.

void Test(void)

{

   char *str = (char*)malloc(100);

   strcpy(str, "hello");

   free(str);

   if (str != NULL)

   {

      strcpy(str, "world");

     printf(str);

   }

}

void GetMemory1(char *p)

{

    p = (char *)malloc(100);

}

void Test1(void)

{

    char *str = NULL;

    GetMemory1(str);

    strcpy(str, "hello world");

    printf(str);

}

//str一直是空,程式崩潰

char *GetMemory2(void)

{

    char p[] = "hello world";

    return p;

}

void Test2(void)

{

    char *str = NULL;

    str = GetMemory2();

    printf(str);

}

char *GetMemory3(void)

{

     return "hello world";

}

void Test3(void)

{

    char *str = NULL;

    str = GetMemory3();

    printf(str);

}

//Test3 中列印hello world,因為返回常量區,而且並沒有被修改過。Test2中不一定能列印出hello world,因為指向的是棧。

void GetMemory4(char **p, int num)

{

    *p = (char *)malloc(num);

}

void Test4(void)

{

    char *str = NULL;

    GetMemory3(&str, 100);

    strcpy(str, "hello");

    printf(str);

}

//記憶體沒釋放

void Test5(void)

{

    char *str = (char *) malloc(100);

    strcpy(str, "hello");

    free(str);

    if(str != NULL)

{

   strcpy(str, "world");

   printf(str);

}

}

//str為野指標,列印的結果不得而知

void Test6()

{

    char *str=(char *)malloc(100);

    strcpy(str, "hello");

    str+=6;

    free(str);

    if(str!=NULL)

    {

        strcpy(str, "world");

        printf(str);

    }

}

//VC斷言失敗,執行錯誤

 

另轉一則:

char *GetMemory3(int num)

{

    char *p = (char *)malloc (sizeof(char) * num);

    return p; //返回指標 p

}
 
void Test3(void)

{

    char *str = NULL;

    str = GetMemory3(100);  //這裡指標 str也和指標 p的指向一樣的記憶體塊。

    strcpy(str, "hello");

    cout<< str << endl;

    free(str);  //這裡最終只是釋放了str指向的記憶體塊,對於str指標 依然還是指向那塊記憶體,看了一些資料說應該加一句str=0;那指標 p呢?p也依然在指向那塊記憶體啊。

}

回覆:

答者:wutaozhao() 信譽:100 級別:user2 日期:2007-6-29 21:42:21 id:41451636


指標 之間傳遞的就是地址,所以str和p所指向的記憶體塊是同一個,釋放其中任何一個即可

答者:what_a_big() 信譽:100 級別:user1 日期:2007-6-29 21:42:21 id:41451637


指標 p呢?
=============================================================================
after returning from GetMemory()
p (區域性 變數)沒了,那塊記憶體還在。
現在str 指向那塊記憶體。

看了一些資料說應該加一句str=0
=================================
也可以不寫str=NULL,只要你不再引用str。

答者:believefym(feng) 信譽:100 級別:user5 日期:2007-6-29 21:50:38 id:41451675


同一塊記憶體,釋放一次即可,只不過所有指向那塊記憶體的指標 都失效了,就像樓主程式碼裡的p和str

釋放之後str就變成了野指標 ,加str=NULL,可以避免之後錯誤使用該指標 ,方便除錯

答者:lightnut() 信譽:100 級別:star1 日期:2007-6-29 22:05:48 id:41451736


1.
函式進入Test3():
char* str = NULL;

======================
變數   變數的地址         變數的值(內容)
str    0x0013fe8c         0

2.
str = GetMemory3(..)=====>
char *p = (char *)malloc (sizeof(char) * num);
將分配的記憶體起始地址(0x003a60b0)賦給棧變數p,

===============================================
變數     變數的地址       變數的值(內容)
p        0x0013fda8       0x003a60b0

3. 從GetMemory3(..)返回, p的值(0x003a60b0)
拷貝給str, 棧變數p生命結束, 釋放其所佔棧記憶體
(地址0x0013fda8)

=================================================
變數       變數的地址     變數的值(內容)
str        0x0013fe8c      0x003a60b0

4. free(str):  釋放str指向的記憶體(0x003a60b0開始的記憶體空間)
5. 推出Test3()後,  棧變數str生命結束, 釋放其所佔棧記憶體
(地址0x0013fe8c)

答者:freshui(五月的風 -最近老犯困) 信譽:100 級別:user1 日期:2007-6-29 22:12:07 id:41451763


呵呵 p沒了
已經死了 
:)

注意看變數的作用域

答者:buhaohaoxuexi() 信譽:100 級別:user1 日期:2007-6-29 22:37:00 id:41451888


為什麼會死呢,不是說動態分配是分配在堆上的嗎?他不應該死啊。

答者:yydrewdrew(滿堂花醉三千客,一劍霜寒十四州) 信譽:100 級別:user1 日期:2007-6-29 23:55:01 id:41452325


char *GetMemory3(int num)

{

    char *p = (char *)malloc (sizeof(char) * num);

    return p; //返回指標 p

}
 
void Test3(void)

{

    char *str = NULL;

    str = GetMemory3(100);  //呼叫完後p被析構,p為區域性 作用域存在於函式GetMemory3的棧中,當GetMemory3被呼叫完後,棧空間被釋放,因而被析構。

    strcpy(str, "hello");

    cout<< str << endl;

    free(str);  //這裡最好加一句str = NULL。

}

答者:yydrewdrew(滿堂花醉三千客,一劍霜寒十四州) 信譽:100 級別:user1 日期:2007-6-29 23:56:59 id:41452334


注意p和它指向的物件的生命期是兩回事
p是一個區域性 指標 ,而它指向的物件是堆空間具有全域性生命期

答者:xlbdan(流浪劍客) 信譽:100 級別:user5 日期:2007-6-30 0:15:43 id:41452409


return p; //返回指標 p


這裡返回之後,p已經消失.
但是p指向的那塊記憶體依然存在,並且被返回到了呼叫端,用str接收了.
所以只要free(str)就行了,也就釋放了p申請的那塊記憶體
然後把 str賦為NULL,是防止再錯誤的去使用它

相關文章