函式傳回指標
傳回值不能是區域變數的位址
傳回的記憶體位址,不能是函式中區域變數的記憶體位址,因為函式呼叫完畢後,函式記憶體空間與區域變數的記憶體會被系統回收。
注意!這邊說的記憶體回收並不是值被清空,而是多出一塊空餘記憶體空間,可以給其它變數或字串或建立物件使用。
下面的程式碼,func()函式傳回區域變數n的記憶體位址。
main()函式中,p指標變數接收函式回傳的記憶體位址。
印出"test test"。
接下來p指標(記憶體位址)使用*取值運算子,把記憶體位址的值取出來,請問輸出結果會是什麼?
1
2
3
4
5
6
7
8
9
10
11
12
int* func() {
int n = 100;
return &n;
}
int main() {
int* p = func();
int n;
printf("test test\n");
n = * p;
printf("%d\n",n);
return 0;
}
test test
-610123577
輸出結果並非100,而是-610123577。
使用static靜態變數
靜態變數不會隨著函式呼叫完畢,記憶體位址被系統回收。
靜態變數會存在data segment。
把func()函式中的n變數設為static。
1
2
3
4
5
6
7
8
9
10
11
12
int* func() {
static int n = 100;
return &n;
}
int main() {
int* p = func();
int n;
printf("test test\n");
n = * p;
printf("%d\n",n);
return 0;
}
test test
100
輸出結果是印出100,而不是亂七八糟的數字。
記憶體位址由參數傳入
函式不負責建立記憶體位址空間,比如下面的程式,由main()函式建立變數記憶體空間,再由參數傳入func()函式,c1、c2的生命周期會隨著main()函式執行完畢一起結束,而不是隨著func()函式而結束。
1
2
3
4
5
6
7
8
9
10
11
12
13
char* func(char* c1, char* c2) {
if (strlen(c1) > strlen(c2))
return c1;
else
return c2;
}
int main() {
char c1[100] = "Hello World!";
char c2[100] = "ABCDEF";
char* c = func(c1, c2);
cout << c << endl;
return 0;
}
Hello World!
使用動態配置記憶體空間
動態配置記憶體空間,儲存的位址是在Heap,不會隨著函式呼叫完畢,記憶體位址被系統回收。
但動態配置記憶體需要自行delete 指標,並把指標設為nullptr,因為c++不會在離開main()函式後,自動清除動態配置記憶體。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int* func() {
int* n = new int(50);
return n;
}
int main() {
int* p = func();
int n;
printf("test test\n");
n = * p;
printf("%d\n",n);
delete p;
p = nullptr;
return 0;
}
test test
50
使用全域變數
隨著整個程式結束,全域變數才會被記憶體回收。
傳回陣列記憶體位址
以下程式碼,在func()函式建立區域變數arr,是錯誤的。
1
2
3
4
5
6
7
8
int* func() {
int arr[5] = {1, 2, 3, 0, 0};
return arr;
}
int main() {
int* p = func();
return 0;
}
正確作法如下:
使用動態配置記憶體
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
using namespace::std;
int* func(int size) {
int* arr = new int[size] {1, 2, 3, 0, 0};
return arr;
}
int main() {
int size = 5;
int* p = func(size);
// 使用original 記錄最初的陣列位址,後面要刪除,因為p是會移動
int* original = p;
for (int i = 0; i < size; i++) {
cout << * p << ", ";
p++;
}
cout << endl;
// 使用original 記錄最初的陣列位址,後面要刪除,因為p是會移動
delete[] original;
original = nullptr;
return 0;
}
1, 2, 3, 0, 0,
使用static
使用靜態陣列有些麻煩,因為static是編譯時就要知道陣列大小,所以一定要用#define
來設定大小。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#define ARR_SIZE 5
int* func() {
static int arr[ARR_SIZE]= {1, 2, 3, 0, 0};
return arr;
}
int main() {
int* p = func();
for (int i = 0; i < ARR_SIZE; i++) {
cout << * p << ", ";
p++;
}
cout << endl;
return 0;
}
1, 2, 3, 0, 0,
回傳物件記憶體位址
以下程式碼使用動態分配記憶體,但要記得delete。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Student {
public:
const char* name;
int id;
const char* addr;
};
Student* createStudent() {
return new Student;
}
int main() {
Student* p = createStudent();
p->name = "Mary";
p->id = 10;
p->addr = "Taoyuan";
printf("name = %s , id = %d, addr= %s \n", p->name, p->id, p->addr);
delete p;
p = nullptr;
return 0;
}
name = Mary , id = 10, addr= Taoyuan
以下是錯誤的方式,因為student是區域變數,會隨著函式執行完畢,記憶體被回收。
1
2
3
4
Student* createStudent() {
Student student;
return &student;
}
字串回傳記憶體位址
以下是錯誤,在Stack中建立str字串陣列,函式func執行完畢,記憶體位址會被系統回收。
1
2
3
4
5
6
7
8
9
char* func() {
char str[100] = "Hello World";
return str;
}
int main() {
char* str = func();
cout << str << endl;
return 0;
}
以下是正確方式。
可使用RODATA區塊,也就是字串常數,不會因為函式結束而被記憶體回收。
1
2
3
4
5
6
7
8
9
const char* func() {
const char* str = "Hello World";
return str;
}
int main() {
const char* str = func();
cout << str << endl;
return 0;
}
Hello World
以上程式碼可以簡化如下:
1
2
3
4
5
6
7
8
const char* func() {
return "Hello World";
}
int main() {
const char* str = func();
cout << str << endl;
return 0;
}
字串記憶體位址可以由main函式(調用者),傳入記憶體位址參數給func1函式
1
2
3
4
5
6
7
8
9
10
11
12
13
char* func(char* c1, char* c2) {
if (strlen(c1) > strlen(c2))
return c1;
else
return c2;
}
int main() {
char c1[100] = "Hello World!";
char c2[100] = "ABCDEF";
char* c = func(c1, c2);
cout << c << endl;
return 0;
}
Hello World!
結論
傳回記憶體位址的方式如下:
- main()函式,傳入記憶體位址參數給函式()。
- 使用static靜態變數。
- 字串可以用RODATA區塊中的字串常數。
- 動態分配記憶體,要記得delete與nullptr。
- 全域變數。
- 使用「傳值」更安全。