函數傳參的原理和對象初始化的原理完全一致,用實參去初始化形參
函數傳參的兩種形式
1.按值傳參
按值傳參時,實參的拷貝用來初始化形參。
int main(int argc, char const *argv[])
{
int i=0;
paratest(i);
cout<<i<<endl;
return 0;
}
void paratest(int a)
{
++a;
cout<<__func__<<a<<endl;
}
對paratest函數的形參a做的所有操作都不會影響i的值,上述過程相當於如下過程
int a=i; ++a;
a和i是兩個不同的對象,a的變化並不會導致i的變化
傳指針參數也是按值拷貝的一種,只不過會改變指針指向的對象,不會改變指針本身
int main(int argc, char const *argv[])
{
int i=0;
int *pi=&i;
paratest(pi);
cout<<i<<endl;
return 0;
}
void paratest(int *pa)
{
*pa=10;
}
輸出結果顯示i的值爲10,這是因爲將i的指針的副本傳入paratest,導致pa也指向i,所以可以通過pa改變i的值,上述過程相當於如下過程
int *pi=&i; int *pa=pi;
第二行代碼導致pa和pi都指向i的地址,所以都可以操作i的數據,但是傳指針參數實質還是按值傳參
2.按引用傳參
將函數的形參聲明爲引用,此時向函數中傳參時,傳遞的是實參本身,而不是實參的拷貝,此時形參與實參綁定,形參只不過是實參的別名
int main(int argc, char const *argv[])
{
int i=0;
paratest(i);
cout<<i<<endl;
return 0;
}
void paratest(int &ra)
{
ra=10;
}
此時對引用形參操作,就是對實參操作
因爲傳遞的是實參本身,並沒有發生拷貝,省略了傳值過程中的拷貝過程,所以對於大的對象來說,效率更高
而且有些對象不支持拷貝操作,所以傳參時,必須使用引用形參
const與函數參數
當非引用或者指針的函數形參有const修飾時,傳遞const對象和非const對象都是OK的
int main(int argc, char const *argv[])
{
int i=0;
paratest(i);
const int ci=10;
paratest(ci);
return 0;
}
void paratest(const int a)
{
cout<<__func__<<a<<endl;
}
上述代碼可以編譯通過,但是不能在函數中修改a
函數const變量的指針或者引用的形參的初始化和const變量的指針或者引用的初始化規則完全一致,見知識點2,3
在聲明一個函數時,儘量使用常量引用來聲明形參
int main(int argc, char const *argv[])
{
string str="asd";
constreferencepara(str);
//constreferencepara("asd");
return 0;
}
void constreferencepara(string &str)
{
}
上述代碼的第5行必須註釋掉,否則會報錯
因爲一般的引用無法綁定字面值,但是const變量的引用可以,而且const變量的引用也可以綁定一般的變量,所以如果將函數constreferencepara的參數聲明爲const string &str,那麼就不會報錯
int main(int argc, char const *argv[])
{
string str="asd";
constreferencepara(str);
constreferencepara("asd");
return 0;
}
void constreferencepara(const string &str)
{
}
此時函數可以傳入參數的範圍變大,擴展性提高,而且傳遞引用不需要拷貝對象,效率提高,所以,建議使用const變量的引用作爲函數形參
數組形參
當使用值傳遞的方式傳遞數組參數時,數組參數會轉化爲一個指針,指向數組首元素地址,下列三種函數聲明都是等價的
void func(int *a);
void func(int a[]);
void func(int a[10]);
第三中聲明方式只是期望傳入的數組有10個元素,但是本質和前兩種完全一樣
當分別實現這三個函數時,編譯器會報重定義的錯誤
void func(int *a)
{
cout<<"pointer func"<<endl;
}
void func(int a[])
{
cout<<"array func"<<endl;
}
void func(int a[10])
{
cout<<"array func that include 10 ele"<<endl;
}
由於向函數中傳入數組時,函數接收到的只是數組首元素的指針,如果在函數中需要遍歷數組,需要人爲指定數組長度
int main(int argc, char const *argv[])
{
int a[10]={1,2,3,4,5};
int len=sizeof(a)/sizeof(a[0]);//計算數組長度
func(a,len);
return 0;
}
void func(int *a,int len)
{
for (int i=0;i<len;++i) {
cout<<a[i]<<endl;
}
}
這樣做不如使用vector簡潔,建議使用vector代替數組
傳遞數組的引用和指針
如果一個函數的形參是數組的引用,那麼必須指定數組長度,因爲引用要綁定某個具體的數組,不能綁定一個長度不確定的數組
void arrayreferpara(int (&arr)[]);
int main(int argc, char const *argv[])
{
int a[10]={1,2,3,4,5};
arrayreferpara(a);
return 0;
}
void arrayreferpara(int (&arr)[10])
{
for (int i=0;i<10;++i) {
cout<<arr[i]<<endl;
}
}
因爲使用數組的引用作爲形參時,傳入的數組參數的個數必須和形參數組的個數一致,所以這種接口的可擴展性不強
函數的形參如果是數組的指針也是同理,數組的長度必須指定,因爲數組的指針必須指向一個長度固定的數組
void arrayreferpara(int (*arr)[]);
通過數組的指針遍歷數組
int main(int argc, char const *argv[])
{
int a[10]={1,2,3,4,5};
arrayreferpara(&a);
return 0;
}
void arrayreferpara(int (*arr)[10])
{
for (int *p=arr[0];p!=arr[0]+10;p++) {
cout<<*p<<endl;
}
}
因爲形參是一個數組的指針,所以可以傳入一個數組的數組,也就是二維數組,道理和指針形參可以傳對應數組相同,下面三個的函數定義完全是一個意思
void arraypointerpara(int (*arr)[10])
{
}
void arraypointerpara(int arr[10][10])//第一個維度只是個期望值,可以填任意數
{
}
void arraypointerpara(int arr[][10])
{
}
所以會報重定義的錯,道理同一維數組
這三種形式中,數組形參的第二個唯獨必須確定,因爲二維數組依然是一個一維數組,只不過每個元素都是一個一維數組,每個元素必須確定,所以每個元素的維度必須確定
當傳入一個二維數組後,數組的指針指向二維數組的第一個一維數組
int main(int argc, char const *argv[])
{
int a[2][10]={1,2,3,4,5};
int dim=sizeof(a)/sizeof(int)/10;
arrayreferpara(a, dim);
return 0;
}
void arrayreferpara(int arr[][10], int length)
{
/*int num=sizeof(arr)/sizeof(int)/10;
cout<<num<<endl;*/
for (int (*ap)[10]=&arr[0];ap!=&arr[0]+length;ap++) {
for (int *p=ap[0];p!=ap[0]+10;++p) {
cout<<*p<<endl;
}
}
}
因爲函數的數組形參接收的是數組名,是一個地址,所以不要在函數內對數組形參做sizeof,否則會出警告
提示sizeof將返回數組指針的大小而不是數組的大小
參考:
《C++ Primer》
歡迎大家評論交流,作者水平有限,如有錯誤,歡迎指出