首页 > 系统相关 >C语言-动态内存管理(malloc、calloc、realloc)

C语言-动态内存管理(malloc、calloc、realloc)

时间:2024-09-28 17:50:43浏览次数:7  
标签:malloc int realloc void free C语言 str NULL

目录

1.内存布局

2.动态内存函数

2.1 malloc

2.1.1 malloc是什么

2.1.2 如何用

​编辑2.2 free

2.2.1 free是什么

2.2.2 如何用

2.3 calloc

2.3.1 calloc是什么

2.4 realloc

2.4.1 realloc是什么

2.4.2 realloc如何使用

2.4.3 realloc可以实现与malloc同样的功能

3.常见的动态内存错误

3.1 对NULL指针的解引用操作

3.2 对动态开辟空间的越界访问

3.3 对非动态开辟内存使用free释放

 3.4 使用free释放一块动态开辟内存的一部分

 3.5 对同一块动态内存多次释放

3.6 动态开辟内存忘记释放(内存泄漏)

4.相关题目


1.内存布局

     内存布局的简单描述就是栈区、堆区、静态区

           1)栈区:是来放置局部变量和函数形参等临时变量的。

           2)堆区:是用来动态内存开辟的,malloc、calloc、free、realloc等函数都是在堆区上进行操作的。

           3)静态区:是来放置全局变量、静态变量的。

     今天所要描述的知识点都是在堆区上进行操作的。

2.动态内存函数

2.1 malloc

2.1.1 malloc是什么

         这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。

           1)如果开辟成功,则返回一个指向开辟好空间的指针

           2)如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。

           3)返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己 来决定。

           4)如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。

2.1.2 如何用

int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		perror("main");
	}
	for (int i = 0; i < 10; i++)
	{
		*(p+i) = i;
	}
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", p[i]);
	}
	free(p);
	p = NULL;
	return 0;
}

2.2 free

2.2.1 free是什么

        free函数用来释放动态开辟的内存。需要注意以下两点:

           1)如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的。

           2)如果参数ptr是NULL指针,则函数什么事都不做

2.2.2 如何用

int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		perror("main");
	}
	free(p);
	p = NULL;
	return 0;
}

需要注意的是:free函数不会把ptr指针主动置为NULL指针,因此我们需要主动的把ptr指针置为NULL指针,不然当动态开辟的内存空间经过free函数释放,还给操作系统之后,再调用ptr将会导致非法访问内存的问题。一般来讲,free都是和其他动态开辟内存空间函数成对出现。

2.3 calloc

2.3.1 calloc是什么

         calloc 函数也用来动态内存分配。它只有两点与malloc不同,其它功能与malloc相似,两点如下:

           1)参数不同,calloc有两个参数,第一个参数为 num,第二个是size表示创建 num 个大小为 size

           2)把开辟的空间的每个字节初始化为0

2.4 realloc

2.4.1 realloc是什么

         realloc函数的出现让动态内存管理更加灵活,有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的使用内存,我们一定会对内存的大小做灵活的调整。那realloc 函数就可以做到对动态开辟内存大小的调整。

1. ptr 是要调整的内存地址

2. size 调整之后新大小

3. 返回值为调整之后的内存起始位置。

4. 如果开辟到新的空间上,这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。

5. realloc在调整内存空间的是存在两种情况:

    1)原有空间之后有足够大的空间

    2)原有空间之后没有足够大的空间

    3)堆区上没有那么大的空间进行开辟

2.4.2 realloc如何使用

int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		perror("main");
		return;
	}
	int* temp = (int*)realloc(p, 20 * sizeof(int));
	if (temp == NULL)
	{
		perror("main");
		return;
	}
	p = temp;
	free(p);
	p = NULL;
	temp = NULL;
	return 0;
}

    1)原有空间之后有足够大的空间

     2)原有空间之后没有足够大的空间

     3)堆区上没有那么大的空间进行开辟

        realloc 有可能找不到合适的空间,来调整大小这时就返回NUL

2.4.3 realloc可以实现与malloc同样的功能

int main()
{
	int* p = (int*)realloc(NULL, 10 * sizeof(int));
	if (p == NULL)
	{
		perror("main");
		return;
	}
	free(p);
	p = NULL;
	return 0;
}

3.常见的动态内存错误

3.1 对NULL指针的解引用操作

void test()
{
	int* p = (int*)malloc(INT_MAX / 4);
	*p = 10;//如果p的值是NULL,就会有问题
	free(p);
}

3.2 对动态开辟空间的越界访问

void test()
{
	int i = 0;
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		exit((EXIT_FAILURE));
	}
	for (i = 0; i <= 10; i++)
	{
		*(p + i) = i;//当i是10的时候越界访问
	}
	free(p);
}

3.3 对非动态开辟内存使用free释放

void test()
{
	int a = 10;
	int* p = &a;
	free(p);
}

 3.4 使用free释放一块动态开辟内存的一部分

void test()
{
   int *p = (int *)malloc(100);
   p++;
   free(p);//p不再指向动态内存的起始位置
}

 3.5 对同一块动态内存多次释放

void test()
{
   int *p = (int *)malloc(100);
   free(p);
   free(p);//重复释放
}

3.6 动态开辟内存忘记释放(内存泄漏)

void test()
{
	int* p = (int*)malloc(100);
	if (NULL != p)
	{
		*p = 20;
	}
}
int main()
{
	test();
	while(1);
}

