主要区别是函数形参的三种传递方式:值传递指针传递引用传递

// 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

参考文章

[1] C++ 函数参数中 & 和 * 的意义

[2] C++ 值传递、指针传递、引用传递详解