最近在用VC6做一個項目,有的弟兄反映不太理解指針和引用的區別。從目前現況來看,現在的開發人員用慣了Java,C#等語言,距離操作系統底層越來越遠了,以至於大部分人對C/C++中的一些東西早已淡忘。
說到指針,凡是學過c語言的人都不陌生,這是是c語言的精華所在。指針操作不僅高效,而且非常接近系統底層,容易掌控。所以凡是使用c/c++編碼的技術人員,個人建議是首先花精力把指針搞清楚。當然,事情總是具有兩面性,有利必有弊,指針操作最為诟病的就是它的不安全性。除非你對它理解很透徹,否則難免會出現訪問越界、內存洩露以及不可預知的結果。
“引用”是C++中引入的概念,我們經常會看到“引用”應用在函數調用過程中。在C++語言中,調用一個函數時,參數傳遞可以采用3種方式:傳值、傳址和傳引用。
比如下面一段VC代碼:
//主程序代碼
CString strUserId = "001";
CString strUserName = "無名";
BOOL bFind = FindUserName(strUserId, strUserName);
if( bFind )
{
MessageBox("User Name:" + strUserName);
}
else
{
MessageBox("No found!");
}
//查找用戶函數
BOOL FindUserName(CString& strUserIdParam, CString& strUserNameParam)
{
if( strUserIdParam == "001" )
{
strUserNameParam = "王澤賓";
return TRUE;
}
return FALSE;
}
FindUserName函數中使用了兩個引用型參數。主程序代碼最終的運行結果是:彈出一個對話框,內容是“User Name:王澤賓”。函數中的“CString &”,表示參數使用“傳引用方式”。FindUserName中的strUserNameParam與主程序代碼中的strUserName是同一個對象,所以修改他們中的任何一個,都會反映到另外一個。strUserNameParam只不過是strUserName的別名而已,這非常類似於windows系統中文件的快捷方式,或者linux系統中文件的符號鏈接。
如果把查找用戶函數修改為:
//查找用戶函數
BOOL FindUserName(CString strUserIdParam, CString strUserNameParam)
{
if( strUserIdParam == "001" )
{
strUserNameParam = "王澤賓";
return TRUE;
}
return FALSE;
}
FindUserName函數中使用了傳值參數。主程序代碼最終的運行結果是:彈出一個對話框,內容是“User Name:無名”。函數中的“CString”,表示參數使用“傳值方式”。
FindUserName中的strUserNameParam與主程序代碼中的strUserName是完全不同的兩個對象,所以修改他們中的任何一個,都不會反映到另外一個上。strUserNameParam的內容復制了一份strUserName的內容,所以strUserNameParam又稱為strUserName的副本,這個復制操作是在函數調用過程中參數壓棧時執行的。
如果把查找用戶函數修改為:
//查找用戶函數
BOOL FindUserName(CString *pstrUserIdParam, CString *pstrUserNameParam)
{
if( *pstrUserIdParam == "001" )
{
*pstrUserNameParam = "王澤賓";
return TRUE;
}
return FALSE;
}
那麼主程序代碼中調用函數的地方需要略作修改:
BOOL bFind = FindUserName(&strUserId, &strUserName);
FindUserName函數中使用了傳址參數。主程序代碼最終的運行結果是:彈出一個對話框,內容是“User Name:王澤賓”。函數中的“CString *”,表示參數使用“傳址方式”。FindUserName中的pstrUserNameParam與主程序代碼中的pstrUserName是兩個完全沒有關系的指針變量,只是這兩個指針變量中存放的內容完全相同,都是指向同一塊內存空間的地址,所以修改他們中的任何一個指針變量所指向的內存空間,效果都是完全一樣的。pstrUserNameParam的內容復制了一份pstrUserName的內容,pstrUserNameParam是pstrUserName的副本,這個復制操作是在函數調用過程中參數壓棧時執行的。
通過以上例子,我們可以看到引用在作為函數參數傳遞時,具有指針的效果,但是卻可以象傳值的一樣使用,方式比指針簡單。當作為在參數時,通過指針和引用,都可以在函數體內操作直接改變他們所指向的地址空間裡面存儲的內容,無須編譯器為他們創建一個臨時變量復制並保存他們的值的一個副本。
指針和引用在C/C++使用方法上,略有不同:
1、指針的值可以為 NULL ,而引用不能為 NULL,你如果VC中直接定義變量CString& strVar1;或者CString& strVar1 = NULL;這是行不通的,編譯器無法通過語法掃描。編譯器要求定義引用變量時,必須指定初值。
2、一旦引用被初始化,就不能改變引用的關系指針則可以隨時改變所指的對象)。
以下示例程序中,k 被初始化為i 的引用。語句k = j 並不能將k 修改成為j 的引用,只是把k 的值改變成為6。由於k 是i 的引用,所以i 的值也變成了6。
int i = 5;
int j = 6;
int &k = i;
k = j; // k 和i 的值都變成了6;
所以,我們得出結論:引用實際上只是編程語言中語法層面的概念,經編譯後實際執行的代碼中與指針沒有任何區別。引入這個概念後帶來好處有兩條:
1、代碼簡潔,使用方便。可以對比一下上面的例子。
2、部分消除指針使用的不安全。由於從語法層面就要求必須它初始化,所以不會出現空指針操作這樣的危險情況。
Java,C#語言的一個優點就是取消了指針的概念,從而從語法層面就消除了C/C++語言的不安全性,這對於構建健壯的大型應用非常有益。它們對於函數調用的參數傳遞采用兩種方式:對於簡單類型的參數實行傳值方式,對於對象類型的參數實行傳引用方式。
以Java為例:
class ObjUser{
String strName = "init value";
public String toString(){
return strName;
}
}
public class Test{
ObjUser User = new ObjUser();
int Age = 60;
public void changeObj(ObjUser user){
user.strName = "changed value";
}
public void changeInt(int age){
age = 80;
}
public static void main(String[] args)
{
Test objUser = new ObjUser();
System.out.println("==================Print User=================");
System.out.println("Before call changeObj() method: " + objUser.User);
oRef.changeObj(objUser.User);
System.out.println("After call changeObj() method: " + objUser.User);
System.out.println("==================Print Age=================");
System.out.println("Before call changeAge() method: " + Age);
oRef.changePri(objUser.Age);
System.out.println("After call changeAge() method: " + Age);
}
}
運行結果:
==================Print User=================
Before call changeObj() method: init value
After call changeObj() method: changed value
==================Print Age=================
Before call changeAge() method: 60
After call changeAge() method: 60
在Java中如果要實現對象傳值方式,可以先借助於clone()方法先來復制對象,然後再調用函數。
C#的情形基本類似,php中也保留了傳值和傳引用兩種方式,跟C++的語法類似。
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/wanghao72214/archive/2009/07/24/4376158.aspx
本文出自 “網站架構之家” 博客,請務必保留此出處http://wangzebin.blog.51cto.com/653300/182452