内存泄漏是一个很严重的一个问题,由于test函数中的指针变量p生命周期仅限于test函数,当函数调用结束之后,p就销毁,但只要主函数没有结束,我们动态开辟的内存空间就不会释放,但是指向这个空间的地址我们已经丢失,再也没有能找到这个空间的方法,导致了我们的内存泄漏。

因此:不再使用的动态开辟的空间一定要释放,并且正确释放

4.相关题目

void GetMemory(char* p)
{
	p = (char*)malloc(100);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");
	printf(str);
}
int main()
{
	Test();
	return 0;
}

两个问题:

1. str传给函数GetMemory是值传递,因此在函数内部GetMemory所创建的变量p只是str的一份临时拷贝,并不会对str的值有所改变,当函数GetMemory调用之后str仍然是NULL,strcpy会失败。

2. 函数GetMemory内部所创建的动态内存空间,未进行释放,当函数调用完之后,造成了内存泄漏。

char* GetMemory(void)
{
	char p[] = "hello world";
	return p;
}
void Test(void)
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}
int main()
{
	Test();
	return 0;
}

问题是函数GetMemory中所创建的数组p在函数调用完之后便被销毁,虽然数组的首元素地址传给了str,但这个空间已经还给了操作系统,所以str成为了野指针。这一类问题统归为返回栈空间地址的问题。

void GetMemory(char** p, int num)
{
	*p = (char*)malloc(num);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(&str, 100);
	strcpy(str, "hello");
	printf(str);
}
int main()
{
	Test();
	return 0;
}

问题是没有对动态开辟的内存空间进行及时的释放 

void Test(void)
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}
int main()
{
	Test();
	return 0;
}

函数Test中str所指向的动态内存空间被free释放了,因此当strcpy(str, "world")会对内存空间进行操作,会出现错误。

标签:malloc,int,realloc,void,free,C语言,str,NULL
From: https://blog.csdn.net/qq_53706413/article/details/142391881

相关文章

  • C语言指针plus版
            本篇文章,我们将一起探讨指针高级版一、指针数组、数组指针    1.1指针数组    就是存放指针的数组,因此指针数组是一个数组,不是指针    例如:   int*pa[5];   //整型指针的数组   char*pb[2];  //字符型指针......
  • C语言语句
    C语言语句C语言中的的代码是由一条条语句构成,而基本语句分为:•空语句•表达式语句•函数调⽤语句•复合语句•控制语句空语句简而言之,无语句,一个分号为一条语句#include<stdio.h>//主函数intmain(){ ;//空语句 return0;}表达式语句表达式语句......
  • 初始C语言
    C语言程序设计初始C语言初始C语言主要讲解其基础语法、常量变、运算符等等,这是C语言最基本得语法结构,这些是支撑一个程序的必需品,是重中之重。基础语法C语言的令牌主要包括以下几种类型:关键字(Keywords)标识符(Identifiers)常量(Constants)字符串字面量(StringLiterals)......
  • 实验1 c语言输入输出和简单程序编写
    任务1:task1.11#include<stdio.h>2345intmain()6{7printf("o\n");8printf("<H>\n");9printf("II\n");10printf("o\n"......
  • 【C语言用筛选法求质数】
    C语言用筛选法求质数筛选法,另一种思路的求质数方法上面的方法数越大判断次数越多,运算时间越长,效率越差,如果对于给定的一个集合,可以用筛选法,思路是将集合中的非质数(合数)标出来,余下来的就是质数了。给定的字符数组charprime[100]={0};,初始化为0,默认全是质数:-)!prime[0]=......
  • 【C语言标准库函数】标准输入输出函数详解2:字符串输入输出
    目录一、字符串输入函数1.1.gets函数(已废弃)1.1.1.函数简介1.1.2.注意和废弃原因1.2.fgets函数1.2.1.函数简介1.2.2.使用场景1.2.3.注意事项1.2.4.示例二、字符串输出函数2.1.puts函数2.1.1.函数简介2.1.2. 使用场景2.1.3.注意事项2.1.4.示例2.2.......
  • C语言 16 系统库
    前面了解了如何使用#include引入其他文件,接着来了解一下系统提供的一些常用库。字符串计算字符串长度:#include<stdio.h>#include<string.h>intmain(){char*c="HelloWorld!";//使用strlen计算长度,注意返回值类型是size_t(别名而已,本质上就是unsignedlong)......
  • C语言 16 系统库
    前面了解了如何使用#include引入其他文件,接着来了解一下系统提供的一些常用库。字符串计算字符串长度:#include<stdio.h>#include<string.h>intmain(){char*c="HelloWorld!";//使用strlen计算长度,注意返回值类型是size_t(别名而已,本质上就是unsigned......
  • C语言指针系列3——含野指针+assert
    今天我们来继续感受指针的魅力~野指针首先我们来了解一下什么叫野指针~1.定义    野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)指针变量在定义时如果未初始化,其值是随机的,指针变量的值是别的变量的地址,意味着指针指向了一个地址是不确定......
  • C语言VS实用调试技巧
    文章目录一、什么是bug?二、什么是调试?三、Debug和Release四、VS调试快捷键4.1环境准备4.2调试快捷键五、监视和内存观察5.1监视5.2内存六、调试举例七、编程常见错误归类7.1编译型错误7.2链接型错误7.3运行时错误一、什么是bug?......