一.Malloc函数

1.概述

Malloc函数用于在内存的动态存储区(堆区)中分配一块长度为 size 字节的连续区域,用来存放类型说明符指定的类型。函数原型返回 void*指针,使用时必须做相应的强制类型转换 ,分配的内存空间内容不确定,一般使用memset 初始化。

头文件:

2.使用

(1)函数原型

注意:

malloc申请的内存中存放的内容是随机的,不确定的

函数原型:void* malloc(size_t size);

  • size_t是一个无符号整型,通常用来表示大小或长度

(2)返回值

注意:

  • 要对malloc的返回值进行判断,判断内存是否申请成功
  • 单次申请的内存空间是连续的,多次申请的就不一定了

返回值:

  • 分配空间的起始地址(分配成功)
  • NULL(分配失败,通常是因为内存不足)

3.示例代码

通过malloc动态申请的内存区域的指针当作数组来使用,因为其返回的是一块连续的内存区域

# include 
# include 
/*根据输入的整形来确定要申请内存的大小
申请成功后对其进行内容填充*/
int main() {
	int num;
	printf("请输入要申请的大小: ");
	scanf_s("%d", &num);//使用scanf_s是因为Vs Studio认为单纯的scanf不安全
	
	//malloc的实参通俗点将就是要申请内存的大小
	int* p = (int*)malloc(num * sizeof(int));
	if (p == NULL) {
		printf("内存申请失败");
		return 0;
	}

	//因为malloc动态申请的内存可以看作是一块连续的内存空间,所以可以将其视为是一个数组
	for (int i = 0; i < num; i++) {
		p[i] = i;
	}
	for (int i = 0; i < num; i++)
	{
		printf("p[%d]=%d\n", i, p[i]);
	}

	//释放动态申请的内存
	free(p);
	return 0;
}

二.Free函数

1.概述

**概述:**Free是用来释放内存的函数,其释放传入指针所指向malloc,calloc,relloc动态申请的内存

头文件:

2.使用

函数原型:void free(void *ptr);

  • void *prt:指向要释放内存的指针

注意事项:

  • **指针有效性:**必须是由malloc,calloc,relloc动态申请的内存
  • **多次释放:**尝试释放同一块内存多次也是未定义行为。在释放内存后,应立即将指向该内存的指针设为NULL,以防止误用。

3.示例

char *p = (char *)malloc(100);
free(p);

三.calloc函数

1.概述

**功能:**在内存的堆中,申请nmemb块,每块的大小为size个字节的连续区域函数的返回值

头文件:

2.使用

(1)函数原型

注意:calloc函数申请的内存中的内容均为0,区别于malloc

函数原型:void* calloc(size_t nmemb,size_t size);

  • size_t nmemb:无符号整形,用于指定申请内存的块数
  • size_t size:无符号整形,用于指定每块内存的大小

(2)返回值

单次申请的内存是连续的,两次申请的两块内存不一定连续

  • 分配空间的起始地址(分配成功)
  • NULL(分配失败,通常是因为内存不足)

3.示例

# include 
# include 
int main() {
	int num;
	printf("请输入要申请的大小: ");
	scanf_s("%d", &num);//使用scanf_s是因为Vs Studio认为单纯的scanf不安全
	
	//使用calloc申请内存
	int* p = (int*)calloc(num, sizeof(int));
	if (p == NULL) {
		printf("内存申请失败");
		return 0;
	}

	//因为malloc动态申请的内存可以看作是一块连续的内存空间,所以可以将其视为是一个数组
	for (int i = 0; i < num; i++) {
		p[i] = i;
	}
	for (int i = 0; i < num; i++)
	{
		printf("p[%d]=%d\n", i, p[i]);
	}

	//释放动态申请的内存
	free(p);
	return 0;
}

四.realloc函数

1.概述

**功能:**用于调整已经申请内存大小的函数,此操作可以保持内存的连续性

头文件:```

2.使用

(1)函数原型

函数原型:void* realloc(void* ptr, size_t newSize);

  • ptr:指向之前分配的内存块的指针
    • 如果ptrNULL,则会分配一个新的内存块
  • newSize:新的内存块大小,是调整后的内存大小

(2)返回值

newSize小于原内存,会去掉多余的内存

image-20240206091712953

newSize大于原内存

  • 原内存后空间够,在它后面新增内存块

    image-20240206090550511
  • 原内存后空间不足,会在堆区新申请一个内存,将原内存内容复制过去,并将这个新内存的地址返回

    image-20240206091001065

3.示例

char *p;
//使用malloc申请100字节的内存
p=(char *)malloc(100);
//发现100字节有点多余,故使用reallco进行调整
p=(char *)realloc(p,50);//新的内存小于原内存,原内存后的50字节会被释放

五.内存泄露

自述:我感觉拿个小本本记住哪个内存没有释放貌似也不错( •̀ ω •́ )y

1.概念

**概述:**指定的动态内存申请的内存,指向它的首地址即指针丢失了,无法找回,也无法使用和释放的情况

2.示例

(1)内存指针指向其它位置

将本应该指向申请内存的指针指向了其它位置,导致申请内存指针丢失,进而导致内存泄露

int main(){
    //用指针p来记录申请的内存
    char *p = (char *)malloc(100);
    //将申请内存的指针指向了helloworld
    p = "helloworld";
    //从此以后,再也找不到申请的那100个字节了
    return 0;
}

(2)函数内申请不释放

在函数内动态申请了内存,且在函数结束时没有对其进行释放,导致函数结束后指针p被释放了,没有指针指向内存,进而导致内存泄露

void fun(){
    //光申请最后没有释放
    char *p = (char *)malloc(100); 
    //...后继使用省略
}

int main(){
    //每调用以此函数,就有100字节内存泄露
    fun();
    fun();
    return 0;
}

3.解决方案

总之保证一件事,申请内存的首地址即指针不能丢

(1)函数内申请、释放

在函数内申请内存,在函数结束前即刻释放

void fun(){
    char *p = (char *)malloc(100); 
    //...后继使用省略
    free(p);
}

int main(){
    fun();
    fun();
    return 0;
}

(2)返回函数申请内存指针

将函数内申请内存的指针作为函数的返回值进行返回,后继由接收处处理这个指针

char * fun(){
    char *p = (char *)malloc(100); 
    //...后继使用省略
    return p;
}
int main(){
    //接收函数返回的申请内存的指针
    char *q = fun();
    //在接收函数内部进行释放
    free(q);
    return 0;
}
最后修改:2024 年 03 月 11 日
如果觉得我的文章对你有用,请随意赞赏