指標作為形參進行傳遞注意事項
一個例子
#include<iostream>
using namespace std;
int m_value = 1;
void func(int *p)
{
p = &m_value;
}
int main(int argc, char *argv[])
{
int n = 2;
int *pn = &n;
cout << *pn << endl;
func(pn);
cout << *pn << endl;
return 0;
}
執行結果:
2
2
修改func函式如下
void func(int *p)
{
/*p = &m_value;*/
*p = 3;
}
執行結果:
2
3
修改func函式如下:
void func(int *p)
{
/p = &m_value;/
//*p = 3;
int a = 3;
*p =a;
}
依然是2,3
修改func函式如下:
void func(int *p)
{
/*p = &m_value;*/
//*p = 3;
int a = 3;
p =&a;
}
結果是2,2
解釋:
編譯器總是要為函式的每個引數製作臨時副本,指標引數p的副本是 _p,編譯器使 _p = p。
如果函式體內的程式修改了_p所指向的內容,就導致引數p的內容作相應的修改。把_p所指的記憶體地址改變了,那麼 _p指向的記憶體塊,和p指向的記憶體塊就不一樣了。所以對p毫無影響。只要程式裡不改變 _p的指向, _p和p就是一樣的,操作 _p內容,就是操作p的內容。
下面常用的交換程式:
#include <stdio.h>
int *swap(int *px, int *py){
int temp;
temp = *px;
*px = *py;
*py = temp;
return px;
}
int main(void){
int i = 10, j = 20;
int *p = swap(&i, &j);
printf("now i=%d j=%d *p=%d\n", i, j, *p);
return 0;
}
結果:
now i=20 j=10 *p=20
進一步思考
函式裡面分配記憶體
void myMalloc(char *s) //我想在函式中分配記憶體,再返回
{
s = (char *)malloc(100);
}
void main()
{
char *p = NULL;
myMalloc(p); //這裡的p實際還是NULL,p的值沒有改變,為什麼?
if (p)
{
cout << "p不為空" << endl;
free(p);
}
else
cout << "p為空" << endl;
}
結果
p為空
看另一個例子:
void myMalloc(char **s) //我想在函式中分配記憶體,再返回
{
*s = (char *)malloc(100);
}
void main()
{
char *p = NULL;
myMalloc(&p); //傳遞的是p的地址,這裡的p可以得到正確的值了
if (p)
{
cout << "p不為空" << endl;
memcpy(p, "Hello",6);//如果拷貝5個的話,最後一個'\0'沒有拷貝過去,輸出p會出現Hello,燙燙燙...
strcpy(p, "hello");
cout << p << endl;
free(p);
}
else
cout << "p為空" << endl;
}
結果:
p不為空
hello
解釋:
1.被分配記憶體的是形參s,p沒有分配記憶體
2.被分配記憶體的是形參s指向的指標p,所以分配了記憶體
第三個小例子:
void GetMemory(char *p, int num)
{
p = (char *)malloc(sizeof(char) * num);
}
void Test(void)
{
char *str = NULL;
GetMemory(str, 100); // str 仍然為 NULL
strcpy(str, "hello"); // 執行錯誤
}
道理一樣。
所以要想借助函式進行分配記憶體,需要傳遞的是“指標的指標”,就是第一個例子。
void GetMemory2(char **p, int num)
{
*p = (char *)malloc(sizeof(char) * num);
}
另外還可以:用函式返回值來傳遞動態記憶體
char *GetMemory3(int num)
{
char *p = (char *)malloc(sizeof(char) * num);
return p;
}
void Test3(void)
{
char *str = NULL;
str = GetMemory3(100);
strcpy(str, "hello");
cout<< str << endl;
free(str);
}
輸出結果:hello
注意這裡函式返回值傳遞的動態記憶體,必須是在“堆上”申請的記憶體!!!
不要用return語句返回指向“棧記憶體”的指標,因為該記憶體在函式結束時自動消亡
char *GetString(void)
{
char p[] = "hello world";
return p; // 編譯器將提出警告
}
void Test4(void)
{
char *str = NULL;
str = GetString(); // str 的內容是垃圾
cout<< str << endl;
}
但是下面的又會讓人疑惑:
char *GetString2(void)
{
char *p = "hello world";
return p;
}
void Test5(void)
{
char *str = NULL;
str = GetString2();
cout<< str << endl;
}
這個能執行,輸出:hello world
函式Test5執行雖然不會出錯,但是函式GetString2的設計概念卻是錯誤的。因為GetString2內的“hello world”是常量字串,位於靜態儲存區,它在程式生命期內恆定不變。無論什麼時候呼叫GetString2,它返回的始終是同一個“只讀”的記憶體塊。
char *pp = "wocao!";
pp[0] = '1';//這樣賦值是錯誤的。
所以上述測試時:
多加一句是會報錯的
void main(void)
{
char *str = NULL;
str = GetString2();
cout << str << endl;
str[0] = '1';//報錯
}
<高質量C C++程式設計指南>這本書上說: 指標p 指向常量字串(位於常量儲存區),常量字串的內容是不可以被修改的,企圖修改常量字串的內容而導致執行錯誤。所以這個問題出現的原因是char*p=”abcdefghi”,賦值的是字串常量,儲存在常量儲存區,而常量儲存區的內容是無法修改的。
如果使用陣列來代替的話,資料就儲存在堆疊空間,堆疊空間的內容是可以修改的,就不會出現執行時錯誤。
另外再加一個小總結:
程式1:
void myMalloc(char *s) //我想在函式中分配記憶體,再返回
{
s=(char *) malloc(100); // s是值參, 函式返回後就回復傳遞前的數值,無法帶回分配的結果
}
這個和呼叫 void func (int i) {i=1;}; 一樣,退出函式體,i指復原的
程式2:void myMalloc(char **s)
{
*s=(char *) malloc(100); // 這個是可以的
}
等價於
void int func(int * pI) {*pI=1;} pI指標不變,指標指向的資料內容是變化的
值參本身不變,但是值參指向的記憶體的內容發生了變化。
程式3:
void fun(int *p)
{
int b=100;
p=&b; // 等同於第一個問題, b的地址並沒有被返回
}
程式4:
void fun(int *p)
{
*p=100; // okay
}
結論:
1.函式的返回值是指標型別的,檢查是靜態記憶體指標還是堆記憶體指標還是棧記憶體指標,棧記憶體指標是絕對要不得滴!
2.函式需要使用指標引數進行傳入傳出的,在函式中只能對指標的指向的值(*p)進行修改,而不能修改指標指向,也就是指標地址!(函式中不得修改指標引數的地址,否則請使用指標的指標!)
一般在函式中定義一個物件有兩種方法:
1、在棧上建立區域性變數。注意,在棧上時!棧用於函式是為了返回時找得到呼叫點(在呼叫時壓入棧的)
,那麼,返回時要POP才能得到。函式體中建立的任何東西都消失了(返回值除外),你返回的指標指向的內
容現在不知被用作什麼用途了,如果你還要修改的話,那麼後果不能確定。
2、在堆中分配。返回時不會摧毀,因為堆是全域性存在的。但函式的呼叫者要記得delete回來的指標。
指標的引用
void func(int *&p)
{
p = &m_value;
// 也可以根據你的需求分配記憶體
/*p = new int;
*p = 5;*/
}
int main(int argc, char *argv[])
{
int n = 2;
int *pn = &n;
cout << *pn << endl;
func(pn);
cout << *pn << endl;
return 0;
}
結果是:2,1
此時傳遞過去就是指標p,因為引用!
看一下func(int *&p)方法
p: 是指標的引用,main()方法裡的 *pn
*p:是main()方法裡的pn指向的內容。