一、动态分配内存
申请堆区的空间,需要手动申请,手动释放
如果直接定义在代码中的变量,是系统默认分配栈区的空间,栈区空间自动申请,生命周期结束自动释放。
需要用到头文件#include
malloc和free一定成对出现
1.1 malloc
函数原型:
void *malloc(size_t size); //有参数有返回值函数
功能:
从堆区申请size个字节,并将这一片空间的首地址返回给主调函数处
所以返回值为void*方便强转
void*表示万能指针(可以被强转成任何类型的指针)
使用:
int *p = (int*)malloc(sizeof(int)); //赋值运算符右侧的(int*)表示将malloc申请的首地址,强转成int类型
//让int*类型的指针p指向malloc申请的(sizeof(int))大小的堆区空间
//int *p = (int*)malloc(4);
1.2 free
功能:释放堆区的内存空间
函数原型:
void free(void *ptr);
返回值:无
参数:要释放的堆区空间的首地址
堆区申请空间一定要使用free释放防止内存泄漏
一般释放完堆区内存后,会将原本指向堆空间的指针指向NULL
1.3 指针的强转
因为不同数据类型的指针的大小相同,所以指针之间的强转是安全的
但是会改变指针的偏移量
int *p;
char *p1=(char*)p;
上面的强转过程不会造成数据的丢失,但是如果p本身已经指向int类型数据,使用p1访问该数据时,可能只访问到低字节的数据
1.4 堆空间的地址作为指针函数的返回值
#include <stdio.h>
#include <stdlib.h>
int *func()
{
int *p=(int*)malloc(4);
*p=49;
return p;
}
int main(int argc, const char *argv[])
{
int *ret=func(); //因为func的返回值为堆空间申请的内存,不手动释放不会被回收
printf("%d\n",*ret);
printf("%p\n",ret);
//释放堆内存
free(ret);
return 0;
}
1.5 悬空指针
指针指向一片地址,但是对这片地址没有使用权,间接访问可能会发生段错误
1.5.1 指向已经堆放的空间
#include <stdio.h>
#include <stdlib.h>
int *func()
{
int *p=(int*)malloc(4);
*p=49;
return p;
}
int main(int argc, const char *argv[])
{
int *ret=func(); //因为func的返回值为堆空间申请的内存,不手动释放不会被回收
printf("%d\n",*ret);
printf("%p\n",ret);
//释放堆内存
free(ret);
printf("%p\n",ret); //释放前后,ret的值不变
//一般释放完堆区内存后,会将原本指向堆空间的指针指向NULL
ret=NULL;
return 0;
}
1.5.2 指向已经被回收的空间
#include <stdio.h>
int *func()
{
int a=90;
int *p=&a;
return p; //这种方式返回编译不会报警告,因为智能识别到p是局部变量,返回p的值
}
int main(int argc, const char *argv[])
{
int *ret=func(); //ret是一个悬空指针,指向已经被回收的栈空间的地址
printf("%d\