主要区别是函数形参的三种传递方式:值传递、指针传递和引用传递。
// bind 函数定义
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
// 函数调用
bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
上例中,定义时 addr 是指针形参(*),调用时 &serv_addr 取地址传入(&)。
指针传递
- 形参(函数定义时)是指针变量
- 实参(函数调用时)是变量的地址,或指针变量本身
- 调用时形参保存实参的地址,函数体内可通过形参指针修改实参所指内存的内容
函数定义和声明时,用 * 修饰形参,表示指针类型;函数调用时,用 & 修饰实参,表示传入该变量的地址。
引用传递
在形参类型后加 & 即可,调用时直接传变量,写法与值传递相同,无需 & 或 *。引用是实参的别名,语法上比指针更简洁。
值传递
形参是实参的拷贝,改变形参不会影响外部实参。从被调函数角度看,值传递是单向的(实参 → 形参),参数只能传入、不能传出。当函数内部需要修改参数,且不希望影响调用者时,采用值传递。
引用传递
形参是实参的别名,对形参的操作即对实参的操作。引用必须在定义时绑定到已有对象,不能为空,也不能重新绑定。
引用传递避免了拷贝开销,函数内对形参的修改会直接反映到实参上。
指针传递
形参为指向实参的指针,对形参解引用(*)的操作即对实参本身的操作。
class ConveyPoint {
public:
void convey1(int num);
void convey2(int &num);
void convey3(int *num);
};
void ConveyPoint::convey1(int num) {
cout << "值传递--函数操作地址 " << &num << endl;
num++;
}
void ConveyPoint::convey2(int &num) {
cout << "引用传递--函数操作地址 " << &num << endl;
num++;
}
void ConveyPoint::convey3(int *num) {
cout << "指针传递--函数操作地址 " << num << endl;
*num = *num + 1;
}
ConveyPoint cp;
int n = 10;
cp.convey1(n);
cout << "after change1() num=" << n << endl; // 10,未改变
cp.convey2(n);
cout << "after change2() num=" << n << endl; // 11,已改变
cp.convey3(&n);
cout << "after change3() num=" << n << endl; // 12,已改变
若函数需要修改参数并影响调用者,指针传递和引用传递都可以做到——本质是直接在实参的内存上修改,不像值传递那样先拷贝到另一块内存。
另一种常见用法:函数只能显式返回一个值,但需要带回多个结果时,可将额外变量以指针或引用传入,函数内修改后由调用者读取,相当于隐式多返回值。
声明和调用时的区别
&
声明时
用于创建引用,即变量的别名,绑定到另一变量,共享同一内存地址。
int a = 10;
int& ref = a; // ref 是 a 的引用
调用时
用于取地址,获取变量在内存中的地址。
int a = 10;
int* ptr = &a; // ptr 存储变量 a 的地址
*
声明时
声明指针变量,用于存储另一个变量的内存地址。
int a = 10;
int* ptr = &a; // ptr 是指向 int 的指针
调用时
解引用,访问指针所指向的对象。
int a = 10;
int* ptr = &a;
int value = *ptr; // value 为 10
评论