在力扣做题,发现给的函数是char ** fizzBuzz(int n, int* returnSize)
这种类型的,也就是返回的是一个二级指针
题目
给你一个整数 n ,找出从 1 到 n 各个整数的 Fizz Buzz 表示,并用字符串数组 answer(下标从 1 开始)返回结果,其中:
answer[i] == "FizzBuzz" 如果 i 同时是 3 和 5 的倍数。
answer[i] == "Fizz" 如果 i 是 3 的倍数。
answer[i] == "Buzz" 如果 i 是 5 的倍数。
answer[i] == i (以字符串形式)如果上述条件全不满足。
示例 1:
输入:n = 3
输出:["1","2","Fizz"]
示例 2:
输入:n = 5
输出:["1","2","Fizz","4","Buzz"]
代码
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
char ** fizzBuzz(int n, int* returnSize)
{
char **ans;
ans=(char **) malloc (sizeof(char*)*n);
for(int i=1;i<=n;i++)
{
if(i%3==0&&i%5==0)
{
ans[i-1]="FizzBuzz"; // ans才是二级指针,ans[i-1]中储存的是指针,字符串可以直接赋值给指针;
}
else if(i%3==0)
{
ans[i-1]="Fizz"; // 字符串常量不需要分配内存,指针储存了它的地址;
}
else if(i%5==0)
{
ans[i-1]="Buzz";
}
else
{
ans[i-1]=(char**)malloc(sizeof(char*)); // 不是字符串常量,需要分配地址和内存,需要malloc();
sprintf(ans[i-1],"%d",i);
}
}
*returnSize=n;
return ans;
}
以前用过的是int* twoSum(int* nums, int numsSize, int* returnSize)
这样的,这里int* twoSum()
代表函数返回一个指针(其实就是一个数组),nums
是在函数外声明的数组,numsSize
是数组nums
的元素个数,returnSize
是返回地址处储存元素的个数的地址。
为什么这么说?什么叫“地址处储存元素的个数”?
因为数组名和数组的首元素地址等价,如果return ans;
就等价于return &ans[0]
,所以函数如果返回一个指针,那这个指针有可能是指向一个单个值,也有可能指向一个数组。这里的returnSize
是一个指针,我们通过*returnSize=size
把这些信息传递出去。
所以可以看到,这个函数做到了“传入数组”和“返回数组”。
既然数组也可以视为指针,为什么要用malloc()
创建数组?
这涉及到变量的储存方式,简单来说:
- 如果直接用数组,那么这个数组的生命只有这个函数的大括号这么长,因为它是属于这个函数的局部变量。
- 如果使用
malloc()
,那么这个数组的生命将持续到内存被free()
释放或者main()
结束。
局部变量储存在“栈”中,这是由系统自行分配的,程序员无法插手,所以使用受到限制,但优点是速度快。用malloc()
分配的变量储存在“堆”中,这个内存是由程序员控制的,在没有使用free()
释放的情况下,将持续到程序结束。
所以我在初次学习malloc()中遇到的那句Note: The returned array must be malloced, assume caller calls free().
,其意思就是,不要使用局部变量,要使用malloc()
,这样力扣才能得到你储存的东西(这句话出现次数多是因为力扣总是给一个函数,而不是让自己写程序,所以只能考虑到函数的一些局限性)。
可是有一个问题,我在上个博客中说过,如果malloc()
和free()
不成对使用,那么就有内存泄漏的风险。那我在函数中是否也要成对写呢?如果成对写,内存还是在函数中就被释放,和局部变量无异。如果不成对写,又有内存泄漏的风险,怎么办?
不用担心,他告诉了assume caller calls free()
,也就是让你假设函数的调用者调用了free()
,所以你才必须使用malloc()
,因此不用在函数中使用free()
,不用担心内存泄漏的问题。
另:如果在函数内使用了free()
,会怎么样?有时还是会正常输出,但是这里储存的东西变成了未定义的。
回过头来,我们看一开始的返回值为二级指针的函数。
首先我们肯定还是要使用malloc()
,但是返回值为二级指针,写法如下:
char **ans = (char**) malloc(sizeof(char*)*size);
指针是储存某个值地址的变量,那么二级指针就是储存这些指针的地址的变量。
前面说过,指针可以看作是数组,只不过元素个数没有声明而已。那么二级指针,就可以看作是二维数组。
举个例子就能明白:
// 3个字符数组
char a[3]={'1','2','3'};
char b[2]={'4','5'};
char c[1]={'6'};
// 3个指向储存字符类型值的指针
char *a1,*b1,*c1;
// 储存数组的地址
a1=&a;
b1=&b;
c1=&c;
// 1个指向储存字符类型值的指针的指针
char **a2[3]={&a1,&b1,&c1};
// 这个二级指针a2的效果如下:
a2 -> a1 -> a = {'1','2','3'}
-> b2 -> b = {'4','5'}
-> c2 -> c = {'6'}