2015年2月8日 星期日

指標作為函式回傳值可能碰到的問題

剛學到C++字元陣列可以用字元指標做傳遞時,很可能會再做寫函式時忽略記憶體的問題,導致字串資料遺失,以下用幾個小程式來說明:

return_pointer_1.cpp
01 /*file name: return_pointer_1.cpp*/
02 
03 #include<iostream>
04 #include<cstdio>
05 using namespace std;
06 char* test()
07 {
08     int i;
09     char a[5] = "A";
10     char *ptr = a;//ptr 指向的 a 是函式是屬於區域變數,回到主程式後記憶體被釋放
11     cout << "函式內:\n" << "值: ";
12     for(i = 0; i < 5; i++)
13       printf("%d ", *(ptr+i));
14     cout << endl;
15     cout << "址: ";
16     for(i = 0; i < 5; i++)
17       printf("%p ", (ptr+i));
18     cout << endl;
19     return ptr;
20 }
21 int main()
22 {
23     char *name;
24     name = test();
25     int i;
26     cout  << "回傳後:\n" << "值: ";
27     for(i = 0; i < 5; i++)
28       printf("%d ", *(name+i));
29     cout << endl;
30     cout << "址: ";
31     for(i = 0; i < 5; i++)
32       printf("%p ", (name+i));
33     cout << endl;
34 }

執行結果:
函式內:
值: 65 0 0 0 0
址: 0042F9A4 0042F9A5 0042F9A6 0042F9A7 0042F9A8
回傳後:
值: -1 -1 -1 -1 -26
址: 0042F9A4 0042F9A5 0042F9A6 0042F9A7 0042F9A8

從return_pointer_1.cpp可見,當要回傳的指標ptr指向的是一個函式內的區域變數,回傳後主程式收到的位址沒錯,但因為函式結束,a陣列被釋放掉了,所以主程式無法取得函式中a陣列的值。
如果想要回傳的字串不要再函式結束時被釋放掉,可以改用動態配置記憶體的方式:
01 /*file name: return_pointer_2.cpp*/
02 
03 #include<iostream>
04 #include<cstdio>
05 #include<cstdlib>
06 using namespace std;
07 char* test()
08 {
09     int i;
10     char a[5];
11     char *ptr = (char*)malloc(sizeof(a));//ptr 指到一塊動態配置的記憶體,沒有被釋放前資料不會消失
12     for(i = 0; i < 5; i++)//先把剛配置的記憶體的值歸零
13       *(ptr+i) = 0;
14     *(ptr+0) = 'A';
15     cout << "函式輸出: ";
16     for(i = 0; i < 5; i++)
17       printf("%d ", *(ptr+i));
18     cout << endl;
19     return ptr;
20 }
21 int main()
22 {
23     char *name;
24     name = test();
25     int i;
26     cout << "回傳後輸出: ";
27     for(i = 0; i < 5; i++)
28       printf("%d ", *(name+i));
29     cout << endl;
30     free(name);//^釋放前  ˇ釋放後
31     cout << "釋放後輸出: ";
32     for(i = 0; i < 5; i++)
33       printf("%d ", *(name+i));
34 }
執行結果:
函式輸出: 65 0 0 0 0
回傳後輸出: 65 0 0 0 0
釋放後輸出: 96 24 -39 0 -64

return_pointer_2.cpp中把ptr改成指向一個動態配置的記憶體,在釋放前資料不會消失,所以函式結束後在主程式能得到所配置的5 Bytes的資料。

對於return_pointer_1.cpp主程式無法得到函式中ptr指到的陣列的值,可以改成以址傳遞、無回傳值的方式:
01 /*file name: return_pointer_3.cpp*/
02 
03 #include<iostream>
04 #include<cstdio>
05 using namespace std;
06 void test(char *ptr)
07 {
08     int i;
09     for(i = 0; i < 5; i++)
10       *(ptr+i) = 0;//全部歸 0,用來比對主程式中的初始化
11     cout << "函式內:\n" << "值: ";
12     for(i = 0; i < 5; i++)
13       printf("%d ", *(ptr+i));
14     cout << endl;
15     cout << "址: ";
16     for(i = 0; i < 5; i++)
17       printf("%p ", (ptr+i));
18     cout << endl;
19 }
20 int main()
21 {
22     char a[5] = "HaHa";//用來比對呼叫函式之後的結果
23     test(a);
24     int i;
25     cout  << "回傳後:\n" << "值: ";
26     for(i = 0; i < 5; i++)
27       printf("%d ", a[i]);
28     cout << endl;
29     cout << "址: ";
30     for(i = 0; i < 5; i++)
31       printf("%p ", &a[i]);
32     cout << endl;
33 }
執行結果:
函式內:
值: 0 0 0 0 0
址: 0071FCA0 0071FCA1 0071FCA2 0071FCA3 0071FCA4
回傳後:
值: 0 0 0 0 0
址: 0071FCA0 0071FCA1 0071FCA2 0071FCA3 0071FCA4

return_pointer_3.cpp中,我們把a陣列的初始位置傳進函式,把值全部歸零,因為使用的記憶體空間是主程式的,所以在主程式結束前沒有生命週期的問題,跟return_pointer_2.cpp比起來,return_pointer_2.cpp是在函式內動態配置記憶體,return_pointer_3.cpp是使用主程式靜態配置的記憶體,都可以達到讓主程式得到正確的字串。

沒有留言:

張貼留言