C++函數返回指針和引用的坑
常用C++進行項目開發的童鞋們應該都知道,在C++中指針和引用是常用的語法了,而指針又是C++區別于其他高級語言的一大精髓。
而今天我們再來看看在C++新手們針對指針和引用的使用經常犯的錯誤。
函數返回指針
在C++中針對一個函數返回指針的實現方式一般有三種:
1.返回一個變量的地址
例如以下代碼:
// 返回int指針地址
int * funTest(){
int a = 101;
return &a;
}
int main(int argc, const char *argv[]) {
int *a = funTest();
std::cout << "a的值:" << *a << std::endl;
return 0;
}
以上代碼在筆者的電腦上運行就直接報錯崩潰了,崩潰信息:
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
上面的代碼返回一個局部變量a的地址,這個變量a緊在函數funTest內有效,當函數funTest結束了,變量a的生命周期也就結束了, 此時變量a所占用的內存空間將被釋放,因此返回的指針地址將會被指向一個未知數,后續再使用這個指針是未定義的行為,可能會導致程序崩潰或者出現其他異常。
針對這樣的危險代碼行為,其實編輯器也已經給出了警告,所以說在開發過程中也不要以為的忽略警告哦。
為了杜絕此類行為的發生,還可以使用筆者之前的介紹的代碼質量檢測工具cppcheck進行檢測,在開發過程中直接劃線提醒。
介紹一款CPP代碼bug檢測神器。
2.返回一個使用static修飾的變量地址
我們修改一下funTest函數的變量a,使用static關鍵字修飾一下:
// 返回int指針地址
int * funTest(){
static int a = 101;
return &a;
}
int main(int argc, const char *argv[]) {
int *a = funTest();
std::cout << "a的值:" << *a << std::endl;
return 0;
}
運行發現程序并沒有崩潰,而且是正確打印出了變量a的值。這是因為 使用static 表示將這個變量存儲到全局區(static靜態區), 此時就不受棧區管控,當函數funTest執行完畢后,變量a依然存在,不會存在前面所說的變量地址被釋放的問題。
3.使用動態分配內存new關鍵字
int * funTest(){
//動態分配的內存空間,手動delete后才會釋放
int* a = new int(101) ;
return a;
}
int main(int argc, const char *argv[]) {
int *a = funTest();
std::cout << "a的值:" << *a << std::endl;
return 0;
}
上述代碼不會崩潰,也能正常運行,但是存在一個隱患就是返回的指針變量a如果忘記調用delete則會造成內存泄露, 這就引發了一個指針變量誰維護銷毀的問題。一般默認規則是誰開發維護。
因此,針對這樣的場景,筆者的建議是智能指針你值得擁有...
函數返回一個引用
我們看看以下返回一個引用的例子代碼:
int & funTest(){
//動態分配的內存空間,手動delete后才會釋放
int a = 101 ;
return a;
}
int main(int argc, const char *argv[]) {
int a = funTest();
std::cout << "a的值:" << a << std::endl;
return 0;
}
筆者在CLion上測試也是直接崩潰了,原因也是和上面所說的返回一個局部變量的地址一樣, 都是因為函數funTest結束后,變量a的生命周期結束了, 變量a也就是被釋放了,再返回它的引用的話就是未定義的。至于為什么它們的原因是一樣的呢?因為所謂引用,可以簡單地理解為引用其實就是帶const修飾的指針。
那么針對這個問題該如何修正呢?首先使用static關鍵字肯定是可以的。那么使用動態內存new的方式行不行呢?答案也是可行的,但是需要注意的一點就是如果一個引用 的值來源于一個指針,后來這個指針被delete掉了,那么再使用這個引用也是會造成崩潰的...
如何返回一個數組
那么問題來了,舉一反三,如果想通過一個函數返回一個數組那該如何實現呢?
眾所周知,C++是不允許直接返回一個數組的,如果您想要從函數返回一個一維數組,您必須聲明一個返回指針的函數。
例如下面的寫法是編譯不通過的:
// 無法編譯通過,不能返回一個數組
int[] funTest(){
int myArray[3] = {1, 2, 3};
return myArray;
}
正確的寫法應該是:
int* funTest(){
static int myArray[3] = {1, 2, 3};
return myArray;
}
因而可以看出,其實返回一個數組的函數所遇到的坑其實就轉換成了返回一個指針的函數所遇到的坑,這些坑的舉例就如前面所說...