最近焦虑着找工作的事情,所以也来刷一刷 leetcode 上面的题目,想了想还是用比较基础的C语言,先做简单的题,不做不知道,一做就发现了一些C语言知识上的盲点,指针这里是让所有C语言学习者又爱又恨的地方了。
题目非常简单,就是用指针定义一个数组,然后给数组进行加一的操作,最后返回加一之后的数组即可,由于长时间也没有使用C编程,所以开始跑的时候就出了一些问题,leetcode 的在线编译器又无法调试,因此就在自己的本地跑,新的问题就出现了,那就是需要写 main 函数,当然这个就是引发问题的地方,因为最后子函数都在 leetcode 上运行成功了,但是在本地跑的时候还是结果不对,这里就给刚才遇到的坑一个总结。
Part 1 指针,局部变量和全局变量
这是刷 leetcode 就开始遇到的疑惑,题目中往往给出如下的一段程序1
2
3
4
5/**
* Return an array of size *returnSize.
* Note: The returned array must be malloced, assume caller calls free().
*/
int* plusOne(int* digits, int digitsSize, int* returnSize)
我在开始刷的时候很长一段时间都以为 return 的内容应该是 *returnSize ,按变量名的意思也就是最后数组的长度。。。所以怎么写程序就怎么觉得不对劲。于是我放弃了在线编译,转而在自己的电脑上写一个完整的程序。
抱着这样的想法,我开始写 main 函数,但是写着写着就发现不对,因为如果这个子函数最后的返回值是数组的长度,那么我们是无法直接获取输出的,然而它题目的示例是这样的1
2
3输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。
想法一
所以我否定了最初的想法,转而去想是否返回值为 *returnSize,但是修改的是已经定义好的那个 digits 数组呢?我用这个思路也写了一下,发现 leetcode 上依然没有通过,orz。。。
这种想法基本是可以否定的,因为这个数组的大小是有可能变化的,如果变化的话,需要重新给 digits 这个数组 malloc 空间,但是这个数组很明显是在 main 函数中就已经 malloc 好了空间的,在子函数里面再重新分配一次必然会有冲突。
想法二
由于之前也稍微见过这种要求返回数组的题目,想了一想,记得最后在子函数里返回的是指向数组的一个指针,并非什么数组的大小,但是转念一想?这个 *returnSize 要怎么返回呢?这样一个数组,一个大小,这俩值总不能做个数组一起返回吧,233333
至此就要提出这个题给我补上的第一个知识点了
指针的“全局性”
这样说当然是不妥当的,所以我在“全局性”这三个字上加了引号,为了解释清楚我做了一个小实验1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53源码
#include <stdio.h>
#include <stdlib.h>
int c = 1;
void af(int i)
{
i = i + 1;
}
void bf1(int* i)
{
*i = *i - 1;
}
void bf2(int* i)
{
int j = 2;
i = &j;
}
void cf1()
{
c = c + 1;
}
void cf2()
{
c = c - 1;
}
int main()
{
int a = 1;
int* b;
b = &a;
printf("origin a value: %d\n",a);
af(a);
printf("current a value: %d\n",a);
printf("**********************************\n");
printf("origin b value: %d\n",*b);
printf("origin b address: %d\n",b);
bf1(b);
printf("current b1 value: %d\n",*b);
printf("current b1 address: %d\n",b);
bf2(b);
printf("current b2 value: %d\n",*b);
printf("current b2 address: %d\n",b);
printf("***********************************\n");
printf("origin c value: %d\n",c);
printf("origin c address: %d\n",&c);
cf1();
printf("current c1 value: %d\n",c);
printf("origin c1 address: %d\n",&c);
cf2();
printf("current c2 value: %d\n",c);
printf("origin c2 address: %d\n",&c);
return 0;
}
上面的程序非常的简单,大家应该不需要解释都能看懂,结果如下
对程序运行分析可以得出:指针并非全局变量,只是作为参数传递时,修改的是其指向的内容,因此,表现出来伪“全局性”
Part 2 C语言指针赋值
了解上面的问题之后,我快速地在 leetcode 上完成了代码的书写并测试通过,本身题目逻辑很简单,但是在本地测试竟然还是有问题?我非常地不服
下面为 main 函数的内容1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22int main()
{
int size;
printf("please put in the array size:");
scanf("%d",&size);
int* digits = (int*)malloc(size*sizeof(int));
for(int i=0;i<size;i++){
scanf("%d",&digits[i]);
}
int* returnSize;
returnSize = &size;
//int* digit2 = (int *)malloc((size+1)*sizeof(int));
//int* digit2;
// = (int *)malloc((size+1)*sizeof(int));
//printf("%d",(int *)plusOne(digits,size,returnSize)[0]);
int * digit2 = (int *)plusOne(digits,size,returnSize);
printf("%d result:\n",(* returnSize));
for(int j=0;j<size;j++){
printf("%d",digit2[j]);
}
return 0;
}
由于一直出不来结果,只能到处加输出找错误的位置,最后发现1
int *returnSize = size;
这样一句话出了问题,那么问题在哪儿呢?就在这个赋值
例子如下1
2int *p;
*p = 7;
则编译器(vs2008)会提示The variable ‘p’ is being used without being initialized.即使用了未初始化的变量p。
因为p是指向7所在的地址,*p = 7给p所指向的内存赋值,p没有赋值,所以p所指向的内存位置是随机的,没有初始化的。
即returnSize初始化出了问题导致出错。