函數返回局部變量

看看下面的程序的輸出:

#include <stdio.h>
char *returnStr()
{
    char *p="hello world!";
    return p;
}
int main()
{
    char *str;
     str=returnStr();
    printf("%s\n", str);
    
    return 0;
}


這個沒有任何問題,因爲"hello world!"是一個字符串常量,存放在靜態數據區,
把該字符串常量存放的靜態數據區的首地址賦值給了指針,
所以returnStr函數退出時,該該字符串常量所在內存不會被回收,故能夠通過指針順利無誤的訪問。

但是,下面的就有問題:

#include <stdio.h>
char *returnStr()
{
    char p[]="hello world!";
    return p;
}
int main()
{
    char *str;
     str=returnStr();
    printf("%s\n", str);
    
    return 0;
}

"hello world!"是一個字符串常量,存放在靜態數據區,沒錯,
但是把一個字符串常量賦值給了一個局部變量(char []型數組),該局部變量存放在棧中,
這樣就有兩塊內容一樣的內存,這是與前着最本質的區別,
當returnStr函數退出時,棧要清空,局部變量的內存也被清空了,
所以這時的函數返回的是一個已被釋放的內存地址,所以打印出來的是亂碼。

如果函數的返回值非要是一個局部變量的地址,那麼該局部變量一定要申明爲static類型。如下:

#include <stdio.h>
char *returnStr()
{
    static char p[]="hello world!";
    return p;
}
int main()
{
    char *str;
     str=returnStr();
    printf("%s\n", str);
    
    return 0;
}


這個問題可以通過下面的一個例子來更好的說明:

#include <stdio.h>
//返回的是局部變量的地址,該地址位於動態數據區,棧裏

char *s1()
{
    char p[]="Hello world!";
    printf("in s1 p=%p\n", p);
    printf("in s1: string's address: %p\n", &("Hello world!"));
    return p;
}

//返回的是字符串常量的地址,該地址位於靜態數據區

char *s2()
{
    char *q="Hello world!";
    printf("in s2 q=%p\n", q);
    printf("in s2: string's address: %p\n", &("Hello world!"));
    return q;
}

//返回的是靜態局部變量的地址,該地址位於靜態數據區

char *s3()
{
    static char r[]="Hello world!";
    printf("in s3 r=%p\n", r);
    printf("in s3: string's address: %p\n", &("Hello world!"));
    return r;
}

int main()
{
    char *t1, *t2, *t3;
     t1=s1();
     t2=s2();
     t3=s3();
    
    printf("in main:");
    printf("p=%p, q=%p, r=%p\n", t1, t2, t3);

    printf("%s\n", t1);
    printf("%s\n", t2);
    printf("%s\n", t3);
    
    return 0;
}

運行輸出結果:

in s1 p=0xbff92efb
in s1: string's address: 0x80486ac
in s2 q=0x80486ac
in s2: string's address: 0x80486ac
in s3 r=0x804998c
in s3: string


這個結果正好應證了上面解釋,同時,還可是得出一個結論:
字符串常量,之所以稱之爲常量,因爲它可一看作是一個沒有命名的字符串且爲常量,存放在靜態數據區。
這裏說的靜態數據區,是相對於堆、棧等動態數據區而言的。
靜態數據區存放的是全局變量和靜態變量,從這一點上來說,字符串常量又可以稱之爲一個無名的靜態變量,
因爲"Hello world!"這個字符串在函數 s1和s2 中都引用了,但在內存中卻只有一份拷貝,這與靜態變量性質相當神似。

v

ly:CR �.r;��f,��f,ii-theme-font:minor-latin;mso-fareast-font-family:宋體;mso-fareast-theme-font:minor-fareast;mso-hansi-font-family:Calibri;mso-hansi-theme-font:minor-latin'>重載的operator new必須含有一個size_t參數,表示要分配內存的對象的長度,同時必須返回一個指向等於這個長度的對象的指針。若沒有找到符合要求的存儲單元,構造函數將不被調用,另外需要一個表示失敗的返回值以外還應該拋出異常(可以自定義)。

Operator delete參數爲一個指向由operatornew分配內存的void*(已調用析構函數後得到的指針),其返回類型爲void。

演示示例

3.3.2重載類中的new/delete

重載類中的new實際上創建的是一個static成員函數,該new只爲創建該類對象起作用,不會影響默認的全局版本new,但是需要注意名字隱藏的問題。

注意:任何類中只要提供了自己的operator new或operator new[],那麼就得同時提供對應的類相關版本的plain new,placement new以及nothrow new,否則根據名字隱藏的規則(將全局new遮掩掉了),將會發生沒有可用匹配的錯誤。

一旦爲類重載了operator new與operator delete,那麼無論何時創建這個類的對象,都將調用這些重載的運算符,但若創建該類的一個對象數組時,全局operator new將立即被調用,用來爲這個數組分配足夠的內存,因此要避免出現這種情況,需要重載operator new的數組版本:operator new[]與operator delete[]。

演示示例:(item 36 in exceptional c++)

1.             //問題1:爲什麼B的delete有第二個參數而D沒有?2.             class B 3.             {4.             public:5.               virtual ~B();6.               void operator delete ( void*,size_t ) throw();7.               voidoperator delete[]( void*, size_t ) throw();8.               void f( void*, size_t )throw();9.             };10.          class D : public B11.          {12.         public:13.            void operator delete ( void* )throw();14.            void operatordelete[]( void* ) throw();15.          };16.          void f()17.          {18.            //問題2:下面各個語句中,調用的是哪一個delete以及調用時的參數,爲什麼?19.       D* pd1 = new D; 20.       delete pd1;21.       B* pb1 = new D;22.       delete pb1;23.       D* pd2 = new D[10];24.       delete[] pd2;25.       B* pb2 = new D[10];26.       delete[] pb2;27.     28.      //問題3:下面兩個賦值語句合法嗎?29.       typedef void(B::*PMF)(void*, size_t); 30.       PMFp1 = &B::f;31.       PMF p2 =&B::operator delete;32.     }3.4 內存分配失敗問題

A 內存分配失敗的報告方式:大多數new通過拋出bad_alloc異常來報告分配失敗;nothrownew則通過C中malloc方式報告失敗,即僅返回空指針,永遠不會拋出異常。

B 內存分配失敗的處理過程:通過set_new_handler調用錯誤處理函數new_handler,檢查指向函數的指針,若指針非0,則指向的函數被調用。

new_handler與set_new_handler的原型如下:

typedef void (*new_handler)();

new_handler set_new_handler(new_handler p)throw();

new_handler是一個typedef,表現出一個函數指針,該函數沒有參數也沒有傳回值,而set_new_handler是一個函數,需要一個new_handler參數並傳回一個new_handler。

Set_new_handler的參數指針指向的函數正是當operatornew無法配置足夠內存時,應該去調用的函數,其傳回值是一個函數指針,指向先前登陸過的new_handler。用法示例如下:

void nomorememory()

{

      cerr << "unable to satisfy request for memory\n";

      abort();

}

int main()

{

      set_new_handler(nomorememory);

      int *pbigdataarray = new int[100000000];

      ...

}

 

發佈了14 篇原創文章 · 獲贊 19 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章