下面的內容是在C專家編程里面看到的,摘錄於此。
在C語言中,局部變量的作用域只在函數內部,在函數返回后,局部變量的內存就會被釋放。如果函數只是返回局部變量,那么這個局部變量會被復制一份傳回被調用處。但是如果函數返回的是局部變量的地址,那么就會報錯,因為函數只是把指針復制后返回了,但是指針指向的內容已經被釋放,這樣指針指向的內容就是不可預料的內容,程序就會出錯。准確的來說,函數不能通過返回指向棧內存的指針(返回指向堆內存的指針是可以的)。
先來看一個函數返回局部變量的例子
-
-
-
int fun()
-
{
-
int num = 100;
-
num = num + 100;
-
-
return num;
-
}
-
int main()
-
{
-
int num;
-
-
num = fun();
-
-
printf("%d\n", num);
-
-
return 0;
-
}
fun函數返回一個int的局部變量,函數會把局部的num的值復制一份拷貝給主函數里面的num。這樣是可以的,而且這種方式在程序里面還是經常用到的。上面程序輸出:200
下面函數返回局部變量地址
-
-
-
char *fun()
-
{
-
char buffer[20];
-
int i;
-
for(i = 0; i < sizeof(buffer)-1; i++)
-
buffer[i] = 'a';
-
buffer[i] = '\0';
-
return buffer;
-
}
-
int main()
-
{
-
char *str;
-
str = fun();
-
printf("%s\n", str);
-
return 0;
-
}
編譯運行:
編譯警告程序返回局部變量地址,輸出為亂碼。因為fun返回的是局部變量的地址,真是拷貝了一份地址,地址所指向的內容在fun結束的時候已經釋放,變量已經被銷毀,現在根本不知道地址指向的內容的是什么。
如果確實要返回一個局部變量的地址應該怎么做,解決這個問題有下面幾種方案。
1、返回一個字符串常量的指針
-
-
-
char *fun()
-
{
-
char *buffer = "aaaaaaaaaa";
-
return buffer;
-
}
-
int main()
-
{
-
char *str;
-
str = fun();
-
printf("%s\n", str);
-
return 0;
-
}
編譯運行
這樣程序運行是沒有問題的,buffer存在只讀內存區,在fun退出的時候,字符串常量不會被收回,因此把地址賦給str時可以正確訪問。上面這個方式只是最簡單的解決方案,因為字符串存放在只讀內存區,以后需要修改它的時候就會很麻煩。
2、使用全局聲明的數組。
-
-
-
char buffer[20];
-
-
char *fun()
-
{
-
int i;
-
for(i = 0; i < sizeof(buffer)-1; i++)
-
buffer[i] = 'a'+ i;
-
buffer[i] = '\0';
-
return buffer;
-
}
-
int main()
-
{
-
char *str;
-
str = fun();
-
printf("%s\n", str);
-
return 0;
-
}
編譯運行
這種情況適用於自己創建字符串,而且簡單容易。缺點就是任何人都有可能在任何時候修改這個全局數組,而且該函數的下一次調用會覆蓋數組的內容。
3、使用靜態數組
-
-
-
char *fun()
-
{
-
int i;
-
static char buffer[20];
-
for(i = 0; i < sizeof(buffer)-1; i++)
-
buffer[i] = 'a'+ i;
-
buffer[i] = '\0';
-
return buffer;
-
}
-
int main()
-
{
-
char *str;
-
str = fun();
-
printf("%s\n", str);
-
return 0;
-
}
編譯運行
使用靜態數組可以保證內存不被回收,而且可以防止任何人修改這個數組。只有擁有指向該數組的指針的函數才能修改這個靜態數組,不過同時該函數的下一次調用會覆蓋數組的內容。同時和全局數組一樣,大型緩沖區閑置是非常浪費空間的。
4、顯示的分配內存,在堆上動態分配內存。
-
-
-
-
char *fun()
-
{
-
int i;
-
char *buffer = (char *)malloc(sizeof(char) * 20);
-
strcpy(buffer, "abcdefg");
-
return buffer;
-
}
-
int main()
-
{
-
char *str;
-
str = fun();
-
printf("%s\n", str);
-
return 0;
-
}
這個方法具有靜態數組的方法,而且每次調用都創建一個新的緩沖區,不會覆蓋以前的內容。適用於多線程代碼。缺點是程序員必須承擔內存的管理,這項任務可能很復雜,很容易產生內存泄露或者尚在使用就被釋放。
5、最好的解決辦法就是調用者分配內存來保存函數的返回值,同時指定緩沖區的大小
-
-
-
-
void fun(char *str, int size)
-
{
-
char *s = "abcdefghijklmnopq";
-
strncpy(str, s, size);
-
}
-
int main()
-
{
-
int size = 10;
-
char *str = (char *)malloc(sizeof(char) * size);
-
fun(str, size);
-
printf("%s\n", str);
-
free(str);
-
return 0;
-
}
編譯運行
程序員在同一塊代碼中同時進行malloc和free操作,內存管理最安全,方便。