写C代码的时候,总免不了跟指针打交道。尤其是把指针当函数参数传进去的时候,一不小心就踩了坑。比如你写了个函数想修改某个变量的值,结果跑完发现原变量纹丝不动,八成是没搞明白指针传递的门道。
传值和传址的区别
假设你要交换两个数,写了个函数:
void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}
调用后发现主函数里的变量根本没变。原因很简单:这是值传递,函数拿到的是副本。想要真正改到原来的变量,得传地址进去。
用指针做参数才能改外面的值
正确的做法是传指针:
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
调用时传地址:swap(&x, &y);。这样一来,函数里通过指针解引用操作,就能直接改到外面的变量。
常见错误:只想改指针本身
有时候你会遇到这种情况:函数里给指针重新赋值,想让它指向别的内存,结果调用完发现指针还是原来那样。比如:
void reassign_ptr(int *p) {
p = (int *)malloc(sizeof(int));
*p = 100;
}
这个函数看似分配了内存,但实际上外面的指针根本没变。因为p是形参,只在函数内有效。要想改变指针本身,得传二级指针:
void reassign_ptr_correct(int **p) {
*p = (int *)malloc(sizeof(int));
**p = 100;
}
调用时传 &my_ptr,这样才算真正改到了原始指针。
实际场景:读串口数据存到缓冲区
比如你在做端口映射相关的程序,要从串口读数据存进缓冲区。函数可能长这样:
int read_serial_data(int port_id, char *buffer, int max_len) {
// 从指定端口读数据,填入buffer
// 返回实际读取长度
...
return bytes_read;
}
这里buffer是指针,函数通过它往外部内存写数据。如果传NULL进来,程序可能直接崩。所以调用前得确保buffer指向合法内存。
别忘了空指针检查
写这种函数时,最好加个判断:
if (buffer == NULL) {
return -1;
}
不然用户一不小心传个空指针,程序一运行就段错误,查半天才发现是参数问题。
数组名传参的本质也是指针
当你把数组传进函数,其实传的是首元素地址:
void process_array(int arr[], int size)
等价于:
void process_array(int *arr, int size)
所以在函数里对arr的操作,直接影响原数组。但sizeof(arr)这时候就不再是数组长度了,而是指针大小,这点容易让人迷糊。