野指针:随机指向内存的一快内存,会导致内存泄漏。
指向一块不可访问的内存
内存泄漏原因:
指向一块已经释放的内存。
如何避免野指针?
1):当定义一个指针时,该指针没有指向,定义为空,也就是初始化,即
Int *p; × / *没有初始化*/
Int *p = NULL; √ /*指针的初始化*/
为什么NULL这么特殊呢,这是因为NULL是系统默认的宏定义,我们可以直接使用,
系统中是这样定义的:
#define NULL (void *)0
2):当要往指针指向的空间赋值时,我们应先分配内存空间(用malloc函数),分配时要
检验是否分配成功,成功时,我们就可以使用指针了,当指针使用完成时,注意,
别忘了要对申请的内存空间释放了,用free()函数。(毕竟用完了别人的东西我们还
得还给别人,呵呵),这样我们的指针又没有指定的内存给它访问了,因此,还得再
一次初始化。
* 注意指针必须为相同类型的指针才可赋值,否者会造成内存越界或取少(也就是
说步长要相同)
看下面的这个例子。(只有框架)
#define MAX_SIZE sizeof(char *) 100
Char *ptr = (char *)malloc(MAX_SIZE * sizeof(char)); //动态的分配内存
If(NULL == ptr) //检查分配的空间是否分配成功
{
Printf(“malloc error!\n”); //空间分配不成功
Exit(1);
}
//memset(ptr, ‘\0’ , sizeof(ptr) ); //清理分配的内存中的残留垃圾
Memset(ptr , 0 , MAX_SIZE); //上一条的优化,提高代码的拓展性
//bzero(ptr, MAX_SIZE); //只能初始化为0 功能没有memset强大
Free(ptr); //使用后释放内存
Ptr = NULL; //避免又成野指针
3): void * ptr : 俗称为万能指针,但只能保存地址,不能操作地址。
总的来说:只要养成良好的编程习惯,就能尽量的避免野指针的存在。
数组
一维数组:
定义一个整型的一维数组:(输入、输出)
int a[3];
int *p = a;
int i;
for(i = 0; i < 3; i++)
{
scanf(“ %s”, &a[i]); //此外还有a+i. p+i. &p[i]
printf(“ %d\n”,a[i]); //*(p+i). p[i]. *p(++)
}
&a:对一维数组的数组名取地址等于数组的地址。
*(&a):对一维数组的地址取值等于数组首元素的地址。
二维数组:
&a:对一维数组的数组名取地址等于数组的地址。
*(&a):对一维数组的地址取值等于数组首元素的地址。
二维数组:
int a[2][2]; //可以省略行号,不可省略列号。
int i;
int j;
for(i = 0; i < 2; i++)
{
for(j = 0; j < 2; j++)
{
scanf(“%d”, &a[i][j]);
printf(“a[%d][%d] = %d\n”,i, j, a[i][j]);
//scanf(“%d”,*(a+i) + j));
//printf(“a[%d][%d] = %d\n”,i, j, *(*(a + i) + j));
}
}
对于:*( * ( a + i ) + j) 拆开来找规律
a + i ; 第i + 1个一维数组的地址。
*(a + i): 第i + 1个一维数组的首元素的地址。
*(a + i) + j: 第i + 1个一维数组的第j + 1个元素的地址。
*(*(a + i) + j): 第i + 1个一维数组的第j + 1个元素的值。
*(&a) = a; 对二维数组的地址取值等于首个一维数组的地址。
三维数组:(同理)
*(*(*(a + i) + j) + k):
a + i: 第i + 1个二维数组的地址。
*(a + i): 第i + 1个二维数组的首个一维数组的地址。
*(a + i) + j: 第i + 1个二维数组的第j + 1个一维数组的地址。
*(*(a + i) + j): 第i + 1个二维数组的第j + 1个一维数组的首元素的地址。
*(*(a + i) + j) + k: 第i + 1个二维数组的第j + 1个一维数组的第k + 1个元素的地址。
*(*(*(a + i) + j) + k): 第i + 1个二维数组的第j + 1个一维数组的第k + 1个元素的值。
*(&a) = a;对三维数组的地址取值等于首个二维数组的地址。
拓展:当函数传参时,对每个数组的正确传法。
传递一维数组名时, 用元素指针来接收, 如:*ptr
传递二维数组名时,用二维数组指针来接收, 如:(*ptr)[ ] 标明具体多少列。
传递三维数组名时,用二维数组指针来接收。 如:(*ptr)[ ][ ] 标明行列号。
指针数组:
定义:int *pa[3];
int i;
for(i = 0; i < 3; i++);
{
pa[i] = (int *)malloc((sizeof(int)); //给指针分配内存
scanf(“%d”,pa[i]);
printf(“pa[%d] = %d\n”,i, *(pa[i]));
}
用法: 当传参为指针数组时,用指针的指针来接收。
如: print_ptr(ptr); --------> void print_ptr(char **ptr)
总结:指针与数组的区别
1);空间分配上: 数组:静态分配。 指针:动态分配。
2)访问效率上: 数组:直接访问。 指针:间接访问。
3)安全性上: 数组:易造成数组越界。 指针:易造成内存泄漏。
4)形参: 数组默认转化成指针
5)使用上: 数组操作的是值。 指针操作的是地址。
6)分配空间上: 数组空间连续。 指针:地址空间不一定连续
拓展:
二叉树的基本选择原则;
先序: 根 、 左树 、右树
中序:左树 、根 、右树
后序:左树 、右树 、根
堆排:如果堆的有序状态因某个节点变得比它的父节点更大而被打破,那么就需要通过交换
它和它的父节点来修复堆,从最后一个非节点逐渐往上浮,直到有序。
如: 打乱的堆(5 11 7 2 3 17) 利用堆排来初始化堆
5 5 17 17
11 7 11 17 11 5 11 7
2 3 17 2 3 7 2 3 7 2 3 5
最后得到堆(17 11 7 2 3 5)