內存四區模型
討論這個問題之前,一定要理解堆區和棧區的工作原理,數據的存儲區域(參考內存四區模型),另外一定不要返回局部對象或變量的引用和指針。
局部變量
局部變量分局部自動變量和局部靜態變量,由於c返回的是值,因此返回一個局部變量是可以的,無論自動還是靜態,因為這時候返回的是這個局部變量的值。另外,函數返回局部變量時實際上是返回變量值的拷貝。a為局部變量,在棧區存儲,雖然在函數調用結束后所在內存會被釋放回收掉,但返回值不是訪問地址,而是a的拷貝副本。
1 int INTtest(){ 2 int a = 10; 3 return a; 4 }
局部指針
局部指針跟上面所述的局部變量一樣。可以返回一個局部指針的值,也可以返回一個局部靜態指針的地址,但不應該返回一個局部自動指針的地址,除非自動指針的地址指向數據區或堆區。
返回值為局部指針,可以分為:(1)聲明局部變量,返回其地址;(2)聲明局部數組,返回數組名;(3)聲明局部指針,返回該指針。
1.返回局部變量地址,局部變量分為自動和靜態局部變量,不應該返回指向局部自動變量的指針,因為函數調用結束后棧上聲明的局部自動變量被拋棄,這個指針指向一個不再存在的對象,是無意義的。但可以返回指向局部靜態變量的指針,因為靜態變量存在數據區,它的生存期從定義起到程序結束。
2.返回數組名,與局部變量相同(自動和靜態),調用的結果指向棧上聲明的數組的首地址,函數結束后自動數組內存釋放掉,將無法對其進行訪問(某些編譯器Release下可以訪問,但理論上是沒有意義的),但靜態數組可以。
3.返回指針,調用結果指向該指針指向的內存,在函數結束后在棧上聲明的指針也會被釋放掉,但應該注意原指針指向的內存地址,若改地址同樣是棧上聲明的,則無法訪問,如果是數據區或堆區的內容,則可以訪問。
1和2比較容易理解,這里不再進行舉例,僅對第3種情況的數據區和堆區進行說明。
數據區:返回指針,但指針指向的內存地址存儲在數據區(常量區)
1 char * test(){ 2 char *arr = "hello world"; //字符常量 3 //static char arr[] = "hello world!"; 4 return arr; 5 } 6 7 void test_(){ 8 char *s = test(); 9 printf("s = %s\n", s); 10 } 11 12 int main(){ 13 test_(); 14 return 0; 15 }
堆區:返回指針,指針指向的內存地址存儲在堆區,沒有手動釋放前都可以訪問到。
1 char *getstring(){ 2 char *p; 3 p = malloc(100); 4 memset(p, 0, 100); 5 strcpy(p, "hello"); 6 7 return p; 8 } 9 10 void test(){ 11 char *ret = getstring(); 12 printf("%s\n", ret); 13 free(ret); 14 ret = NULL; 15 } 16 17 int main(){ 18 19 test(); 20 21 system("pause"); 22 return 0; 23 }
如果指針指向的內存是在函數內部申請的,基本不干返回指針這種事,做這種事情很有可能是在給自己找麻煩,多數做的都是在可以釋放的地方申請好內存,通過指針傳遞進函數,申請和釋放都在同一函數中。上面的函數,可以修改如下:
1 void getstring01(char *p){ 2 strcpy(p, "hello"); 3 } 4 5 void test01(){ 6 char *p = NULL; 7 p = malloc(100); 8 memset(p, 0, 100); 9 //char *ret = NULL; 10 getstring01(p); 11 printf("%s\n", p); 12 free(p); 13 p = NULL; 14 } 15 16 17 int main(){ 18 test01(); 19 return 0; 20 }