1. 指針作為函數參數
// 計算數組中所有數據元數之和
// 其中,參數pArray 和nCount分別表示數組的首地址和數組元素的個數,
// 用于向函數傳入一個數組,
// pSum指向用于保存結果數據的變量,用于從函數中傳出計算結果
void SumArray(int* pArray, int nCount, int* pSum )
{
// 參數有效性檢查…
int nRes = 0; // 結果數據
// 循環遍歷整個數組,計算所有數據元數的和
for( int i = 0; i < nCount; ++i )
{
// 通過pArray指針訪問它所指向的函數外的nArray數組,
// 讀取其中的數據并累加到nRes中,實現向函數內傳入數據
nRes += *pArray;
pArray++; // 指針加運算,訪問數組中的下一個元素
}
// 通過pSum指針訪問它所指向的函數外的nArraySum變量
// 將結果數據寫入這個變量,實現向函數外傳出數據
*pSum = nRes;
}
int main()
{
// 保存結果數據的變量
int nArraySum = 0;
// 需要統計的數組
int nArray[5] = { 1, 2, 3, 4, 5 };
// 使用數組的首地址nArray傳入數組,
// 使用指向變量nArraySum的指針來接收計算結果
SumArray(nArray, 5, &nArraySum);
// 運算結果已經保存在nArraySum中,直接輸出運算結果
cout<<"數組中所有數據之和是 :"<<nArraySum<<endl;
return 0;
}
在主函數中,我們將數組nArray 的首地址和指向保存結果數據的變量nArraySum的地址作為實際參數傳遞給數組求和函數SumArray()。這樣在SumArray()函數中,我們就可以通過傳入的數組地址訪問整個數組,完成傳入數據的功能。在完成統計后,又可以利用pSum指針,將結果數據直接保存到它所指向的函數外用于保存結果數據的變量nArraySum中,完成傳出數據的功能。利用指針作為函數參數傳遞數據的本質,就是在主調函數和被調函數中,通過指向同一內存地址的不同指針訪問相同的內存區域,從而實現數據的傳遞和交換。圖7-3展示了指針作為函數參數訪問相同內存區域的過程。
2. 指針作為函數返回值
我們可以把指針當作一種基本數據類型,除了可以用它定義變量、作為函數參數之外,自然也可以作為函數的返回值類型。跟函數的參數傳遞一樣,函數的返回過程同樣也涉及到返回數據的拷貝,當我們需要從函數內返回某個比較大體積的數據,或者是返回數據不能被拷貝時,就可以采用返回指向這個數據的指針來代替返回數據本身。例如,在單件模式的getInstace()函數中,因為需要返回的對象不能在返回過程中被復制,所以就用指針作為它的返回值,從函數內返回指向這個對象的指針來代替返回這個對象本身:
// 以SalarySys*指針作為返回值
static SalarySys* getInstance()
{
if ( nullptr == m_pInstance )
m_pInstance = new SalarySys();
return m_pInstance;// 返回指向SalarySys對象的指針
}
這里需要特別注意的是,不能把一個指向函數內局部變量的指針作為返回值。這是因為函數內部定義的局部變量在函數結束后,其生命周期已經結束,內存會被自動釋放,這時它的內存地址是無意義的。如果這時仍將指向這個地址的指針作為函數返回值返回給主調函數,并在主調函數中訪問這個指針所指向的數據,將產生不可預料的結果。例如,如果前面的getInstance()函數是下面這個樣子,雖然能夠編譯通過,但是在運行的時候卻可能會產生非常嚴重的錯誤:
// 錯誤的getInstance()函數
static SalarySys* getInstance()
{
if ( nullptr == m_pInstance )
{
// 定義一個局部變量sys
SalarySys sys;
m_pInstance = &sys; // 獲得局部變量的指針
}
return m_pInstance;// 返回指向局部的sys對象的指針
}
int main()
{
// …
// 獲得的指針指向getInstance()函數內的局部變量sys
SalarySys* p = SalarySys::getInstance();
// 局部變量sys已經被銷毀,對它的訪問是無意義的
p->Input();
//…
}
在主函數中,我們通過getInstance()函數獲得的指針指向的是函數內部的一個局部對象sys,當函數調用結束后,這個對象就會被自動銷毀。如果這時仍然通過這個指針試圖訪問這個已經被銷毀的對象,其結果是不可預料的,有可能正確,也有可能錯誤。而這恰恰使得這個錯誤具有極大的隱蔽性,時而正確時而錯誤,很難被發現。而要消滅這個錯誤的最好方法就是,牢記下面的規則:以指針為返回值的函數可以返回用new全新申請的內存地址;可以返回全局變量的地址;可以返回靜態變量的地址,但就是不可以返回局部變量的地址。