多重指针
这个多重指针类似多维数组的那种情况,所以我们可以知道多重指针肯定和一维指针有很大的关系,然后我们来看下面这段代码:
#include<stdio.h>
int main()
{
int a = 1;
int* p ;
p = &a;
int* *p1 = &p;
printf("%d\n",*p);
printf("%p\n",&a);
printf("%p\n",&p);
printf("%ld\n",*p1);
/*这里输出的是a的地址,为啥呢?首先*p1
是指向p1所储存的地址,而p1储存的地址是
&p所以是指向&p,那么自然是输出p储存的东西
p储存的正是a的地址*/
printf("%d",**p1);
return 0;
}
/*
1
000000000061FE14
000000000061FE08
6422036,这个就是a的地址只不过这是2进制,上面是16进制
1
*/
其实在看多重指针的时候我们只需认准一个点,指针运算符一定是接地址的,所以我们完全可以用理解多维数组的方式来理解。
动态分配内存
前面在讲函数在调用后会释放掉自己的内存所以在作为指针函数返回函数的地址时我们说,函数内存被释放掉了所以返回的会是一个随机值,那么今天就来具体了解如何解决这一问题。
静态分配
在复习数组的时候我们说,不管怎么样你想要输入一个数组你总得要先确定这个数组的大小,且在C语言的执行过程中这个数组的大小是不能变的,但是世界是并没有那么多的事情可以和你预想的那样一成不变,有很多时候我们往往需要根据具体情况去调整我们所需要的内存。那么像数组那样事先就知道的内存或者定义变量,我们就称之为静态分配
int a;
int [2];
这样的就是静态分配,我们从一开始就知道它所占用的内存,且在执行的过程中不会改变。
动态分配
在使用过程中可以根据我们的需求自己去分配内存,这就是动态分配,下面介绍几个分配内存的函数
malloc
#include <stdlib.h>
malloc用来向系统申请size大小的内存(连续的),并把这段内存的
首地址返回(void*)
void *malloc(size_t size);
size : 你要分配多大的空间, 单位是字节,你要多少字节的空间.
返回值 :
成功 返回分配空间的首地址 void*
失败 返回NULL
void * 表示这东西是一个指针(地址),至于是什么类型的对象,他不关心.malloc开辟的空间地址肯定是连续的,这个连续是指每一次开辟的空间是连续的(多个连续地址),但不过,如果你用malloc连续开辟两个空间(意思是连续调用两次malloc函数),那么这两个空间的地址可能就不连续
其次,在你使用malloc的时候你肯定是不知道你所开辟的空间是用来搞什么的所以并不需要知道它的类型,我们用void*定义就行,但是在你接收这个空间的时候你需要什么样的类型就把它强转为什么类型就ok了
并不是每次你申请内存都会成功,如果内存池有那么多连续的空间那么你申请内存成功的话malloc函数的返回值就是这个连续空间的首地址,如果你没有成功他还是会返回一个NULL,所以在你使用了malloc之后一定要对返回值进行检查
malloc的参数是需要分配的内存字节数
#include <stdio.h>
#include <stdlib.h>
int main()
{
//int arr[10] = { 0 };
//动态内存开辟
int* p = (int*)malloc(40);
// if (p == NULL)//判断p是否为空指针
// {
// printf("%s\n", strerror(errno));
// return 1;//异常返回
// }
//使用内存
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;
printf("%d ", *(p + i));
/*这里很有意思阿,我们之前晓得数组名作为指针通过加减法可以
连续输入数组,这里不是数组为什么也可以呢?其实阿,通过指针加减法来输入其本质不就是
只要满足连续的地址不就ok了吗!*/
}
//没有free
//当程序退出的时候,系统自动回收内存空间
free(p);
p = NULL;
return 0;//正常返回
}
calloc
这个也是分配空间的函数,但是它和malloc还是有区别的,既然都是函数我们就先来看这两个函数在参数的要求上有什么区别,前面说到malloc的参数只要包含大小(也就是总共的字节数)就ok了,而calloc则是有两个参数,分别是所需元素的数量和元素的字节数,还有就是,你在使用malloc开辟的时候,开辟好的空间里面是没有东西的,而calloc,在开辟好了之后会把空间里面全部用0填满,也就是说calloc会初始化。
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p = (int*)calloc(10, sizeof(int));
// if (p == NULL)
// {
// printf("%s\n", strerror(errno));
// return 1;
// }
//打印
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
//释放
free(p);
p = NULL;
return 0;
}
/*
这样虽然我们啥也没有做,就只是分配好空间,但是当我们输出的空间所保存的东西的时候,
全部都是0
*/
除了上面说到的两个不同点,其他的东西和malloc一样,就不再赘述了
char *p=(char *)malloc(100);//申请一个100字节的空间
char *p=(char *)calloc(3,100);//申请三个100字节的空间
realloc
这个函数就是再分配,意思就是,让一块已经分配好的空间,扩大or缩小。
#include <stdlib.h>
realloc用来把ptr(malloc/calloc/realoc返回的地址,或者是NULL)指向的空间,
扩大到size大小
void *realloc(void *ptr, size_t size);
ptr :
ptr == NULL,首次分配
realloc(NULL,size) <==> malloc(size)
ptr != NULL,再次分配空间
1.size > 原来的大小
"扩建"
realloc用来把 ptr 指向的内存,扩大到size字节大小
原来前面的内存的内容保持不变,后面新增的内存的内容不会初始化,
他只负责分配空间.
(1)原址扩建
(2)整体搬迁
2.size == 0
realloc(ptr ,0) <==> free(ptr)
3.size < 原来的大小
这种情况,作者都没有考虑到这种行为, 行为是未定义(什么结果都有可能发生)
返回值 :
成功 返回扩建后的新首地址(也有可能不动,原地扩建)
void* realloc (void* ptr, size_t size);
- ptr是要调整的内存地址
- size是调整后的新的大小
1.当用于扩大一个内存块时,这块内存原先的内容将依然保留,新增加的内存添加到原先内存块的后面,且新内存并没有进行初始化。
2.当缩小一块内存的时候,该内存块尾部的部分内存被拿掉,剩下的原先内容保留不变。
3. 当原先的内存块无法改变大小,realloc将分配另一块正确大小的内存,并把原先内存的内容复制到新的内存块上。所以在使用realloc之后,不能再使用指向旧的内存的指针,而是该用realloc所返回的新指针。
free
这个就是释放掉你申请的空间,直接free(接受所开辟空间的东西),这么说有点low哦,反正就是释放空间,怎么释放呢?通过所开辟空间的首地址来搞定
#include <stdlib.h>
free用来释放动态分配的空间
void free(void *ptr);
ptr :要释放的空间的首地址,这个地址一定是malloc/calloc/realloc的返回值
返回值 :
无