二级指针
格式: **p
定义: 这是一个指针的指针,保存指针变量的地址,也称为二级指针;搭配指针数组使用。
动态内存分配
定义: 数组定义时,数组长度是预先定义好的,整个程序中固定不变。但实际编程中,往往需要根据实际情况申请空间。C语言提供了一些内存管理函数,可以通过需要动态分配内存空间,也可以把不再使用的空间回收。
静态分配:
1.在编译或运行时,按事先规定的大小分配空间,比如int a[10],系统自动分配
2.必须事先指定所需空间大小
3.分配在栈区或全局变量区,一般以数组形式
4.按计划分配
动态分配:
1.在程序运行中,根据需要大小自由分配,需要程序员自己分配
2.分配在堆区,一般使用特定函数进行分配
3.按需分配
使用的函数:
1.malloc函数: 开辟出一片连续的内存空间,需要手动初始化
格式:指针变量 = malloc(元素类型字节大小*元素个数)
例子:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
int *p;
p = (int *)malloc(sizeof(int)*10); //用malloc申请一个存10个int型元素的数组
if(p == NULL)
{
printf("error\n");
}
memset(p, 0, 40); //memset函数对申请的空间初始化
int i;
for(i = 0; i < 10; i++)
{
p[i] = i;
printf("%d\n",p[i]);
}
free(p); //如果没有释放申请的空间,会造成内存泄漏,内存泄漏就是只申请不释放
p = NULL; //将p指向空,防止p成为野指针
return 0;
}
注:1.开辟一块内存空间,开辟的大小以字节为单位。位置在堆上
2.使用sizeof()求出类型的大小*元素个数
3.返回值是已开辟好空间的首地址,所以必须用同类型指针变量接受
4.malloc开辟的内存是连续的
5.多次调用malloc,开辟的空间不一定连续
2.free函数
格式:free(指针变量)
3.calloc函数: 开辟一片连续的内存空间,自动初始化为0
格式:指针变量 = calloc(元素个数,元素类型所占的字节数)
例子:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
int *p;
p = (int *)calloc(20,sizeof(int));//calloc()函数等价于malloc()和memset()
if(p == NULL)
{
printf("error\n");
}
int i;
for(i = 0; i < 20; i++)
{
printf("%d\n",p[i]);
}
free(p);
p = NULL;
return 0;
}
注:1.开辟一块内存空间,开辟的大小以字节为单位。位置在堆上
2.第一个参数是元素的个数,第二个参数是元素所占字节数
3.返回值是已开辟好空间的首地址,所以必须用同类型指针变量接受
4.calloc开辟的内存是连续的
5.多次调用malloc,开辟的空间不一定连续
4.realloc函数: 重新分配内存空间大小,主要是进行扩展内存空间
格式:指针变量 = realloc(已分配内存指针变量,重新分配空间大小的字节数)
例子:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
int *p;
p = (int *)malloc(sizeof(int)*10); //用malloc申请一个存10个int型元素的数组
if(p == NULL)
{
printf("error\n");
}
memset(p, 0, 40); //memset函数对申请的空间初始化
int i;
for(i = 0; i < 10; i++)
{
p[i] = i;
}
p = realloc(p, 100); //realloc用来对空间进行扩容,原来的数据保留,新增加的空间里面的值是随机的
if(p == NULL)
{
printf("error\n");
}
for(i = 0; i < 25; i++)
{
printf("%d\n",p[i]);
}
printf("**********\n");
memset(p+10, 0, sizeof(int)*15); //将扩容后的部分赋值为0
for(i = 0; i < 25; i++)
{
printf("%d\n",p[i]);
}
free(p); //如果没有释放申请的空间,会造成内存泄漏,内存泄漏就是只申请不释放
p = NULL; //将p指向空,防止p成为野指针
return 0;
}
注:1.存在bug,如果开辟失败,会导致指向位置丢失
2.如果p指向的空间之后有足够的内存空间可以追加,则直接追加,返回p
3.如果p指向的空间之后没有足够的内存空间可以追加,则realloc函数会重新找一个新的内存区域,开辟一块
满足需求的空间,并且把原来空间内存中的数据拷贝回来,释放旧的内存空间,并返回新开辟的内存空间
的地址
4.如果开辟失败,得用一个新的变量来接受realloc函数的返回值
内存泄漏
定义:
1.只申请不释放
2.申请的内存首地址丢了,找不到了,指向了其他的地址,再也没有办法使用,也没有办法释放。
指针与数组关系
a+1 => a是一维数组名的话,意味着向后移动一个元素
a+1 => a是二维数组名的话,意味着向后移动一行元素(每次移动一个一维数组)
&a+1 => a是二维数组名的话,意味着向后移动整个数组(每次移动一个二维数组)
a[i] => a是二维数组名的话,代表着二维数组中每个一维数组的首地址
&a[i] => a是二维数组名的话,代表着二维数组名a
a、a[0]、a[0] [0] => 在二维数组中,三者的地址相同。a可以看做是一个存储了一维数组首地址的指针
注:如果a是一维数组名,a为一级指针。如果a是二维数组名,a是二级指针。如果a是二维数组名,&a是三级指针。一级指针加一移动一个元素,二级指针加一移动一个一维数组,三级指针加一移动一个二维数组。