引子
最近在写一个有关链表的操作例子,需要计算两个链表的数值之和,并完成链表后移操作,但是在调试过程中发现,自己编写的函数无法实现链表元素后移,每次都是计算的 Head List(即,总是指向ListHead头部)。对应的代码如下:
//define a List struct.
struct ListNode {
ListNode(int x) : val(x), next(NULL) {}
int val;
ListNode * next;
};
//Implement the sum of NodeLists value and pointer to next ListNode.
int sumTwoListVal(ListNode* left, ListNode* right, int& carry)
{
int sum = left->val + right->val + carry;
if (sum >= 10) carry= 1;
else carry= 0;
left = left->next;
right = right->next;
return sum % 10;
}
后面修改为指针的引用(*&)就可以完成需要的功能,还是基本功不够扎实,并未深刻理解指针和引用,首次书写的时候居然没有意识到这点。特此记录下来,并重新梳理下指针的引用场景。
指针的引用
我们先用例子熟悉一下指针的引用定义:
int val = 10;
int *ptr = &val; //define a pointer
int ref = &val; //define a reference
int *& ptrRef = ptr //define a pointer reference
//function declaration
void fooPtr(int *ptr_val);
void fooPtrRef(int *&ptrRef_val);
//call function
fooPtr(ptr);
fooPtrRef(ptr);
当我们把一个指针作为参数传递给一个函数或者方法时,其实是把指针的副本(copy)传递给了函数,即把指针的值本身传递到ptr_val。其实质还是属于值传递。
因此,当我们在函数或者方法内部修改指针(注意,不是修改指针所指向的值)时,其实修改的是函数内部指针的副本(copy),而非外部的指针本身。还是接用知乎大神们的图示看一下(尽管不是很清晰,但懒的重新画了,有时间再更新)。
下图是普通指针变量方式(即调用 fooPtr(ptr) ),此时外部传入的实参 ptr 的地址可以认为是1000,形参 ptr_val 的地址是 1004,他们指向的内存单元数值都是10。当修改函数内部形参 ptr_val 地址的时候,实参 ptr 的地址是不会改变的。
图示存粹是为了理解方便,与文中描述有差异,请注意对比
我们再看看指针的引用调用方式(即调用 fooPtrRef(ptr) ),此时外部传入的实参 ptr 的地址还认为是1004,形参 ptr_val 的地址也是 1004,他们指向的内存单元数值都是10。根据引用的定义,此时 ptrRef_val 就是实参 ptr 的别命。因此当修改函数内部形参 ptrRef_val 地址的时候,实参 ptr 的地址也会随之改变。
图示存粹是为了理解方便,与文中描述有差异,请注意对比
引用场景
当函数调用时需要修改指针的指向,或者函数调用结束后指针所指的这块内存会发生改变的时候,就需要使用指针的引用,或者指针的指针。
举例1(修改指针的指向):
- 如上面因子中提出的,当使用链表做参数,如果只是类似遍历查找链表元素的时,此时链表并未做任何修改,可以使用普通指针遍历方式;
- 若是增加、删除、修改链表操作,意味着传入的节点指针(一般多为HeadNode* )可能发生改变,此时就需要要使用 指针的引用。
开头的例子正确定义方式如下:
int sumTwoListVal(ListNode*& left, ListNode*& right, int& carry)
举例2(子函数内存改变):
常见的就是调用 malloc 分配一块内存空间,常见的面试题中都会碰到,这里不在赘述,只把例子放在这里。
面试中长碰到的试题:
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
void foo(char *p)
{
p = (char *)malloc(sizeof(char) * 100);
}
int main()
{
char *str = NULL;
foo(str);
strcpy(str, "hello world"); //it will be segment fault here.
printf("%s\n", str);
}
提供的解题思路一般是要使用二级指针,代码如下,:
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
void foo(char **p)
{
*p = (char *)malloc(sizeof(char) * 100);
}
int main()
{
char *str = NULL;
foo(&str); // Notice here is str address.
strcpy(str, "hello world"); //it will be OK
printf("%s\n", str);
}
当然,看完本篇,推荐你使用指针的引用这种方式,毕竟看起来更为清晰。
# include <iostream>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
void foo(char *&p)
{
p = (char *)malloc(sizeof(char) * 100);
}
int main()
{
char *str = NULL;
foo(str); //It still using str here not it's address.
strcpy(str, "hello world");
printf("%s\n", str);
}
参考
https://blog.csdn.net/matrix_google/article/details/77543192
https://zhuanlan.zhihu.com/p/139543762