目录
前言
一、动态内存分配
二、malloc和free函数
2.1 malloc函数
2.2 free函数
三、calloc和realloc函数
3.1 calloc函数
3.2 realloc函数
四、常见的动态内存的错误
1.对NULL指针的解引用操作
2.对动态开辟空间的越界访问
3.对非动态开辟内存使用free释放
4.使用free释放⼀块动态开辟内存的⼀部分
5.对同⼀块动态内存多次释放
6.动态开辟内存忘记释放(内存泄漏)
五、动态内存经典笔试题分析
5.1 题目1
5.2 题目2
5.3 题目3
5.4 题目4
六、柔性数组
6.1 柔性数组的特点
6.2 柔性数组的使用
6.3 柔性数组的优势
七、总结C/C++中程序内存区域划分
总结
前言
当一个内存空间是程序运行前我们无法确定的,且大小无法估计,这就是动态内存。今天我们来讲一下C语言中动态内存管理,
一、动态内存分配
为什么要动态内存分配呢?让我们来看一下我们已知的开辟内存空间的方法:
int val = 20;//在栈空间上开辟四个字节
char arr[10] = {0};//在栈空间上开辟10个字节的连续空间
二、malloc和free函数
2.1 malloc函数
C语言中malloc函数就是来开辟内存空间的:
void* malloc (size_t size);
#include<stdio.h>
#include<stdlib.h>
int main() {
int* p = (int*)malloc(5 * sizeof(int));//开辟5个整型的空间
if (p == NULL) { //判断返回是否为空
return 1;
}
}
2.2 free函数
void free (void* ptr);
#include<stdio.h>
#include<stdlib.h>
int main() {
int* p = (int*)malloc(5 * sizeof(int));//开辟5个整型的空间
if (p == NULL) { //判断返回是否为空
return 1;
}
int i = 0;
for (i = 0; i < 5; i++) {//赋值
*(p + i) = i;
}
for (i = 0; i < 5; i++) {
printf("%d ", *(p + i));
}
free(p);//释放动态内存空间
p = NULL;
}
free释放空间之后:
最后p=NULL,是为了防止p变成野指针。
三、calloc和realloc函数
3.1 calloc函数
void* calloc (size_t num, size_t size);
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p = (int*)calloc(5, sizeof(int));//开辟5个大小为4的整型字节
if (NULL != p)
{
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", *(p + i));//打印
}
}
free(p);
p = NULL;
return 0;
}
空间的每个字节初始化为0:
3.2 realloc函数
当我们有时感觉我们开辟的空间大了或者小了,我们难道需要重新开辟吗?
这个时候我们就可以使用realloc函数可以做到对动态开辟内存大小的调整,让动态内存管理更加灵活。函数原型如下:
void* realloc (void* ptr, size_t size);
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* ptr = (int*)malloc(100);
if (ptr != NULL)
{
//业务处理
}
else
{
return 1;
}
//扩展容量
//代码1 - 直接将realloc的返回值放到ptr中
ptr = (int*)realloc(ptr, 1000);
//代码2 - 先将realloc函数的返回值放在p中,不为NULL,在放ptr中
int* p = NULL;
p = realloc(ptr, 1000);
if (p != NULL)
{
ptr = p;
}
//业务处理
free(ptr);
return 0;
}
代码1其实有问题,如果扩展失败,ptr就变为空指针了,会导致内存泄漏的问题。
上述讲的四个动态内存有关的函数都存放在内存的堆区:
四、常见的动态内存的错误
在了解动态内存后,我们现在来看一下常见的动态内存的错误。
1.对NULL指针的解引用操作
void test()
{
int *p = (int *)malloc(INT_MAX/4);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}
2.对动态开辟空间的越界访问
void test()
{
int i = 0;
int *p = (int *)malloc(10*sizeof(int));
if(NULL == p)
{
exit(EXIT_FAILURE);
}
for(i=0; i<=10; i++)
{
*(p+i) = i;//当i是10的时候越界访问
}
free(p);
}
3.对非动态开辟内存使用free释放
void test() {
int a = 10;
int *p = &a;
free(p);//error
}
free只能释放堆区上的内存,及malloc/calloc/realloc函数开辟的内存空间。
4.使用free释放⼀块动态开辟内存的⼀部分
void test()
{
int *p = (int *)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置
}
5.对同⼀块动态内存多次释放
void test()
{
int *p = (int *)malloc(100);
free(p);
free(p);//重复释放
}
6.动态开辟内存忘记释放(内存泄漏)
void test()
{
int *p = (int *)malloc(100);
if(NULL != p)
{
*p = 20;
}
}
int main()
{
test();
while(1);
}
调用完test函数后,指针变量*p被销毁了,但是开辟的内存空间还在,没有被释放。忘记释放不再使⽤的动态开辟的空间会造成内存泄漏。
切记:动态开辟的空间⼀定要释放,并且正确释放。
关于free函数
五、动态内存经典笔试题分析
5.1 题目1
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
5.2 题目2
char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
运行Test 函数会有什么样的结果?
GetMemory调用完后p数组空间会被销毁,返回的p就变成了一个野指针,结果不确定。
5.3 题目3
void GetMemory(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
运行Test 函数会有什么样的结果?
GetMemory调用是传址调用,所以是str就为开辟空间的地址,就可以顺利打印了。
5.4 题目4
void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, "hello");
free(str);
if(str != NULL)
{
strcpy(str, "world");
printf(str);
}
}
运行Test 函数会有什么样的结果?
开辟的空间提前被释放了,所以str指向的空间不存在了。
六、柔性数组
typedef struct st_type
{
int i;
int a[0];//柔性数组成员
}type_a;
在有些编译器上报错,还可以改成:
typedef struct st_type
{
int i;
int a[];//柔性数组成员
}type_a;
6.1 柔性数组的特点
typedef struct st_type
{
int i;
int a[0];//柔性数组成员
}type_a;
int main()
{
printf("%d\n", sizeof(type_a));//输出的是4
return 0;
}
输出结果为4,说明在计算大小不包括柔性数组的内存。
6.2 柔性数组的使用
//代码1
#include <stdio.h>
#include <stdlib.h>
typedef struct st_type
{
int i;
int a[0];//柔性数组成员
}type_a;
int main()
{
int i = 0;
type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
//业务处理
p->i = 100;
for(i=0; i<100; i++)
{
p->a[i] = i;
}
free(p);
return 0;
}
6.3 柔性数组的优势
我们来看看下面柔性数组的优点,上面代码我们可以这样实现:
//代码2
#include <stdio.h>
#include <stdlib.h>
typedef struct st_type
{
int i;
int *p_a;
}type_a;
int main()
{
type_a *p = (type_a *)malloc(sizeof(type_a));
p->i = 100;
p->p_a = (int *)malloc(p->i*sizeof(int));
//业务处理
for(i=0; i<100; i++)
{
p->p_a[i] = i;
}
//释放空间
free(p->p_a);
p->p_a = NULL;
free(p);
p=NULL;
}
七、总结C/C++中程序内存区域划分
总结
上述文章讲了C语言中的动态内存管理,希望对你有所帮助。