概 述
刷过题的朋友肯定见过这个 ** returnColumnSizes,或者是* returnSize. 说实话我看到*returnSize的时候是理解的,但是一到了二级指针就止不住的懵逼,在自己研究讨论了两个小时之后,我想明白了。如有疑问请打脸。
其实 ** 的使用,和交换两个整数的 swap 函数是同一个道理。
使用 returnSize 和 returnColumnSizes 的原因
我的理解是,你返回一个数组的时候,只能返回它的指针,但是官方检测的时候到底要检查多少个数据呢?智能检测也太耗资源了,所以需要你告诉它,我前 returnSize 个数据是有用的,后面的数据只是我防止溢出多创建的,让它检查这么多个就好啦。
首先大家要知道这个 returnColumnSizes 在主函数中应该是一个一维数组,每个元素代表了当前排有多少个有效的列,一般这种题会有一个配套的 returnSize 代表共有多少排,这个一维数组,配合着排的总数,就可以让 Leetcode 后台去检查你的答案了。
swap函数
所有指针的入门教学,都会用一个交换函数来告诉你指针的必要性,
如果你对此耳熟能详,请直接跳到下一个分割线。
void swap(int x, int y) {
int temp;
temp = x;
x = y;
y = temp;
}
int main(void)
{
int x = 1;
int y = 0;
printf("x is %d, y is %d\n", x, y);
swap(x, y);
printf("x is %d, y is %d", x, y);
}
如果我们只是把主函数的 x,y 传进 swap 函数里面,
那么虽然它们在交换函数中被改变了,但是在主函数中并不会改变。
只有你把地址传进去,通过解引用的方式才能够成功改变两个数。
void swap(int *x, int *y) {
int temp;
temp = *x;
*x = *y;
*y = temp;
}
int main(void)
{
int x = 1;
int y = 0;
printf("x is %d, y is %d\n", x, y);
swap(&x, &y);
printf("x is %d, y is %d", x, y);
}
第一种方法不能改变x,y,是因为在把它们传进函数swap的时候,
其实是复制了他们的值给swap中的参数,而他们本身什么也没做。
第二种方法可以成功交换x,y,是因为系统在保存 x,y的内存地址上面,
直接对他们在内存上储存的值进行了操作。
众生平等,指针也一样
参考swap函数,如果我们把一个指针传进一个函数中,然后想要去改变这个指针上面保存的地址的时候,主函数的指针保存的地址同样不会改变。
下面用一段代码来解释:
void change(int *ptr);
int main(void)
{
int *p = NULL;
change(p);
printf("%p", p);
}
void change(int *ptr) {
ptr = 0xff00; // 给 ptr 赋值一个地址,在函数中,ptr确实会改变,但离开函数块后又会变回去
}
如果你在这里开启调试模式,你会发现,原函数中的ptr的地址,并不会因为进入change()而被改变。
那么实际的leetcode主函数到底是怎么写的呢?我写了一个小例子,在函数中使用了 **
void change(int **ptr);
int main(void)
{
int *p = NULL;
change(&p);
for (int i = 0; i < 10; i++) {
printf("%d\n", p[i]);
}
}
void change(int **ptr) {
int *res = (int *)malloc(sizeof(int) * 10);
for (int i = 0; i < 10; i++) {
res[i] = i;
}
*ptr = res;
}
我们可以看一下change的最后一条代码:
*ptr = res;
请问ptr是啥,ptr不就是你传进来的地址?
回主函数找一下,哦~~,原来ptr就是 &p 啊,那翻译过来不就是:
*(&p) = res;
这不就等于 p = res; 嘛,
相当于我在主函数里建立了一个一级指针,但是我不知道它该指向哪里,
答案里面建立了一个一维数组,我现在告诉主函数里面的一级指针,你指向我这个res就好了。
我不想这么麻烦,行吗? 不行!
我要是不想这么麻烦,不想用
*(&p) = res;
可以么? 为什么我不直接把主函数中的一级指针直接传进去,然后在直接把 res 赋值给 ptr 呢?
反正 *(&p) = res; 和 p = res; 完全等价嘛!!!
代码我都给你改好了:
void change(int *ptr);
int main(void)
{
int *p = NULL;
change(p);
for (int i = 0; i < 10; i++) {
printf("%d\n", p[i]);
}
}
void change(int *ptr) {
int *res = (int *)malloc(sizeof(int) * 10);
for (int i = 0; i < 10; i++) {
res[i] = i;
}
ptr = res;
}
我可以保证这个程序不会如你想的一样运行,原因在上个分割线,众生平等,指针也一样 那里解释的很清楚。
指针上面,保存着一个地址。当你把指针传入一个函数中的时候,你可以改变这个地址上面保存着的 值 ,但是你没办法改变 原函数 中,这个指针保存的地址。
可能听起来有点抽象,之后我学一下iviso画个图再来补充下就很容易懂了。
那么我们怎么样才能把,这个指针 上面保存的地址,在其他函数中直接给改了呢?简单啊,你把这个指针的地址传进去不就行了。
如果你把 &p 传入 一个 参数是 ** 的函数,那么你就可以通过
*(&p) = res;
这条代码改变了啊。同样,你传入的这个 &p 也永远没法 改变原函数中的 &p,但是你改变了原函数中的 p。
如果讲到这里,你还是不懂,那就别看了,自己去动手,把我上面的代码都运行一下,调试一下,你就懂了,光看还是有点难理解的。
指针误区
很多同学可能看完我写的东西产生了质疑,指针不就是用来改变值的么?怎么就不行了呢?
那么我们看一下下面指针改变值的成功例子。
void change(int *p) {
p[0] = 5;
p[1] = 6;
p[2] = 7;
}
int main(void) {
int *p = (int *)malloc(sizeof(int) * 3);
for (int i = 0; i < 3; i++) {
p[i] = i;
printf("%d ", p[i]);
}
change(p);
printf("\n");
for (int i = 0; i < 3; i++) {
printf("%d ", p[i]);
}
}
这里确实成功改变了,但是我们改变的是,指针p上面保存的地址,在内存中储存的值,而不是改变了指针p上保存的地址。
如果你在change中加一句 p = 0xff000; 那么依然是没办法改版 原函数 中 p的地址的。