函数介绍:
- 栈:在函数内部声明的所有变量都将占用栈内存。用于存放局部变量、函数的形参;
- 堆:这是程序中未使用的内存,在程序运行时可用于动态分配内存。用于动态内存分配;
- 静态区:用于存放全局变量、静态变量(例如static int)。
局部变量和局部数组在栈区开辟空间;
全局变量和全局数组在静态区开辟空间;
本部分主要包括malloc、free、calloc和realloc四个函数的应用:
struct S
{
char name[20];
int age;
};
int main()
{
//数组的变量必须是常量,不是所有编译器都支持变长数组
struct S arr[50];//创建一个结构体数组,存放多个人的信息,相当于通讯录中的data[MAX]
//30,浪费了空间;60,不够
//malloc/free/calloc/relloc
//malloc开辟空间
return 0;
}
一、malloc函数
此函数用于向内存申请空间
函数格式如下:
void* malloc (size_t size);
即向内存申请(size_t size) 个字节的内存空间;
下面代码表示向内存申请10个整形的空间:
//向内存申请10个整形的空间
int* p=(int*)malloc(10 * sizeof(int));
malloc函数的应用如下:
1.定义整形指针p用于接收malloc函数开辟的空间的地址;
2.判断malloc函数是否开辟空间成功,如果p为空指针,则开辟空间失败;
3.进行for循环,将i的值依次赋给p+i;
4.进行打印*(p+i)的结果;
注意:需要free函数释放空间,可以理解为使用完的空间要还回去,free函数与malloc函数成对配合使用。
int main()
{
//向内存申请10个整形的空间
int* p=(int*)malloc(10 * sizeof(int));
if (p == NULL)
{
printf("%s\n", strerror(errno));//打印错误原因的一个方式
}
else
{
//正常使用空间
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
}
//当动态申请的空间不再使用的时候
//就应该还给操作系统
free(p);//释放空间
//malloc和free成对使用
return 0;
}
输出结果为0 1 2 3 4 5 6 7 8 9
二、calloc函数
calloc函数也用来动态内存分配,开辟需要的空间,并把每个字节都初始化为0。
函数格式如下:
void* calloc (size_t num, size_t size)
num为要分配的元素数量;size为要分配的类型的大小;
下面代码为向内存申请10个 sizeof(int)= 4 大小的字节。
int* p= (int*)calloc(10, sizeof(int));
calloc函数的应用如下:
1.定义整形指针指向calloc函数开辟的空间地址;
2.判断calloc是否开辟成功,若p为空指针,则错误;
3.下面代码同malloc函数的应用。
int main()
{
//malloc(10*sizeof(int))
int* p= (int*)calloc(10, sizeof(int));
if (p == NULL)
{
printf("%s\n", strerror(errno));
}
else
{
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
}
//释放空间
//free函数是用来释放动态开辟的空间的
free(p);
p = NULL;
return 0;
}
三、realloc函数
用于调整动态内存开辟的空间
函数格式如下:
void* realloc (void* ptr, size_t size);
ptr为要调整的内存地址;size为调整目标大小;
下面代码的作用是调整p的动态内存空间大小为40个字节;
int* p2=realloc(p, 40);
realloc函数的应用如下:
1.定义整形指针指向malloc函数开辟的空间地址;
2.判断malloc是否开辟成功,若p为空指针,则错误;
3.为*(p+i)赋值;
4.通过realloc函数调整p地址指向的内存空间调整为40个字节的大小;
5.判断realloc函数是否修改空间成功,如果p为空指针,则内存空间开辟失败;
6.进行for循环,将i的值依次赋给p+i;
7.进行打印*(p+i)的结果;
int main()
{
int* p = (int*)malloc(20);
if (p == NULL)//开辟空间失败
{
printf("%s\n", strerror(errno));
}
else
{
int i = 0;
for (i = 0; i < 5; i++)
{
*(p + i) = i;
}
//就是在使用malloc开辟的20个字节的空间
//假设这里20个字节不能满足我们的使用
//希望能有40个字节的空间
//这里就可以使用realloc来调整动态开辟的内存
//
int* p2=realloc(p, 40);
//
//realloc使用注意事项
//1.如果p指向的空间之后有足够的内存空间可以追加,则直接追加,后返回p
//2.如果p指向的空间之后没有足够的内存空间可以追加,则realloc函数会重新找一块新的内存区域开辟新的一块满足需求的空间,并且把原来内存中的数据copy回来,释放旧的内存空间,最后返回新开辟的空间地址
//3.要用一个新的变量来接受mealloc函数的返回值
//
//int* ptr = realloc(p.INT_MAX);
if(ptr != NULL)
{
p = ptr;
int i = 0;
for (i = 5; i < 10; i++)
{
*(p2 + i) = i;
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p2 + i));
}
}
}
//释放内存
free(ptr)
return 0;
}
另外,realloc指针参数如果为空指针NULL,则相当于malloc(40)。
int* p3 = realloc(NULL, 40);//相当于malloc(40)
常见错误:
1、对空指针进行解引用
malloc函数可能开辟空间失败。
int* p = (int*)malloc(40);
//万一malloc失败,p被赋值为NULL,for里非法--空指针解引用
*p = 0;//err
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;
}
free(p);
p = NULL;
2、对开辟的动态内存进行越界访问
动态内存开辟的大小为5个整形,而for循环中循环执行了10个整形,越界访问。
int* p = (int*)malloc(5 * sizeof(int));
if (p == NULL)
{
return 0;
}
else
{
int i = 0;
for (i = 0; i < 10; i++) //malloc中只有5个整形的空间,此处10,形成越界访问
{
*(p + i)=i;
}
}
free(p);
p = NULL;
3、对非动态开辟内存的free
指针p指向的内存不是动态内存,无需释放。
int a = 10;
int* p = &a;
*p = 20;
free(p);//对非动态开辟内存的free
p = NULL;
4、使用free释放动态内存开辟内存的一部分(p指向的地址变化了);
*p++ = i;
此代码改变了指针p的位置,当释放时,从p之后开始释放,p前的内存没有释放。
int* p = (int*)malloc(40);
if (p == NULL)
{
return 0;
}
int i = 0;
for (i = 0; i < 10; i++)
{
*p++ = i;//释放空间要从p往后,但p变化了,并不是起始位置
}
//回收空间
//使用free释放动态开辟内存的一部分
free(p);
5、对同一块动态开辟的内存空间进行多次释放;
多次free(p);在第一次free之后添加
p = NULL;
下面再出现free()代码,就不会出现重复释放问题。
int* p = (int*)malloc(40);
if (p = NULL)
{
return 0;
}
//使用空间
//释放空间
free(p);
//p = NULL;//此行代码之后后面再释放空指针不会有问题
//....
//重复释放空间
free(p);
//谁申请谁回收
6、对动态开辟内存忘记释放(内存泄漏)
没有free()函数。
while (1)
{
malloc(1);
sleep(1000);
}
return 0;
}