C/C++ 传递指针给函数完整教程
一、核心概念
- 普通传值:函数接收变量副本,函数内修改不影响外部原变量。
- 传指针(地址传递) :函数接收变量内存地址,通过地址可以修改外部原始变量,也能高效传递数组/大结构体(不用拷贝大量数据)。
二、语法规则
1. 函数形参声明指针
c
// 形参是int指针
void func(int *p);
2. 调用时传入变量地址 &变量
c
int a = 10;
func(&a); // &a 取出a的地址,传给指针形参p
3. 函数内部解引用 *p 操作原变量
*p 代表指针指向的内存数据(原变量)。
三、示例1:传指针修改外部变量(最常用)
C 代码
c
#include <stdio.h>
// 接收int指针
void change(int *num) {
// *num 等价于外部变量a
*num = 100;
}
int main() {
int a = 10;
printf("修改前:%d\n", a);
// 传递a的地址
change(&a);
printf("修改后:%d\n", a);
return 0;
}
输出:
修改前:10
修改后:100
对比:如果不传指针、只传值,外部 a 不会变。
四、示例2:传递数组(数组名本质是首元素指针)
数组传给函数会自动退化为指针,无需加 &:
c
#include <stdio.h>
// 数组等价于 int arr[] = int *arr
void printArr(int *arr, int len) {
for(int i = 0; i < len; i++) {
printf("%d ", arr[i]); // arr[i] 等价于 *(arr+i)
}
}
int main() {
int nums[] = {1,2,3,4,5};
int n = sizeof(nums)/sizeof(nums[0]);
printArr(nums, n); // 直接传数组名,就是首地址
return 0;
}
五、示例3:传递指针变量(指针本身作为实参)
如果已有指针变量,调用时直接传指针变量,不用再加 &:
c
#include <stdio.h>
void setVal(int *p) {
*p = 666;
}
int main() {
int x = 10;
int *p = &x; // p是指针,存x地址
setVal(p); // 直接传指针p,不用&p
printf("%d", x); // 666
return 0;
}
区分易错点
&x:变量x的地址(int*类型)p:指针变量本身,值就是&x&p:指针变量p自己的地址(int**二级指针)
六、二级指针:传递指针,修改指针本身
需求:函数内让外部指针指向新变量,需要传指针的指针 int**
c
#include <stdio.h>
// 二级指针:修改外部一级指针p
void pointNew(int **pp) {
static int b = 999;
*pp = &b; // 修改外部指针的指向
}
int main() {
int a = 10;
int *p = &a;
printf("原指向:%d\n", *p); // 10
pointNew(&p); // 传指针p的地址,二级指针
printf("新指向:%d\n", *p); // 999
return 0;
}
七、C++ 引用 vs 指针传参(对比)
- 指针传参:
void f(int *p) f(&a) - 引用传参(C++专属):
void f(int &r) f(a),底层仍是地址,语法更简洁。
cpp
#include <iostream>
using namespace std;
void f(int &r) {
r = 200;
}
int main() {
int a = 10;
f(a); // 不用取地址
cout << a;
return 0;
}
八、常见坑
-
野指针 :函数接收指针前判断是否为
NULLcvoid func(int *p) { if(p == NULL) return; // 空指针保护 *p = 10; } -
不要返回局部变量地址(局部变量函数结束销毁)
-
数组传指针后丢失长度,必须额外传长度参数
九、总结
- 普通变量传指针:实参写
&变量,形参类型 *p,*p修改原值; - 数组传参:直接传数组名,自动转为首元素指针;
- 要修改外部指针本身:使用二级指针
类型 **pp,实参传&指针变量; - 优势:避免大数据拷贝、跨函数修改变量。