c++用參數返回堆上的空間


《高質量c++和c編程》7.4 指針參數是如何傳遞內存的一節中寫道

void GetMemory(char *p, int num) 
{ 
  p = (char *)malloc(sizeof(char) * num); 
} 
void Test(void) 
{ 
  char *str = NULL; 
  GetMemory(str, 100);  // str  仍然為 NULL  
 strcpy(str, "hello"); // 運行錯誤 
}

無法返回內存,可以用如下方式

void GetMemory2(char **p, int num) 
{ 
  *p = (char *)malloc(sizeof(char) * num); 
}  
void Test2(void) 
{ 
  char *str = NULL; 
 GetMemory2(&str, 100); //  注意參數是 &str ,而不是str 
 strcpy(str, "hello");  
  cout<< str << endl; 
 free(str);  
}

個人的理解就是,實際上指針傳遞仍然是一種值傳遞,只不過在參數是指針的時候,傳遞的是指針的副本,這樣你在地址上的操作實際就反映到了內存中,舉個例子來說,假設有一個函數

void fun(int  *p)
{
  p = new int;      
}

當用調用時fun(q),會產生實參的一個副本設為_p,函數體為副本_p分配了內存,實際上並未改變實參p,這就是GetMemory沒有成功的原因。相反,如果我們有如下函數

void fun(int *p)
{
  *p = 3;  
}

在這個函數中,當發生實參調用的時候,仍然會產生實參的副本,但是注意這里不是改變副本,而是改變副本指向的內存中的內容,這里p是一個整形指針,在內存中占四個字節,副本和實參指向同一片內存,所以當你

在以副本為地址的內存內賦值3,實際也就是改變了實參指向的內存中的內容。

總結一下就是:指針傳遞仍然是值傳遞,所以我們在函數體內只有操作*p才會達到我們的指針傳遞要求,而不是操作p,這樣操作只在副本上,實際並不反映到實參指向的內存。

書中另一種方法是:

char *GetMemory3(int num) 
{ 
  char *p = (char *)malloc(sizeof
 return p; 
}  
void Test3(void) 
{ 
  char *str = NULL; 
  str = GetMemory3(100);  
 strcpy(str, "hello"); 
  cout<< str << endl; 
 free(str);  
} 

實際是將堆的特性和return相結合,堆上分配的內存在函數不會釋放,而return實際返回的p的一個副本,但是這里的副本是一個指針,簡單的說是一個內存地址,而這個地址在函數結束后並沒有釋放,所以,我們可以繼續

使用。如果是普通的局部變量,return返回它的一個副本,隨后局部變量隨着函數的結束而被釋放,這在某些時候會引起麻煩,比如

char *GetString(void) 
{ 
  char p[] = "hello world"; 
 return p; // 編譯器將提出警告 
} 
void Test4(void) 
{ 
char *str = NULL; 
str = GetString(); // str  的內容是垃圾 
cout<< str << endl; 
}

至於return似乎還有東西說,一時想不起。。。

事情總有例外,今天小妞找我調試程序,發現了一件很奇特的事情,看代碼

void getArray(char **s, int N)
{
    std::ifstream in("test.txt");
    if (!in.is_open())
    {
        std::cout<<"error"<<std::endl;
    }
    int i = 0;
    while(i < N)
    {
        s[i] = (char*)malloc(sizeof(char)*100);
        in.getline(s[i], 100);
        ++i;
    }
    in.close();

}
int main()
{
    char **s = (char **)malloc(sizeof(char) * 4);
    getArray(s, 4);
   for (int i=0; i<4; i++)
   {
       std::cout<<s[i]<<std::endl;
   }
    return 0;
}

這個程序可以正確編譯執行。而下面代碼

void getArray1(char **s, int N)
{
//    s = (char **)malloc(sizeof(char) * 4);
    s = new char*[4];
    std::ifstream in("test.txt");
    if (!in.is_open())
    {
        std::cout<<"error"<<std::endl;
    }
    int i = 0;
    while(i < N)
    {
//        s[i] = (char*)malloc(sizeof(char)*100);
        s[i] = new char[100];
        in.getline(s[i], 100);
        ++i;
    }
    in.close();

}

int main()
{
    char **s;
    getArray1(s, 4);
    for (int i=0; i<4; i++)
    {
        std::cout<<s[i]<<std::endl;
    }
    return 0;
}

這個代碼確實在運行時出錯

分析了一下,個人認為雖然兩個函數的參數都是char **s,但是一個在main()中先分配,一個直接在getArray中分配,原因就在於此,getarray函數在main函數中先分配了內存,然后傳遞給它,雖然仍然是值傳遞,但是

s的元素是指針,getarray函數中在main函數分配的內存上完成了操作,所以當函數結束時,所有操作仍然保留下來。getarray1函數不同,它是在函數體內完整分配內存,然后施加操作的,相當於都在副本上,所有操作都不會

在函數結束后保留下來。

這是個人的一點理解,如有不對的地方還請指教。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM